这个for循环的时间复杂度是多少(与`n`有关)?

Posted

技术标签:

【中文标题】这个for循环的时间复杂度是多少(与`n`有关)?【英文标题】:What is the time complexity of this for loop (be related to `n`)? 【发布时间】:2018-11-06 13:07:19 【问题描述】:

这个for循环的时间复杂度是多少(与n有关)?

for(int i = 1, j; i <= n; i = j + 1)

        j = n / (n / i);

请注意ijn 是整数变量,它们遵循整数运算。特别是循环内的表达式n/(n/i)应该解释如下:

【问题讨论】:

认为它是什么?您是否尝试过使用不同的n 对其进行测量? @user463035818 j 在 for 循环的 init decl 中声明。 n / (n / i) = n * (i / n) = i. @Jabberwocky 是正确的。 j 除了在增量 stmt 中之外不被使用,并且最初在第一次主体迭代时设置。我也双重接受了。 @AlgirdasPreidžius 那些括号是有原因的。当您涉及整数除法时,该表达式不是您认为的那样。 【参考方案1】:

如果我们使用j = i;而不是j = n / (n / i);,时间复杂度是O(n)。 现在是j = n / (n / i);,假设 n = i*k+r,其中 k 和 r 都是整数并且 r = n%i。因此 j = (i*k+r)/((i*k+r)/i) = (i*k+r)/k = i+r/k >= i,这意味着 i 将比使用j = i; 的情况。所以至少时间复杂度小于 O(n),我想这会给你另一个 O(n)。

除了大 O 符号之外,还有另外两个符号(Θ 和 Ω),表示 O(n) 的下限和上限。您可以通过找到这两个界限来获得时间复杂度。还有一个规则,如果我没记错的话,O(k*n) = O(n),系数k不管多大都无所谓。

【讨论】:

@Walter O(n) 表示算法将少于c*n 步骤,因此它与您的O(sqrt(n)) 不矛盾(确实更严格)。【参考方案2】:

作为elaborated by taotsi,每次迭代中i的增量为

inc = 1 + r/k

r=n%ik=n/i。由于r&lt;i,增量为1,只要i&lt;sqrt(n)(因为在整数除法中i*i/n&lt;1 变为0)。此后,增量(通常)为2i&lt;2*sqrt(n) 一样长。这继续类似于几何级数,在sqrt(n) 上给出一个因子 2,即2 sqrt(n) 迭代。

如果我们用整数0 &lt;= b &lt;= 2*a(即a=int(sqrt(n))b=n-a*a)编写n = a*a+b,那么简单实验中的总迭代次数总是

b < a?  2*a-1 : 2*a

因此,复杂度为 O(√n)(前提是在循环内完成了一些有用的工作,例如计算总迭代次数,这样编译器就不能省略整个循环)。

【讨论】:

【参考方案3】:

由于@Walter 已经提供了证明,我已经为时已晚,但这里是您的代码的 Python3 版本以及作为n2*sqrt(n) 函数的函数的迭代次数图.它们看起来大致相同(最多 n = 1e9)。

import matplotlib.pyplot as plt
from numba import jit
import math

@jit
def weird_increment_loop(n):
    i = 1
    j = 0
    iterations = 0
    while i <= n:
        j = n // (n // i)
        i = j + 1
        iterations = iterations + 1

    return iterations

iterations = []
func_2sqrt = []
domain = range(0,1000000001,1000000)
for n in domain:
    iterations.append(weird_increment_loop(n))
    func_2sqrt.append(math.sqrt(n)*2)

plt.plot(domain,iterations)
plt.plot(domain,func_2sqrt)
plt.xlabel("n")
plt.ylabel("iterations(n) and 2*sqrt(n)")
plt.show()

剧情如下:

如果你看不出有什么不同,那是因为几乎没有区别 :D 当然,人们应该永远相信数学 ;)

【讨论】:

【参考方案4】:

严格按照 C++ 的规则,它是O(1)。循环要么在一定数量的没有可观察的工作后终止,要么永远循环(这是未定义的行为)。符合规范的实现可能会假设未遇到未定义的行为,因此我们可以假设它终止。

由于程序的可观察效果不依赖于循环内部发生的事情,因此允许实现将其“假装”成虚无。

【讨论】:

以上是关于这个for循环的时间复杂度是多少(与`n`有关)?的主要内容,如果未能解决你的问题,请参考以下文章

Python中嵌套For循环的时间复杂度

这个程序的空间复杂性是多少?

以下代码的时间复杂度是多少?

该代码的时间复杂度是多少?

6M - 循环多少次?

时间复杂度怎么算?