Big-O表示法中的嵌套循环?

时间:2010-11-22 23:00:25

标签: theory big-o

也许我对Big-O符号的理解是错误的(自从我开始学习算法以来已经有一段时间了),但以下对我来说从来没有太多意义:

这将被视为O(n ^ 2):

for (int i = 0; i < num_1; i++)
{
    for (int j = 0; j < num_2; j++) 
    {
        cout << i << " " << j << endl;
    }
}

这将被视为O(n):

for (int z = 0; z < num_3; z++) { cout << z << endl; }

我的问题是实际用语。让我们假设num_1 = 10; num_2 = 20; num_3 = 1000;。在这种情况下,第一个例子是O(n ^ 2),它的内部迭代次数比O(n)第二个例子少得多。

用更一般的术语来说:当num_3 > num_1 * num_2时,O(n ^ 2)片段的作用小于O(n)片段。在实际应用程序中,这两个片段可能正在执行两个非常独立的任务,其中num_1num_2num_3上的功能界限有很大差异。嵌套的num_1num_2可能在0到255之间循环变量值,但num_3可能会超过一百万的频繁值。

为什么编码人员在没有考虑实际操作变量边界时,是否应该基于其Big-O表示法信任算法或片段?

6 个答案:

答案 0 :(得分:4)

如果明确知道应该是什么,那么O(n^2)中的某些内容才有意义。通常它指的是输入的大小(或者如果输入是一个数字,它只是指那个数字),但在你的代码中,它不清楚输入是什么。

for (int i = 0; i < num_1; i++)
{
    for (int j = 0; j < num_2; j++) 
    {
        cout << i << " " << j << endl;
    }
}

通常可以说上述代码段的运行时间在O(num_1 * num_2)。如果num_1num_2都是常量,则表示它位于O(1)中。如果num_1num_2都与程序输入(n)的大小成线性比例,那么它确实是O(n^2)。如果num_1num_2都与输入大小的平方成正比,则它位于O(n^4)

底线:它完全取决于num_1num_2是什么以及如何以及取决于他们成长的因素。

for (int z = 0; z < num_3; z++) { cout << z << endl; }

现在此代码位于O(num_3)。根据{{​​1}}说出这是什么意味着再次要求我们知道nnum_3的关系。

如果nnum_1num_2的所有内容与num_3呈线性比例,那么您确实可以说第一个代码段在n时间内运行和O(n^2)中的第二个。但是,在这种情况下,对于足够大的O(n)num_3无法大于num_1 * num_2

答案 1 :(得分:3)

Big O描述算法速度,而不是实际代码。

当你有一个通用算法时,你不知道变量的约束是什么。

答案 2 :(得分:0)

大哦符号是一种将计算复杂性表示为增长率函数的方法。绝对必须要理解的是,它是一个近似值,并且实际上只能忽略所涉及的变量的大值(例如N)。

绝对正确的是,单个变量值,常量等会产生很大影响。

然而,对于类似(和大)大小的变量值,一组算法的big-Oh表达式将指示它们的相对性能。更典型的是,它是一种方便的实现独立方式,用于表示算法的最佳,平均和最差情况复杂性。

但是,在一天结束时,一旦选择了候选算法的简短列表(可能基于大哦符号,以及其他特征,例如空间要求等),那么使用代表性数据集计时实施是要走的路。

答案 3 :(得分:0)

Big O符号表示算法对给定幅度的数据有多长时间工作,以及当获得更多数据时它将如何“缩放”,如果O(n)算法获得的数据多于O(n ^ 2)算法(正如你在你的例子中所示)。但是如果你向O(n)算法提供2倍以上的数据,你应该期望运行时间延长2倍,而O(n ^ 2)你应该期望长4倍。

答案 4 :(得分:0)

你可以将这些例子中的Big O想象为N接近无穷大。

所以你在你的场景中就是num_3&gt; num_1 * num_2,但随着这三个数字越来越大,它将不再成立。

如果algorithm1是O(N)且算法2是O(N ^ 2),它并不意味着algorithm1总是比算法2好,它只是意味着N有一些阈值(通常称为N0),之后point algorithm1的性能优于algorithm2。

一个随机的例子是插入排序是O(N ^ 2),其中MergeSort是O(N * log(N)),但是对于非常小的N值,插入排序实际上最终会更快。一旦N变得足够大,MergeSort总是更快。 Arrays.sort函数的Java版本实际上有一个if语句,它对非常小的N值使用插入排序,对大于特定大小的任何东西使用修改的快速排序或合并排序(幻数是约N = 7)。

Arrays.sort数组的int的Java代码(在Java 6中)如下所示:

private static void sort1(int x[], int off, int len) {
    // Insertion sort on smallest arrays
    if (len < 7) {
           //insertion sort
        }

        //modified quick sort
}

在一天结束时,Big O符号是一种分类机制,可帮助您快速分析&amp;以独立于计算机硬件的方式比较算法,并且不需要您编写,测试和计算各种算法的执行时间。这是一个简化的符号,所以它永远不会是精确的,正如我刚给出的示例所示,它非常依赖于数据的大小和范围。

算法的Big O表示法的一个主要警告是,如果您可以对数据做出假设,通常可以对算法进行改进。

答案 5 :(得分:0)

Big-O为您提供上限或最差情况下的增长率。常量被忽略,因为随着n的增长,它们变得越来越无关紧要(例如,而不是说O(3 + 2n),你只会说O(n))。

Big-Omega是一个最佳案例增长率,根据您对算法使用方式的了解,您可能更适合在某些情况下使用。

如果给定算法的Big-O和Big-Omega相同,则称为精确顺序,您可以将其称为Big-Theta。

编辑:为了澄清,最坏情况分析通常更可取,因为您希望能够告诉客户“它将始终表现良好或更好”,而不是“如果您的数据恰好是完美的,它将表现出色! “