【问题标题】:How do you show that one algorithm is more efficient than another algorithm?你如何证明一种算法比另一种算法更有效?
【发布时间】:2010-01-08 15:00:09
【问题描述】:

我不是专业程序员,也不研究它。我是一名航空专业的学生,​​为我的毕业论文做了一个数值方法,还编写了一个程序来证明它有效。

我做了几种方法并实现了几种算法,并试图证明为什么不同的情况需要自己的算法来解决任务。

我用数学方法证明了这一点,但有些算法非常具体,以至于我知道它们做了什么并且它们做得对,但是很难找到一个数学函数或其他东西来显示多少次迭代或循环它必须做,直到它完成。

所以,我想知道您是如何进行这种比较的。您是否还提供数学函数,或者您只是对两种算法进行速度测试,如果您以数学方式进行,您是如何做到的?您是在大学学习期间学习的吗?还是如何学习的?

提前谢谢你,安德烈亚斯

【问题讨论】:

    标签: algorithm math comparison performance


    【解决方案1】:

    比较不同算法的标准方法是使用Big O 表示法比较它们的复杂性。在实践中,您当然也会对算法进行基准测试。

    例如,排序算法冒泡排序和堆排序的复杂度分别为 O(n2) 和 O(n log n)。

    最后要说明的是,构建具有代表性的基准非常困难,请参阅 Christer Ericsson 的 this 关于该主题的有趣帖子。

    【讨论】:

    • 同意,只是补充一点,基准应该使用各种数据集来显示它在不同情况下的反应。
    • 不知何故我对这个答案投了反对票,不是故意的,所以不会让我撤消它。奇怪。
    • 您无法对算法进行基准测试。您可以对算法的实现进行基准测试。
    • 您好,但是“大 O”的计算具体是多少?这些算法通常是指数的,这是肯定的,但是当你不能真正为它设置函数时,你怎么说,一个比另一个进展得更快?
    • @Gumbo 算法的一个定义是可以由图灵完备系统模拟的一系列操作。这意味着例如所有 C 程序都满足算法的定义,在这种情况下,实现就是算法。
    【解决方案2】:

    虽然 big-O 表示法可以为您提供一种区分 糟糕 算法和 合理算法 的方法,但它只告诉您计算复杂度的特定定义。在现实世界中,这实际上不允许您在两种算法之间进行选择,因为:

    1) 两种复杂度相同的算法,我们称它们为fg,两者的O(N^2) 复杂度在运行时可能相差几个数量级。 Big-O 表示法不测量与每次迭代相关的单个步骤的数量,因此f 可能需要 100 步,而g 需要 10 步。

    此外,不同的编译器或编程语言可能会为算法的每次迭代生成或多或少的指令,算法描述中的细微选择可以使缓存或 CPU 硬件的性能降低 10 到 1000 倍,而不会改变其中任何一个big-O 顺序,或步数!

    2) O(N) 算法可能优于 O(log(N)) 算法

    Big-O 表示法不测量与每次迭代相关联的单个步数,因此如果 O(N) 需要 100 步,但 O(log(N)) 每次迭代需要 1000 步,那么对于达到一定大小的数据集 @ 987654330@会更好。

    同样的问题也适用于上述编译器。


    解决方案是对 Big-O 表示法进行初始数学分析,然后是基准驱动的性能调整周期,使用时间和硬件性能计数器数据,以及大量经验。

    【讨论】:

    • O(log(N)) 的复杂度低于 O(N)。我想你的意思是 O(N log(N))。
    【解决方案3】:

    首先需要定义更有效的含义,是否意味着更快,使用更少的系统资源(例如内存)等...(这些因素有时是相互排斥的)

    就效率的标准定义而言,人们通常会使用Big-0 Notation,但在学术界以外的“现实世界”中,通常会分析/基准测试两个方程,然后比较结果

    通常很难对 Big-0 表示法做出一般假设,因为这主要与循环有关,并且假设循环内代码的成本是固定的,因此基准测试是更好的方法

    需要注意的一个警告是,有时结果可能会根据您正在使用的数据集大小而有很大差异 - 对于循环中的小 N,有时不会发现太大差异

    【讨论】:

    • +1 表示通常需要在性能和内存等其他资源之间进行权衡。
    【解决方案4】:

    当最坏情况或预期情况的渐近 Big-O 复杂度类存在显着差异时,您可能会轻松下车。即使这样,您也需要证明隐藏的常数因子不会使“更好”(从渐近角度)算法对于合理大小的输入变慢。

    如果差异不大,那么鉴于当今计算机的复杂性,使用各种数据集进行基准测试是唯一正确的方法。您甚至无法开始考虑来自分支预测准确性、数据和代码缓存命中率、锁争用等所有复杂的相互作用。

    【讨论】:

      【解决方案5】:

      运行速度测试不会像数学那样为您提供高质量的答案。我认为你的大纲方法是正确的——但在分析你的算法时,你的经验和知识广度可能会让你失望。我推荐 Knuth 和其他人的《具体数学》一书,但是还有很多其他好的(甚至更多不好的)书籍涵盖了分析算法的主题。是的,我在大学学习期间学到了这一点。

      写完所有这些之后,大多数算法的复杂性都是根据最坏情况下的执行时间(所谓的 big-O)来分析的,并且您的数据集可能不会接近最坏情况,在这种情况下,速度会考验您run 可能会说明您的实际性能,而不是算法的理论性能。因此,测试并非没有价值。不过,我想说的是,该值次于数学值,这不应该让您感到头疼。

      【讨论】:

        【解决方案6】:

        这取决于。在大学里,你确实通过根据参数的大小/值计算它执行的操作数来学习比较算法。 (比较analysis of algorithmsbig O notation)。我会要求每个体面的程序员至少了解它的基础知识。

        但在实践中,这仅对小型算法或大型算法的一小部分有用。例如,对于 XML 文档的解析算法,您将很难计算这一点。但是了解基础知识通常可以防止你犯脑死错误 - 例如,请参阅 Joel Spolskys 有趣的博客条目"Back to the Basics"

        如果您有一个更大的系统,您通常会通过有根据的猜测比较算法,进行时间测量,或者使用profiling tool 查找系统中的麻烦点。根据我的经验,这很少有那么重要 - 努力降低系统的复杂性会更有帮助。

        【讨论】:

        • “例如,对于 XML 文档的解析算法,您将很难计算。” 哦,我不知道。可能很难证明,但是任何体面的解析器在普通文档上都是 O(n) 的,而且我很确定对于 XML,所有文档都可以达到 O(n) 的预期时间。当有 DTD 时,唯一真正的复杂性出现了,即使在那里我也看不到任何真正的问题。
        • DTD 等价于上下文无关文法,可识别 O(n^3)。
        【解决方案7】:

        回答您的问题:“您是否还提出了一个数学函数,或者您只是对这两种算法进行了速度测试。”

        两者都是 - 让我们总结一下。

        上面讨论的“Big O”方法是指上面提到的最坏情况下的性能。您提到的“速度测试”将是一种估计“平均案例性能”的方法。在实践中,最坏情况性能和平均情况性能之间可能存在很大差异。这就是为什么您的问题有趣且有用的原因。

        最坏情况下的性能一直是定义和分类算法性能的经典方法。最近,研究更关注平均案例性能或更精确的性能界限,例如:99% 的问题将需要少于 N 次操作。你可以想象为什么第二种情况对于大多数问题来说更实用。

        根据应用程序,您可能有非常不同的要求。一个应用程序可能要求响应时间在 95% 的情况下小于 3 秒 - 这将导致定义性能界限。另一个可能要求性能从不超过 5 秒 - 这将导致分析最坏情况下的性能。

        在这两种情况下,这都是在大学或研究生院级别教授的。任何开发用于实时应用程序的新算法的人都应该了解平均性能和最差情况性能之间的差异,并且还应该准备开发算法性能的模拟和分析,作为实现过程的一部分。

        希望这会有所帮助。

        【讨论】:

          【解决方案8】:

          Big O 表示法在最坏的情况下为您提供算法的复杂性,并且主要用于了解当必须处理的数据量增加时算法将如何随着执行时间的增长而增长。例如(C风格的语法,这个不重要):

          List<int> ls = new List<int>();           (1) O(1)
          for (int i = 0; i < ls.count; i++)        (2) O(1)                                     
             foo(i);                                (3) O(log n) (efficient function)
          
          Cost analysis:
              (1)  cost: O(1), constant cost
              (2)  cost: O(1), (int i = 0;)
                         O(1), (i < ls.count)
                         O(1), (i++)
                         ----  total: O(1) (constant cost), but it repeats n times (ls.count)
              (3)  cost: O(log n) (assume it, just an example), 
                                  but it repeats n times as it is inside the loop
          

          因此,在渐近符号中,它的成本为:O(n log n)(效率不高)在本例中是一个合理的结果,但以本例为例:

          List<int> ls = new List<int>();           (1) O(1)
          for (int i = 0; i < ls.count; i++)        (2) O(1)                                     
            if ( (i mod 2) == 0) )                  (*) O(1)  (constant cost)
              foo(i);                               (3) O(log n)
          

          相同的算法,但有一个带有条件的新行。在这种情况下,渐近符号将选择最坏的情况,并得出与上述 O(n log n) 相同的结果,此时很容易检测到 (3) 步骤将只执行一半。

          数据只是示例,可能并不准确,只是试图说明大 O 表示法的行为。它主要为您提供数据增长时算法的行为(您的算法将是线性的,指数的,对数的,...),但这并不是每个人都知道的“效率”,或者几乎,这不是唯一的“效率”的意思。

          但是,这种方法可以检测到“不可能的过程”(对不起,不知道确切的英文单词)算法,这是在早期步骤中需要大量时间来处理的算法(想想例如,阶乘或非常大的矩阵)。

          如果您想要进行真实世界的效率研究,您可能更喜欢收集一些真实世界的数据,并使用这些数据对您的算法行为进行真实世界的基准测试。这不是数学风格,但在大多数情况下会更精确(但不是在最坏的情况下!;))。

          希望这会有所帮助。

          【讨论】:

            【解决方案9】:

            假设速度(而不是内存)是您最关心的问题,并且假设您想要一种经验(而非理论)方法来比较算法,我建议您准备几个大小相差很大的数据集,例如 3 个数量级。然后针对每个数据集运行每个算法,对它们进行计时,并绘制结果。每个算法的时间与数据集大小曲线的形状可以很好地了解其 big-O 性能。

            现在,如果您的数据集的大小在实践中是众所周知的,那么具有更好 big-O 性能的算法不一定更快。要确定对于给定的数据集大小哪种算法更快,您需要调整每个算法的性能,直到它“尽可能快”,然后查看哪个算法获胜。性能调优需要在指令级别进行分析或单步执行,或者我最喜欢的技术stackshots

            【讨论】:

              【解决方案10】:

              正如其他人正确指出的那样,一种常见的方法是使用大 O 表示法。

              但是,只要您考虑到明确定义和范围(例如冒泡排序)的算法的处理性能,Big O 才是好的。

              当其他硬件资源或其他并行运行的软件开始发挥作用时,称为工程的部分开始发挥作用。硬件有其限制。内存和磁盘是有限的资源。磁盘性能甚至取决于所涉及的机制。

              例如,操作系统调度程序将区分 I/O 绑定和 CPU 绑定资源,以提高给定应用程序的总体性能。在集群的情况下,DBMS 将考虑磁盘读取和写入、内存和 CPU 使用,甚至网络。

              这些东西很难在数学上证明,但通常很容易根据一组使用模式进行基准测试。

              所以我猜答案是开发人员都使用大 O 和基准测试等理论方法来确定算法及其实现的速度。

              【讨论】:

                【解决方案11】:

                这通常用big O notation 表示。基本上,您选择一个支配实际迭代次数的简单函数(例如 n2,其中 n 是元素的数量)。

                【讨论】:

                  猜你喜欢
                  • 2011-10-18
                  • 2011-05-28
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-12-07
                  • 1970-01-01
                  相关资源
                  最近更新 更多