C语言 为什么这个嵌套循环有O(n)的时间复杂度?

nnsrf1az  于 2023-02-03  发布在  其他
关注(0)|答案(4)|浏览(159)

我有一个关于复杂性的计算机科学考试,我有这个问题:

int counter = 0;
for (int i = 2; i < n; ++i) {
    for (int j = 1; j < n; j = j * i) {
        counter++;
    }
}

我的解是O(nlogn)因为第一个for是n-2,第二个for是以n为底的对数,它是n-2 * logn,也就是O(nlogn)-
但是我的老师告诉我们它是n,当我尝试在cLion中运行它时,它给我2*n,它是O(n),有人能解释为什么它是O(n)吗?

qgelzfjb

qgelzfjb1#

根据经验,您可以看到,对于n=100n=1,000,这是正确的(大约是系列之和的正确值
如果你想要更多的直觉,你可以考虑这样一个事实,几乎所有的数列,i > sqrt(2)
例如如果n = 100,则90%的值具有i > 10,并且对于n = 1,000,97%的值具有i > 32
从这一点开始,外循环的所有迭代在内循环中最多有2次迭代(因为根据定义,以sqrt(n)为底的log(n)是2)。
如果n变得非常大,您还可以应用相同的逻辑来显示从立方根到平方根,log在2和3之间,等等......

qacovj5a

qacovj5a2#

如果j每次迭代都增加i,而不是乘以i,那么这将是O(nlogn)。现在,j循环的增长比n的增长慢得多,这就是为什么你的老师和CLion说时间复杂度是O(n)。

c90pui9n

c90pui9n3#

注意,它是j=j*i,而不是j=j*2。这意味着大多数情况下,内部循环只有一次通过。例如,n为33,当i在[7,33)中时,内部循环只有一次通过。

n = 33

j = 32
j = 16
j =  8 27
j =  4  9 16 25
j =  2  3  4  5  6
j =  1  1  1  1  1  1  1  1  1  1      1   1
--------------------------------------------
i =  2  3  4  5  6  7  8  9 10 11 ... 28  29

如果你把上面的图看作一个图形,那么算法的复杂度似乎是O(1/log(n)下的面积),我不知道如何证明,而且计算这个积分涉及到我不熟悉的logarithmic integral function,但是维基百科页面确实说这个函数是O(n/log n)。
让我们做个实验。
x一个一个一个一个x一个一个二个x
所以它是双倍加一点,但是额外的一点会随着n的增长而缩小,所以它肯定不是O(n log n),而是O(n/f(n))的形式,其中f()产生某个≥ 1的数,看起来可能是O(n/log n),但这纯粹是推测。
无论f(n)是什么,当n趋近无穷大时,O(n/f(n))趋近于O(n),所以我们也称之为O(n)。

dnph8jn4

dnph8jn44#

对于i的某个值,j将变为
因此,内部循环需要执行的次数如下所示

log_i(n)

这将导致以下结果:

log_2(n) + log_3(n) + log_4(n) + ....
    • 但是**...需要考虑停止条件j < n

现在考虑n作为可以写成m^2的数,一旦i达到值m,所有剩余的内部循环迭代将仅在j等于1j等于i时进行(因为i^2将大于n)。换句话说-将只有2次执行内循环。
因此,迭代的总次数为:

2 * (m^2 - m) + number_of_iteration(i=2:m)

再除以n,结果是m^2

(2 * (m^2 - m) + number_of_iteration(i=2:m)) / m^2

给予

2 * (1 -1/m) + number_of_iteration(i=2:m) / m^2

第一部分2 * (1 -1/m)清除到2,因为m到无穷大。
第二部分是(最坏的情况下):

(log_2(n) + log_3(n) + log_4(n) + ... + log_m(n)) / m^2

(log_2(n) + log_3(n) + log_4(n) + ... + log_m(n)) / n

log(x)/x随着x趋向无穷大而趋向零时,上述表达式也将趋向零。
所以完整表达式:

(2 * (m^2 - m) + number_of_iteration(i=2:m)) / m^2

m趋向无穷大时会趋向2。
换句话说:总的迭代次数除以n将趋向于2,因此我们有O(n)。

相关问题