算法分析入门——输入规模的概念

6,392 阅读5分钟

算法分析入门--输入规模概念

引子

前几天上课,正在昏昏欲睡时,听到老师说:“两个数加法的时间复杂度不一定为O(1),与参与运算数据的输入规模有关”。听到这我突然惊醒,“加法的输入不都是两个数嘛?怎么输入规模还会变?”,于是我带着疑惑就在课下对输入规模的概念进行了搜集和学习。

输入规模

在算法分析的过程中,输入规模充当着一个很重要的角色。

例如,在表示时间复杂度时, T(n) 中的 n 就是作为这个算法的输入规模。

下图就显示了随着输入规模 n 的不断变化,不同表达式计算的时间花费

虽然输入规模 n 经常出现,同时也是决定算法时间复杂度的一个参数,但仍然有些人在学习算法的过程中不重视输入规模,或者把概念搞混淆。


我们从一个例子开始,来介绍一下输入规模.

求下面两个算法的输入规模

1.要计算 n 个整数的加法

输入 a1, a2, ..., an

2.判断一个大数 N 是不是一个素数

输入数 N

第一道题,我们根据直觉判断出他的输入规模应该是是输入整数的个数 n。

但是第二道题的输入规模应该是多少呢?按照第一道题的直觉,输入一个数,那么输入规模应该是 1 。这样判断正确嘛?

答案是错误的,通过输入个数来判断输入规模的方法并不总是可行,这是输入规模的概念不清楚导致的。

要解决这个问题,我们就要从输入规模的概念出发,输入规模到底是什么?为了搞清楚输入规模的含义,要首先思考:输入规模中的输入是向哪里输入?

这里的输入是将数据输入到计算机内存中,而输入规模指的是数据在内存中所占据空间的大小。即 输入规模不是指输入数据的个数,而是数据在内存中所占空间的大小

在计算机中,数据都是以二进制存储的,所以我们可以用数据所占的比特位来代表数据所占的空间。

按照这个思路,我们再分析刚刚例子中的第一题,通过内存的角度来判断第一题中的输入规模到底是多少。

先对于第一个数 a1,如何判断其在内存中占多少比特位呢?我们可以用真实的数来举例。

5 -> 101 3位

8 -> 1000 4位

11 -> 1011 4位

17 -> 10001 5位

寻找规律,对于一个十进制数 a ,他用二进制表示的位数为,

\lfloor log_2⁡a+1 \rfloor

所以第一题输入的 n 个数 a1, a2, ..., an, 所占的比特位可以表示为,

\lfloor log_2a_1 \rfloor + 1 + \lfloor log_2a_2 \rfloor  +1+...+ \lfloor log_2a_n \rfloor +1 = \sum_i \lfloor log_2a_i \rfloor +n

为了方便计算,假设 n 位数中的最大值 为 am,则上面的公式还可以再化简

\sum_i \lfloor log_2a_i \rfloor +n < n \lfloor log_2a_m \rfloor + n = n(\lfloor log_2a_m \rfloor + 1)

做到这一步,就能感觉到离结果越来越近了。接下来就对这个式子进行模糊操作,对于括号里的式子进行省略,就可以得到最终的结果 n

n(\lfloor log_2a_m \rfloor + 1) \approx n

这就证明了我们最开始的直觉是正确的,感觉上输入个数 n 可以作为输入规模,但实际上是 n 是由于比特位的计算后近似的结果

那么就按照这个思路,继续计算第二道题的输入规模。

由于刚刚我们已经得到了由一个十进制数计算二进制数的位数的公式,第二道题的计算就变得容易很多,可以直接计算得到输入规模为,

\lfloor log_2N \rfloor + 1  \approx \lfloor log_2N \rfloor

这就说明,如果按照输入规模=输入个数来计算,是很明显错误的。我们要从输入规模更深层次的概念来分析。

通过问题一和问题二的对比,我们就可以发现在通常情况下,可以把算法的输入规模看作与输入数据个数相同。但是在一些特殊情况下,输入规模需要根据其概念来进行计算,例如操作大数的算法。

回到最最开始的问题。“两个数加法的时间复杂度不一定为O(1),与参与运算数据的输入规模有关”,现在就可以理解了,当输入两个数很大时,就不能把输入规模看作是输入数据个数了,而是应该用上面讲解的计算公式来计算求解。

总结

1.输入规模不是输入数据的个数,而是数据在计算机内存中所占空间的大小

2.在通常情况下,计算数据存储位数的结果可以约等于输入数据的个数

3.对于输入大数的算法,就不能只考虑输入数据的个数,也要考虑输入数据的长度,即按照输入规模的概念计算

感谢你的阅读,这是我的第一篇博客,如果你发现内容有错误或者有表达不完善的地方,欢迎与我交流。