这是使用格言的好地方
如有疑问,请由内而外!
也就是说,从最里面的循环开始,并用一个表示它正在做多少工作的表达式替换它,然后重复这个直到你完成。
在你的情况下,最里面的循环是
for (int j=i; j<n; j=j*2)
# O(1) expression
现在,这做了多少工作?为了回答这个问题,让我们考虑一下循环是如何工作的。在每次迭代中,j 的值都是之前的两倍。由于 j 从 i 开始,j 取的值将是 i、2i、4i、8i 等,更一般地,在循环的第 k 次迭代中,j 的值将是 2k ·一世。一旦这个值等于或超过 n,循环就会终止,这发生在
2k · i = n
2k = n / i
k = lg(n / i)
所以这意味着我们将运行循环进行 Θ(log (n / i)) 次迭代。如果我们将其替换为原始循环,我们将得到以下代码:
for (int i=n; i>1; i=i/2):
Do Θ(log (n / i)) work;
那么最终总共需要多少工作量?好吧,外部循环将运行一次 i = n,然后运行一次 i = n/2,然后运行一次 i = n/4,等等,所以完成的总工作是
Θ(log (n/n) + log(n/(n/2)) + log(n/(n/4)) + ... + log(n / 1))
= Θ(log 1 + log 2 + log 4 + log 8 + ... + log n)
= Θ(log 20 + log 21 + log 22 + log 23 + ... + log 2log n)
Θ(0 + 1 + 2 + ... + log n)
记住 0 + 1 + 2 + ... + k = k(k + 1) / 2 = Θ(k2),所以这里做的总功是
Θ(0 + 1 + 2 + ... + log n)
= Θ(log2 n).
得出这个答案的关键技术是从内部开始,将对数与重复将大小翻倍时执行的循环迭代次数联系起来,以及 1 + 2 + ... + k 的公式。