Answers:
有趣的问题是:替代方案是什么?我知道的唯一其他方法是测试/基准测试。我们对算法进行编程,让它们在有限输入集(的代表样本)上运行并比较结果。有两个问题。
就是说,在分析中忽略各种影响和常数是很典型的,但是可以称为懒惰的(相对于实践而言)。它不仅可以用于比较算法思想,还可以用于查明给定(甚至伪代码)实现的性能。社区众所周知,这很粗糙,通常需要仔细观察。例如,对于(非常)小的输入,快速排序的效率不如插入排序。公平地说,更精确的分析通常很难 ²。
另一个形式上的抽象观点的后验理由是,在这个层次上,事情通常更清晰。因此,数十年来的理论研究提出了许多在实践中有用的算法思想和数据结构。从理论上讲,最佳算法并不总是您想在实践中使用的算法-除性能外还需要考虑其他因素;认为斐波那契堆积如山-这个标签甚至可能不是唯一的。对于关心优化算术表达式的典型程序员而言,很难在这个级别上提出新的想法(并不是说它不会发生)。但是,她可以(并且应该)对同化的想法进行优化。
有一些形式上的理论工具可以在一定程度上缩小实践差距。例子是
例如,众所周知,Knuth可以对不同语句的数量进行字面统计(对于给定模型中的给定实现),从而可以对算法进行精确比较。这种方法在抽象层面上是不可能的,并且在更复杂的模型中很难做到(以Java为例)。有关现代示例,请参见[4]。
理论与实践之间始终存在差距。我们目前正在开发一种工具³,其目标是结合两者的优势,对算法成本和运行时间(平均)做出合理的预测,但到目前为止,我们还无法消除一种算法具有更高的场景成本,但运行时(在某些计算机上)比等效运行时小(尽管我们可以检测到并支持查找原因)。
我建议从业人员在运行基准测试之前使用理论来过滤算法的空间:
if ( input size forever bounded? ) {
benchmark available implementations, choose best
schedule new benchmarks for when machine changes
}
else {
benchmark implementations of all asymptotically good algorithms
choose the best
schedule new benchmarks for when machine changes or inputs grow significantly
}
我认为这个问题源于教授一门包括渐近分析的课程。关于为什么在入门班中讲授此材料的问题,有几种可能的答案:
渐近分析是一种数学抽象,可用于分析。作为(可以说)数学家,我们希望能够分析算法,而他们要解决其复杂性的唯一方法是使用渐近分析。
评估算法的渐近性能确实指出了一些在实践中有用的原理:例如,专注于花费大部分时间的那部分代码,并折衷花费一部分时间的渐近可忽略的代码的任何部分。
渐近分析的某些技术很有用。我在这里主要指的是所谓的“主定理”,它在许多情况下都是对现实的很好描述。
还有一个历史原因:当人们第一次开始分析算法时,他们真诚地认为渐近复杂性反映了实际用法。但是,最终他们被证明是错误的。P是可有效解决的问题,NP是难解决的问题,这两者在实践中都产生了误导。
我个人认为,渐进分析是课程的合理组成部分。更可疑的部分包括形式语言理论和复杂性理论(与图灵机有关的任何事物)。有人认为,尽管这些主题对想成为程序员的人本身没有用,但他们确实向她灌输了一定的思维方式,这对于成为一名优秀的实践者是必不可少的。其他人则认为理论有时会影响实践,而这些罕见的案例足以证明向一般计算机科学受众教授这些相当神秘的主题。我希望他们学习历史或文学,或者他们真正感兴趣的其他任何学科;两者都与他们未来的工作前景息息相关,对他们来说与人类一样重要。
使用运行时间渐近分析有两个严重的原因:
提取不重要的细节。在许多需要非平凡算法的应用程序中,大多数时间都花在需要中等数量到大量操作的问题实例上,并且我们对总体趋势更感兴趣,而不是确切的操作数量。在这些应用程序中,小行为是无趣的。
允许数学上的易处理性。可能找到精确的运算计数表达式的情况是例外。研究渐近性开辟了更多的可能性(例如复杂函数的渐近逼近很方便)。
还有许多其他方面(例如机器独立性,有意义性,可比性...)。
因为渐近是“简单的”(总之,比对有限情况进行精确分析更简单)。
例如,比较一下Knuth的百科全书“计算机编程的艺术”,该书对所有重要算法(以及许多不太重要的算法)进行了详细的分析,并且采用了经验法则分析(通常足以得出渐近估计值)(或只是一个界限),就像大多数“算法”书中所实践的那样。
你当然是对的。如果问题足够重要,则很可能需要进行Knuth风格的分析(或者可能不太详细)。在许多情况下,暗示拟合到实验数据的渐进复杂度(也许是色散的平均值)就足够了。在大多数情况下,对竞争算法进行粗略分类,因为在第一轮淘汰赛中,比较渐近症状可能足够精确。如果没有竞争者,那么在细节上获得确切成本的坏消息只是受虐狂。
在这里,通过渐近分析,我假设我们的意思是算法的行为是随着输入的大小达到无穷大。
我们使用渐近分析的原因是, 它在预测实践中算法的行为方面很有用。这些预测使我们能够做出决策,例如,当针对一个问题使用不同的算法时,应该使用哪个算法?(有用并不意味着它总是正确的。)
关于现实世界的任何简化模型,可以提出相同的问题。为什么我们使用现实世界的简化数学模型?
想想物理学。在预测现实世界中,经典的牛顿物理学不如相对论物理学。但这对于建造汽车,摩天大楼,潜艇,飞机,桥梁等来说已经足够好了。在某些情况下,它还不够好,例如,如果我们要制造卫星或向冥王星发送太空探测器或预测运动,大型天体,例如恒星和行星,或超高速物体,例如电子。 知道模型的局限性很重要。
通常,它是逼真的世界的近似值。 在实践中,我们经常看到具有更好渐近分析的算法在实践中效果更好。很少有算法具有更好的渐近行为,因此,如果输入可以足够大,那么我们通常可以将渐进分析作为算法行为的第一个预测。如果我们知道输入将很小,则不是这样。根据我们想要的性能,我们可能需要进行更仔细的分析,例如,如果我们了解有关输入分布的信息,则将给出算法,我们可以进行更仔细的分析以实现我们拥有的目标(例如,快速完成99输入的百分比)。作为第一步,渐近分析是一个很好的起点。在实践中,我们还应该进行性能测试,但要记住,这也有其自身的问题。
具有更好的渐近复杂度。在所有投入中,没有哪个比另一个更好?然后,它变得更加棘手,并取决于我们在乎什么。我们关心大投入还是小投入?如果我们关心大型输入,那么算法具有渐近复杂度但在我们关心的大型输入上表现最差的情况并不常见。如果我们更关心小的输入,那么渐进分析可能没有那么有用。我们应该在我们关心的输入上比较算法的运行时间。在实践中,对于具有复杂需求的复杂任务,渐进分析可能没有用。对于算法教科书涵盖的简单基本问题,它非常有用。
简而言之,渐近复杂度相对容易地计算出用于简单基本任务的算法实际复杂度的近似值(算法教科书中的问题)。随着我们构建更复杂的程序,性能要求也随之变化,变得越来越复杂,渐近分析可能没有用。
最好将渐近分析与其他方法进行比较,以预测算法的性能并进行比较。一种常见的方法是针对随机或基准输入进行性能测试。当计算渐进复杂性困难或不可行时,例如在我们使用启发式算法(例如SAT解决方案)时,这是很常见的。另一种情况是,要求更加复杂,例如,程序的性能取决于外部因素,而我们的目标可能是在某个固定的时间限制(例如,考虑更新显示给用户的界面)上完成99%的操作。输入。
但是请记住,性能分析也存在问题。除非我们实际对将提供给算法的所有输入进行性能测试(通常在计算上不可行)(并且通常无法确定某些输入永远不会给出),否则它不能为性能提供数学上的授权。如果我们针对随机样本或基准进行测试,则隐含地假设了 有关算法性能的一些规律性,即算法将在性能测试以外的其他输入上执行类似的操作。
性能测试的第二个问题是它们取决于测试环境。也就是说,程序的性能不是仅由输入决定的,而是由外部因素(例如,机器类型,操作系统,编码算法的效率,CPU的利用率,内存访问时间等)决定的,其中某些因素可能在不同的运行期间有所不同。在同一台机器上进行测试。再次在这里,我们假设执行性能测试的特定环境与实际环境相似,除非我们在可以运行程序的所有环境上进行性能测试(以及我们如何预测某人可能在哪个计算机上运行排序) 10年内启用算法?)。
现在想象一下,代码中的等待重复次数与调用代码的次数相同。如何从数学上量化/证明快速排序算法的这一明显优势?(即,它的名称真的合理吗,还是仅仅是营销口号?)通过渐进复杂性度量。人们主观地看动画,觉得冒泡是某种较弱的算法,渐近复杂度分析可以定量地证明这一点。但请注意,渐进复杂性分析只是分析算法的工具包中的一种工具,而并非总是最终工具。
并且值得一看的并排代码。bubbleort在概念上似乎更简单,并且不使用递归。quicksort并不是立即理解的,尤其是“ 3的中位数”枢轴原理。bubbleort可能仅在没有子例程的循环中实现,而quicksort通常可能至少具有一个子例程。这显示了一种模式,即更多的代码复杂性有时可以牺牲代码的简单性来改善渐近复杂性。有时存在类似于降低边际收益(源自经济学的原始概念)的极端情况,在这种情况下,非常大量的代码复杂性(要求整篇论文有足够的经验和证据来证明)只需要对渐进复杂性进行很小的改进。这以esp为例显示矩阵乘法甚至可以用图形表示。