【问题标题】:What is the time complexity of my function? [duplicate]我的函数的时间复杂度是多少? [复制]
【发布时间】:2016-05-21 10:48:42
【问题描述】:

开始研究复杂性,我正在努力解决这个问题:

void what(int n) {
    int i;
    for (i = 1; i <= n; i++) {
        int x = n;
        while (x > 0)
            x -= i;
    }
}

嗯,第一个 for 循环显然是 O(n)。第一次迭代是O(n),第二次是O(n/2).. 我猜是log(n) 次? 这意味着 O(n) * O(log(n)) = O(n * log(n)) complexity。我做对了吗?

编辑:(不是重复的)我知道大 O 是什么。我已经要求在特定情况下进行正确的评估。

【问题讨论】:

  • 恕我直言,根本不是 Big O 的简单英语解释的副本。 OP 知道 Big O 是什么,并且她/他在特定情况下要求正确的评估。
  • 看到没有返回值也没有副作用,能确定编译器不会优化掉吗?
  • 哇..你会期望这样的问题获得这样的分数吗? SO之谜...
  • 注意,这也可能是一个技巧问题。引用维基百科,“算法的时间复杂度量化了算法运行所花费的时间量,作为代表输入的字符串的长度的函数”。这里的输入大小是固定的,并且所有输入的代码都会终止,因此复杂度不能大于 O(1)。事实上,任何自尊的优化编译器都会把整个方法变成空操作。
  • @billc.cn 其实这里n是一个函数的参数,所以不知道怎么调用。

标签: c algorithm time-complexity


【解决方案1】:

外循环运行n 次。

对于每次迭代,内部循环运行n / i 次。

总运行次数为:

n + n/2 + n/3 + ... + n/n

渐近(忽略整数算术舍入),这简化为

n * (1 + 1/2 + 1/3 + ... + 1/n)

这个系列松散地向n * log(n)收敛。

因此复杂度是 O(N.log(N)) 如您所料。

【讨论】:

    【解决方案2】:

    第一个内部循环运行 n
    第二个内部循环运行 n/2
    第三个内循环运行n/3
    .. 最后一个运行一次

    所以n + n/2 + n/3 + ... + 1 = n(1+1/2+1/3+1/4 + ... +1/n)

    这是 n 乘以调和级数之和,它没有很好的封闭形式表示。但如图所示hereO(log(n))。所以总的来说算法是O(n log(n))

    【讨论】:

      【解决方案3】:

      作为替代方法,使用变量替换 y = n - x 后跟 Sigma 表示法来分析算法的内部 while 循环的迭代次数。

      对于每个内部while循环,对于n-1不是i的倍数,即(n-1) % i != 0的情况,上述高估了1的迭代次数。随着我们继续,我们将假设(n-1)/i 是所有i 值的整数,因此高估了内部while 循环中的迭代总数,随后包括下面(i) 处的减号或等号.我们继续进行 Sigma 符号分析:

      我们在(ii),通过相关的积分近似了n:th harmonic number。因此,您的算法在O(n·ln(n)) 中渐近运行。


      离开渐近行为并研究算法的实际增长,我们可以使用@chux精确的(n,cnt)(其中cnt是内部迭代次数)对的nice样本数据(参考他的答案),并比较从上面估计的迭代次数,即n(1+ln(n))-ln(n)。很明显,估计值与实际计数完全一致,请参见下图或this snippet for the actual numbers


      最后请注意,如果我们让n-&gt;∞ 与上面的1/i 相加,那么结果系列就是infinite harmonic series,奇怪的是,这是发散的。后者的证明利用了这样一个事实,即在非零项的无限和中,其本身自然是无限的。然而,在实践中,对于一个足够大但不是无限的 nln(n) 是总和的适当近似值,并且这种散度与我们这里的渐近分析无关。


      【讨论】:

        【解决方案4】:

        通过测试和图形来尝试这一点。 log2 vs log2 图看起来相当线性。大于线性 O(n) 和小于 O(n*log(n)) 之间的东西。 “得出”你自己的结论。

        [编辑]

        使用数学派生公式,计算出的 O() 是 O(n * log(n)) 的上限。这使用“循环迭代的分数”,将计数增加一个分数而不是 1。例如当 n 为 6 时,迭代次数为 6 + 3 + 2 + 1.5 + 1.2 + 1 = 14.7 次循环,而不是实际的 6 + 3 + 2 + 2 + 2 + 1 = 16。这种差异与 @987654327 相比相对不那么显着@ 增加,因此略小于 O(n * log(n)) 增长。因此,通过不忽略使用整数数学的代码,我们有一个更具挑战性的问题。


        unsigned long long what(int n) {
          unsigned long long cnt = 0;
          int i;
          for (i = 1; i <= n; i++) {
            int x = n;
            while (x > 0) {
              x -= i;
              cnt++;
            }
          }
          return cnt;
        }
        
        void wtest(int n) {
          unsigned long long cnt = what(n);
          printf("%d %llu\n", n, cnt);
          fflush(stdout);
        }
        
        void wtests(void) {
          int i = INT_MAX/2 + 1;
          while (i > 0) {
            wtest(i);
            i /= 2;
          }
        }
        
        int main(void) {
          wtests();
          return 0;
        }
        

        输出

        1073741824 23567395117
        536870912 11411566988
        268435456 5519718329
        134217728 2666826555
        67108864 1286897093
        33554432 620190504
        16777216 298466265
        8388608 143418602
        4194304 68802063
        2097152 32947406
        1048576 15746897
        524288 7510048
        262144 3573331
        131072 1695816
        65536 802493
        32768 378537
        16384 177921
        8192 83286
        4096 38803
        2048 17973
        1024 8275
        512 3782
        256 1713
        128 765
        64 337
        32 145
        16 61
        8 24
        4 9
        2 3
        1 1
        

        【讨论】:

        • 进一步分析:O(n * log(n)) 肯定是最坏的情况——它只是不会增长得那么快。显然在 O(n * log(n)/log(log(n))) 和 O(n * log(n)) 之间
        • @dfri 通过分析和实验,what() 的 O() 为O(foo(n) * n * ln(n)),其中foo(n) 为待定。它不是一个常数,而是随着n 的增大而逐渐减小的值。由于它是递减的,O(n * ln(n)) 代表一个上限。
        • @dfri 您的fine mathematical analysis,与其他两个好的答案一样,忽略整数算术舍入。因此O(n * ln(n)) 和实际O()what() 之间的差异。
        • 抱歉延迟返回。无论如何,我根据我在下面计算的公式尝试了上面的 n 值,它们是 harmonize quite neatly。即,n(1 + ln(n)) - ln(n) 很好地捕捉到了实际增长。
        猜你喜欢
        • 1970-01-01
        • 2020-12-03
        • 2017-09-11
        • 2020-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多