一段代码的大O复杂性

Beh*_*ani 54 algorithm time-complexity

我在算法设计中有一个关于复杂性的问题.在这个问题中给出了一段代码,我应该计算这段代码的复杂性.伪代码是:

for(i=1;i<=n;i++){
    j=i
    do{
        k=j;
        j = j / 2;
    }while(k is even);
}
Run Code Online (Sandbox Code Playgroud)

我为一些数字尝试了这个算法.我得到了不同的结果.例如,如果n = 6,则该算法输出如下所示

i = 1 -> executes 1 time
i = 2 -> executes 2 times
i = 3 -> executes 1 time
i = 4 -> executes 3 times
i = 5 -> executes 1 time
i = 6 -> executes 2 times
Run Code Online (Sandbox Code Playgroud)

它没有常规主题,我该如何计算呢?

int*_*jay 98

其他答案给出的上限实际上太高了.该算法具有O(n)运行时,其上限比O(n*logn)更严格.

证明:让我们计算内循环将执行的总迭代次数.

外循环运行n时间.对于每个内循环,内循环至少运行一次.

  • 对于偶数i,内循环运行至少两次.这种情况发生了n/2.
  • 对于i4可整除,内循环至少运行三次.这种情况发生了n/4.
  • 对于i8可整除,内循环至少运行四次.这种情况发生了n/8.
  • ...

所以内循环运行的总次数是:

n + n/2 + n/4 + n/8 + n/16 + ... <= 2n
Run Code Online (Sandbox Code Playgroud)

内循环迭代的总量在n和之间2n,即它是Θ(n).

  • +1惊讶不要将此视为已接受的答案.虽然它在技术上是真的,它是O(n log n),但它并不是真正的渐近...而且,如果你想编辑它,这里是一个Θ (15认同)
  • "其他答案给出的上限实际上太高了." 是假的.更好的表述是"......实际上比他们需要的要高". (4认同)

Dav*_*aim 6

你总是假设你得到了每个级别中最糟糕的情况.
现在,你迭代一个包含N个元素的数组,所以我们O(N)已经开始了.
现在让我们说你i的总是等于X并且X总是均匀(记住,每次最坏的情况).
多少次,你需要划分X2获得1?(当偶数达到1时,这是偶数停止除法的唯一条件).
换句话说,我们需要解决的方程 X/2^k = 1X=2^kk=log<2>(X) 这使得我们的算法采取O(n log<2>(X))的步骤,它可以伊斯利可以写成O(nlog(n))

  • 通过这种逻辑,你也可以说复杂度是O(2 ^ n)并且是正确的.我认为推理不符合问题的精神. (7认同)
  • 我最初做了相同的分析,但我认为它不完整.当外循环执行"n"次并且内循环具有O(log(n))的最坏情况复杂度时,并不表示总体复杂度为O(n log(n)); 它取决于内部循环的最坏情况复杂性发生的频率,因为`i`从1变化到'n`.我的猜测是这个频率不是O(n),总体复杂度实际上更低(可能是O(log(n)^ 2)). (5认同)
  • 但这是一个不公平的要求.该阵列具体为[1,2,...,n].我不认为O(n log(n))必然如下.举一个极端的例子,假设内部循环具有O(log(i))的最坏情况行为,对于所有其他值,只有一个"i"和O(1)值.那么外循环执行"n"次的事实不会导致总体复杂度为O(n log(n)); 它将是O(n)+ O(log(n))= O(n).当然,实际的内环没有这种极端的行为,但是对于最坏情况行为的密度来说,保证得出O(n log(n))的结论还远远不清楚(对我而言). (4认同)
  • @RobOcel:不,这不是最糟糕的与平均水平相关的问题.最坏的情况仍然是O(n)*(技术上意味着它也是O(n log n))*.如果n是奇数,它仍然是O(n) - 你要枚举从1到n的所有值,而不仅仅是n本身. (4认同)
  • 如果你想要律师,每个算法都是O(无穷大)和Omega(1),但这实际上并不意味着什么.在大多数情况下,Theta界限更有趣. (4认同)
  • 这里最糟糕的情况是O(n),如我的回答所示.(虽然谈论最坏情况与平均情况有点无意义,因为算法除了"n"本身之外没有输入). (3认同)