这是另一个应用格言的好地方
如有疑问,请由内而外!
也就是说,让我们首先将最里面的循环替换为“做 X 量的工作”,然后不断重复,直到我们完成为止。
这里,最里面的循环是这个:
for(j=1; j<k; j=j*2){
//some operations = constant time here
}
此循环的运行时间是 Θ(log k),您似乎在原始问题中暗示了这一点。如果不清楚为什么会这样,Θ(log k) 会计算在超过 k 之前你可以将某个数量翻倍的次数。
这给我们留下了这段代码:
for (k=0, i=1; i<=n-1; i=i*2) {
k = k+1;
do Θ(log k) work;
}
现在,让我们看看这里做了多少工作。我们可以看到 i 的值会数出 20, 21, 22, ..., 直到超过 n .这意味着最后一次循环迭代将是当 i = 2lg n.
这意味着,在循环中,我们将 k 取值 1、2、3、4、5、...、lg n。而且由于我们每次循环迭代都做 Θ(log k) 工作,所以这里完成的总工作将是
Θ(log 1 + log 2 + log 3 + ... + log (log n))
根据对数的性质,请注意 log 1 + log 2 + log 3 + ... + log m = log(1 · 2 · 3 · ... · m) = log (m!)。因此,我们有
Θ(log 1 + log 2 + log 3 + ... + log (log n))
= Θ(log ((log n)!)).
我们现在可以使用Stirling's approximation,它表示
log (m!) = Θ(m log m)
重写
Θ(log ((log n)!)).
= Θ(log n log log n),
这是整体运行时间。
您对 O(log2 n) 的原始估计是这个数量的保守界限。它略微高估了,因为您假设外部循环的每个 Θ(log n) 迭代都会做 Θ(log n) 的总工作,但是由于与后来的迭代相比,早期的迭代做的工作很少就这样吧。