【问题标题】:Calculating the complexity of an algorithm with 3 loops计算具有 3 个循环的算法的复杂度
【发布时间】:2015-05-02 20:51:25
【问题描述】:

我试图解决以下练习:

以下代码片段的最坏情况运行时间增长顺序是多少 作为 N 的函数?

int sum = 0;
for (int i = 1; i <= N; i++)
    for (int j = 1; j <= i*i; j++)
        for (int k = 1; k <= j*j; k++)
            sum++;

我发现复杂度是 O(n4),但是正确答案是:

答案是:N7

对于给定的 i 值,执行最内层循环的主体 12 + 22 + 32 + .. . + (i2)2 ~ 1/3 i6 次。对 i 的所有值求和产生 ~ 1/21 N7

我需要一些帮助来理解这个答案以及在这种情况下计算复杂性的正确方法。

编辑:特别是,我不明白这个说法:

12 + 22 + 32 + ... + (i2)2 ~ 1/3 i6

因为对我来说,2 + 22 + 32 + ... + (i2 )2 ~ i4

【问题讨论】:

  • 这是一篇针对特定问题的非常笼统的文章。
  • 我添加了更多信息来解决您的编辑问题。
  • @otus 好像你有一本很好的学生书,可以分享一下名字吗?
  • @rahpuser 实际上这是来自 coursera 上的“算法,第 1 部分”课程(由 robert sedgewick,普林斯顿),该课程基于 robert sedgewick 的“算法第 4 版”一书

标签: algorithm time-complexity complexity-theory


【解决方案1】:

编辑:

我将添加一些解释,以消除您对问题中引用的困惑。让我们考虑一个固定值i 并关注最里面的两个循环:

for (int j = 1; j <= i*i; j++)
    for (int k = 1; k <= j*j; k++)
        sum++;

j 循环迭代了多少次?答案是 i^2 次。在每次那些迭代中,k-loop 被迭代 j^2 次,这对于每个外部迭代都是不同的,因为 j 的值从 1 一直增加到 i^2。

当 j = 1 时,k 循环迭代 1^2 次。当 j = 2 时,k 循环迭代 2^2 次。当 j=3 时,3^2 次。计算 k 循环对 j 的所有值的迭代总数,你有 1^2 + 2^2 + 3^2 + ... + (i^2)^2,因为 j 在 1 之间运行和 i^2。希望这可以阐明您是如何得出以下陈述的:

对于给定的 i 值,执行最内层循环的主体 12 + 22 + 32 + .. . + (i2)2 ~ 1/3 i6 次。


总的迭代次数可以用sum的形式表示。对于 j 的每个(变化的)值,最里面的循环恰好有 j^2 次迭代,中间的循环对于 i 的每个值都有 i^2 次迭代,最外面的循环有 N 次迭代。更简洁,确切的迭代次数是:

乘以,你会发现这是 N 的 7 阶多项式,所以很明显为什么这是 O(N^7)。

如果您怀疑上面的答案是否正确,只需运行您自己的代码并将您得到的sum 的值与上面提供的公式进行比较:

var sum = 0;
var N = 10;

for (var i = 1; i <= N; i++)
    for (var j = 1; j <= i*i; j++)
        for (var k = 1; k <= j*j; k++)
            sum++;

function T(N) { return (1/420)*N*(1+N)*(1+2*N)*(8+11*N+21*N*N+20*N*N*N+10*N*N*N*N); }

console.log(sum===T(N));

这是一个演示:http://jsfiddle.net/wby9deax/。无论您在答案中输入的 N 值是多少都是正确的(注意:请注意 N 的值较大,它可能会冻结您的浏览器,因为迭代次数增长得非常快)。

【讨论】:

  • 投反对票的人能否解释一下我做错了什么?谢谢。
  • 既然有三个嵌套循环,为什么你的微积分中没有 3 个和?
  • @otus 最里面的循环是从 k=1 到 j^2 of 1 的和,也就是 j^2。
【解决方案2】:
int sum = 0;
for (int i = 1; i <= N; i++)            -- O(N^1)
    for (int j = 1; j <= i*i; j++)      -- O(N^2)
        for (int k = 1; k <= j*j; k++)  -- O(N^4)
            sum++;

因为它们是嵌套的(而且它们都是线性的),所以你得到 O(N1 × N2 × N4 ) = O(N1+2+4) = O(N7)


编辑:特别是,我不明白这个说法:

12 + 22 + 32 + ... + (i2)2 ~ 1/3 i6

请记住,您可能有 N 个术语隐藏在“...”部分中。

【讨论】:

  • 你实际上没有 N^7 次迭代。它是 O(N^7),但肯定不是 N^7。
  • 为什么最后一个循环是 O(n^4) 而不是 O(n^2),因为它与第二个循环具有相同的结构?
  • 啊,好问题。这是因为它在1...j*j(而不是1...i*i)上运行。 jO(n^2) 所以 j*jO(n^4)
  • 虽然这在这里是正确的,但“让我们乘以大 Os 来得到答案”方法存在一个主要缺陷,特别是当循环是对数行为时:for(int i = 1; i &lt; n; i++) for (int j = 0; j &lt; i; j++) ... 这不会是 @987654329 @.
  • 糟糕,打错字了 - 应该是:for(int i = 1; i &lt; n; i*=2) for (int j = 0; j &lt; i; j++) ...。这不是Theta(nlogn),“让我们将大Os 倍增”的方法在这里失败了。但它在这种情况下有效。
【解决方案3】:

因为会 N^1 循环 - 首先为 N^2 循环 - 第二个 N^4 循环 - 第三个 和 N^1 * N^2 * N^4 = N^7

【讨论】:

    【解决方案4】:

    我认为用 N 个值替换变量(i、j 和 k)是个好主意。

    for (int i = 1; i <= N; i++) //<- i = N
        for (int j = 1; j <= i*i; j++) //<- i*i = N*N
            for (int k = 1; k <= j*j; k++) //<- j*j = (i*i)*(i*i) =  N*N*N*N
    

    在第一个循环中,迭代次数为 N,这很简单。 在第二个循环中,迭代次数为N*N。在第三个 - N*N*N*N

    所以最后,迭代次数将是 N(第一个循环),乘以 N*N(第二个),乘以 N*N*N*N(第三个),所以 N*(N*N)*(N*N*N*N) = N^7

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多