【问题标题】:Nested Loops in Big-O Notation?Big-O 表示法中的嵌套循环?
【发布时间】:2010-11-22 23:00:25
【问题描述】:

也许我对 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 &gt; num_1 * num_2 时,O(n^2) sn-p 比 O(n) sn-p 少。在现实世界的应用程序中,这两个 sn-ps 可能正在执行两个非常独立的任务,其中 num_1num_2num_3 的功能界限有很大不同。嵌套的 num_1num_2 可能会循环 0 到 255 之间的变量值,但 num_3 可能经常出现超过一百万的值。

在不考虑实用操作变量的情况下,为什么编码人员应该/会相信基于Big-O表示法的算法或sn-p边界?

【问题讨论】:

    标签: theory big-o


    【解决方案1】:

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

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

    通常会说上面sn-p的运行时间在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)。要根据n 说明这是什么,我们需要再次了解num_3n 的关系。

    如果所有num_1num_2num_3都与n成线性比例,那么您确实可以说第一个sn-p运行在O(n^2)时间,第二个运行在O(n)。但是,在这种情况下,num_3 不可能大于 num_1 * num_2 以获得足够大的 n

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        big-Oh 符号是一种将计算复杂度表示为增长率函数的方法。绝对必须理解,它是一个近似值,并且实际上只暴露了所涉及变量的大值(例如 N)。

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

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

        不过,归根结底,一旦选择了候选算法的简短列表(可能基于大哦符号和其他特征,例如空间要求等),那么使用具有代表性的数据集对实现进行计时就是路要走。

        【讨论】:

          【解决方案4】:

          大 O 表示法只说明算法对给定大小的数据工作多长时间,以及当您获得更多数据时它将如何“扩展”,如果 O(n) 算法获得的数据多于 O(n^ 2)算法(如您的示例所示)。但是,如果您向 O(n) 算法提供 2 倍的数据,您应该预期运行时间会延长 2 倍,而使用 O(n^2) 您应该预期会延长 4 倍。

          【讨论】:

            【解决方案5】:

            当 N 接近无穷大时,您可以将这些示例更多地考虑为 Big O。

            所以你在你的场景中 num_3 > num_1 * num_2 是对的,但是随着这三个数字越来越大,这将不再适用。

            如果 algorithm1 是 O(N) 并且 algorithm2 是 O(N^2) 这并不意味着 algorithm1 总是比 algorithm2 好,它只是意味着 N 有一些阈值(通常称为 N0)在那之后点算法1 会比算法2 执行得更好。

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

            Arrays.sortint 数组的 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 表示法是一种分类机制,可帮助您以独立于计算机硬件的方式快速分析和比较算法,并且不需要您编写、测试和计时各种算法。这是一个简化的符号,所以它永远不会是准确的,正如我刚刚给出的示例所示,它非常依赖于数据的大小和范围。

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

            【讨论】:

              【解决方案6】:

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

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

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

              编辑:澄清一下,最坏情况分析通常更可取,因为您希望能够告诉客户“它总是会表现得这么好或更好”,而不是“如果你的数据恰好是完美的,它就会表现得很好! "

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2022-07-18
                • 2010-10-20
                • 2016-03-29
                • 2017-01-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多