我想知道是否有一种自动确定给定函数的大O时间复杂度(至少是粗略的)的方法?
如果我将O(n)函数与O(n log n)函数绘制在图表上,我认为我能够直观地确定哪一个是哪一个;我正在思考是否有某种启发式解决方案可以自动完成这个过程。
有什么想法吗?
编辑:我很高兴找到一种半自动化的解决方案,只是想知道是否有一种避免完全手动分析的方法。
我想知道是否有一种自动确定给定函数的大O时间复杂度(至少是粗略的)的方法?
如果我将O(n)函数与O(n log n)函数绘制在图表上,我认为我能够直观地确定哪一个是哪一个;我正在思考是否有某种启发式解决方案可以自动完成这个过程。
有什么想法吗?
编辑:我很高兴找到一种半自动化的解决方案,只是想知道是否有一种避免完全手动分析的方法。
听起来您要求的是Halting问题的扩展。即使从理论上讲,我也不认为这种事情是可能的。
仅仅回答“这行代码是否会运行?”在一般情况下会非常困难,甚至是不可能的。
编辑添加: 虽然一般情况下是棘手的,但是在此处可以看到部分解决方案:http://research.microsoft.com/apps/pubs/default.aspx?id=104919
另外,有人声称手动分析是唯一的选择,但我认为这并不是正确的看法。即使人类被加入到系统/机器中,一个棘手的问题仍然是棘手的。进一步反思后,我想一个99%的解决方案是可行的,并且可能会像或比人类表现得更好。
您可以运行算法以处理各种大小的数据集,并使用曲线拟合得出近似值。(通常仅查看所创建的曲线即可,但任何统计软件都具有曲线拟合功能)。
请注意,某些算法在小型数据集上呈现一种形态,在大型数据集上则呈现另一种形态......而“大型”定义还有点模糊。这意味着一个性能曲线良好的算法可能存在太多真实世界的开销,因此(对于小型数据集)其效果不如理论上更好的算法。
至于代码检查技术,目前还不存在。但是,将您的代码进行工具化,使其在各种长度上运行并输出简单的文件(RunSize RunLength就足够了)应该很容易。生成适当的测试数据可能会更加复杂(有些算法在部分有序数据中表现得更好/更差,因此您需要生成代表正常用例的数据)。
由于“大”的定义问题以及性能与数据相关,我发现静态分析通常会误导人。在优化性能和选择两个算法之间时,真实世界中的“实践检验”测试是我唯一信任的最终仲裁者。
O((n^3/k) + n^2)
的函数。这可以简化为O(n^3)
,因为随着n趋近于无穷大,n^3
项将支配该函数,无论常量k
如何。k
非常大,则该函数将几乎以n^2
的速度运行,直到某个交叉点,在该交叉点之后n^3
项将开始支配该函数。因为常量k
对于任何性能分析工具来说都是未知的,所以无法知道要测试目标函数的数据集有多大。如果k
可以任意大,你就不能制定测试数据来确定该函数的大O运行时间。我惊讶地看到有这么多人试图通过秒表来“衡量”复杂性。一些人已经给出了正确的答案,但我认为仍然有必要强调基本观点。
算法复杂度不是一个“编程”问题;它是一个“计算机科学”问题。回答这个问题需要从数学家的角度分析代码,因此计算大O复杂度实际上是数学证明的一种形式。这需要对基本的计算机操作、代数、也许是微积分(极限)和逻辑有非常深入的理解。没有任何数量的“测试”可以替代这个过程。
停机问题适用,因此机器根本无法确定算法的复杂性。
自动化工具的限制适用,因此可能会编写一个程序来帮助,但它只能在物理作业中帮助像计算器一样,在重新组织代码库时帮助像重构浏览器一样。
对于任何认真考虑编写这样的工具的人,我建议进行以下练习。选择一个相当简单的算法,比如你最喜欢的排序算法,作为你的主题算法。获得一个可靠的参考资料(书籍、基于Web的教程),引导您通过计算算法复杂度和最终“大O”的过程。随着您进行主题算法的过程,记录您的步骤和结果。执行这些步骤并记录多种情况下的进展,例如最佳情况、最坏情况和平均情况。完成后,审查您的文档,并问自己编写一个程序(工具)需要什么。它能做到吗?有多少会自动化,有多少仍然是手动的?
祝一切顺利。
huge_two_dimensional_array foo
for i = 0, i < foo[i].length, i++
for j = 0; j < foo[j].length, j++
do_something_with foo[i][j]
示例 b:
huge_two_dimensional_array foo
for j = 0, j < foo[j].length, j++
for i = 0; i < foo[i].length, i++
do_something_with foo[i][j]
同样是大O表示法...但一个使用行顺序,另一个使用列顺序。由于引用局部性和高速缓存一致性,实际运行时间可能完全不同,特别是取决于数组foo的实际大小。如果算法是软件的一部分,并且内置了一些并发性,那么这甚至无法触及算法的实际性能特征。
不要消极地看待大O表示法,它只是一种具有狭窄范围的工具。如果您深入进行算法分析或尝试证明某个算法的优势,那么它非常有用;但如果您正在进行商业软件开发,则需要实际的性能数据来做出明智的决策。
祝好!
这对于简单算法可能有效,但对于O(n^2 lg n)或O(n lg^2 n)呢?
很容易被视觉上欺骗。
如果是一个非常糟糕的算法,甚至在n=10时都可能无法返回结果。
证明这是不可判定的:
假设我们有一个算法HALTS_IN_FN(Program, function),它确定程序是否在O(f(n))中停止,对于所有n和某个函数f。
让P是以下程序:
if(HALTS_IN_FN(P,f(n)))
{
while(1);
}
halt;
由于函数和程序已经固定,因此在这个输入上 HALTS_IN_FN 具有常数时间复杂度。如果 HALTS_IN_FN 返回 true,则该程序将永远运行,当然不会在任何 O(f(n)) 的时间内停机。如果 HALTS_IN_FN 返回 false,则程序将在 O(1) 时间内停机。
因此,我们得到一个悖论、一个矛盾,因此该程序是不可判定的。
我认为自动完成这个任务几乎是不可能的。请记住,O(g(n))是最坏情况的上界,对于许多数据集来说,许多函数的表现要比O(g(n))更好。你需要找到每个算法的最坏情况数据集才能进行比较。这本身对于许多算法来说就是一个困难的任务。