经常能遇到类似于这样的情况,给出一个n,寻找[1,n]中每个数x的所有落于[1,n]中的倍数。
举一个比较常见的例子,我们用普通的筛法寻找[1,n]之间的所有素数并标记。代码大概如下:
isPrime = new int[n + 1] for(i = 1; i <= n; i++) isPrime[i] = true isPrime[1] = false for(i = 2; i <= n; i = i + 1) for(j = i + i; j <= n; j = i + i) isPrime[j] = false
很容易发现对于数x,在区间[1,n]中其倍数数目为floor(n/x)。而累加每个数,倍数的总数为$ \sum_{i=1}^n{\lfloor\frac{n}{i}\rfloor} $。很容易发现这个值的既是我们上面程序的时间复杂度,那么这个值大约是多少呢?
很容易发现这个值的一个上界$ n\sum_{i=1}^n{\frac{1}{i}} $,之后我们把视角移动到新的上界上来。注意到下面不等式的成立(想起黎曼积分的定义):
$$ \int_i^{i+1}{\frac{1}{x}dx}\le\frac{1}{i}\le\int_{i-1}^i{\frac{1}{x}dx} $$
进行累加可以得到:
$$ \int_1^{n+1}{\frac{1}{x}dx}\le\sum_{i=1}^n{\frac{1}{i}}\le 1+\int_1^n{\frac{1}{x}dx}\Rightarrow\ln\left(n+1\right)\le\sum_{i=1}^n{\frac{1}{i}}\le 1+\ln n $$
因此上面程序的时间复杂度就可以表示为O(nln(n)),是一个相当不赖的时间复杂度。当然还有许多可以优化的空间,甚至还可以优化为线性的欧拉筛。