忽略大O中恒定因子的理由


20

很多时候,如果复杂度具有3n之类的常数,我们会忽略该常数,说O(n)而不是O(3n)。我不明白我们怎么能忽略这三倍的变化?有些事物的变化速度是其他事物的3倍!为什么我们忽略这个事实?


“可以”的语义很重要。在实践中,我们通常不能忽略这样的更改,但是(即描述现实世界中的算法性能)并不是Landau符号的目的。确实存在更精确的形式主义。
拉斐尔

Answers:


22

为了合理化渐近符号忽略常量因子的方式,我通常这样认为:渐进复杂度不是用来比较不同算法的性能,而是用来理解各个算法的性能如何相对于输入大小进行缩放。

例如,我们说一个需要步的函数是,因为从粗略来说,对于足够大的输入,将输入大小加倍不会超过所采取步数的两倍。类似地,意味着将输入大小加倍将最多使步数增加四倍,而意味着将输入大小加倍将最多增加步数一个常数。O n O n 2O log n 3nO(n)O(n2)O(logn)

这是一个工具,用于说明哪种算法可扩展性更好,而不是绝对更快。


11

首先,正如其他答案所解释的那样,,或者换句话说,一个函数是当且仅当它是。意味着存在一个点和一个因数,使得对于所有,。现在选择:对于所有,,所以。相反的证明是相似的。ø 3 Ñ ø Ñ ˚F = Ö 3 Ñ ñ Ç 3 Ñ Ñ ˚F Ñ Ç 33 Ñ c ^ 1 = 3 Ç 3 Ñ ñ ˚F ñ c ^ 1ñ ˚FO(3n)=O(n)O(3n)O(n)f=O(3n)NC3nNf(n)C33nC1=3C3nNf(n)C1nf=O(n)

现在介绍为什么这是正确的工具。观察到,当我们测量算法的复杂性时,我们没有给出单位。我们不计算秒数或机器指令:我们计算一些未指定的基本步骤,每个步骤都需要一定的时间。我们这样做是因为在不同的机器上执行相同的算法会更改每条指令所需的时间-将时钟频率乘以,执行时间从变为。如果我们用不同的语言或不同的系统实现相同的算法,则每个基本步骤所花费的时间可能会有所不同,但这又是太多的细节:我们几乎不会关心这样的差异。f n f n / 33f(n)f(n)/3

当您关心精确的时序时,渐进复杂度就无关紧要了:渐进复杂度告诉您非常大的输入大小会发生什么,这可能是也可能不是您要处理的实际输入大小。


还要注意,Sedgewick在他的“算法分析导论”中主张使用o(g)正确的度量,即作为描述运行时的方式(如果需要,仍可以使用主要的基本操作,但包括困扰OP的常数)。limng(n)T(n)=1
vonbrand

2
@vonbrand Sedgewick真的这么说吗?的通常定义是(即,和限制为零,而不是统一。)LIM Ñ →交通Ť Ñ /Ñ = 0T(n)o(g(n)limn(T(n)/g(n))=0
David Richerby,2015年

3

回想一下Big-O的定义:

f(n)O(g(n))如果存在,则对于所有,。c>0f(n)cg(n)n

在这个定义下,每个常数都有。表示法的目的正是以此方式简化表达式。实际上,增长速度是 3倍,但它们都是线性的。这是否合理-取决于上下文。但是,如果您同意使用表示法,那么按照定义,这成立。dnO(n)dO3nnO


2
这为Big-O提供了很好的解释,但没有解释为什么我们使用此定义。
jmite

正如我所写-目的是简化我们的生活。是因为我们不知道原子操作的确切成本,还是因为我们关心渐近符号。我发现WHY并不是一个有趣的数学问题,而是一个哲学问题。从技术上讲,我们可以没有它。这只会使事情变得非常丑陋且难以使用。
Shaull

3

大O表示法是测量性能变化的无单位均值,因此不受计算原语的相对成本的影响。

简而言之: Big O表示法是一种无单位的相对测量类型(与绝对测量相反)。它只能测量性能变化,而不能测量绝对性能,这对于常量非常重要。优点是,通过允许可以忽略基本操作的相对成本的更简单分析(只要这些成本具有正的固定上限和下限),这使其在很大程度上与实现无关。但是结果是,恒定因素毫无意义。尽管如此,即使就其预期目的而言,渐进复杂性分析也可以基于其他理由提出质疑,并且必须谨慎考虑。例如,原始输入大小可能不是要考虑的正确参数。

首先要说明的是,您的问题并不是很准确。当您忽略的常数时,确实存在“三倍变化”,但是两者的变化速率相同,因此您不能断言“ [某事物的变化快于另一事物的变化3倍”。33n

忽略Landau表示法常量的一个很好的理由是,我们没有可以依靠的单位。当某人说A的生活距离B远两倍于您时,这的确与任何单位无关。即使您以英寸为单位测量距离,而我以光年为单位测量距离,我们也可以达成共识。但是绝对距离测量需要指定单位,其数值公式取决于所选单位。

算法花费的实际时间取决于基本操作的执行时间,而基本操作的执行时间与机器非常相关。您可以算出基本操作的数量,但是没有理由相信它们都花费相同的时间,并且总是有可能将多个操作复合为一个操作,或者反过来将一个操作分解为较小的操作,这样就可以除非您在参考虚拟机上达成共识,否则操作的意义并不真正。独立于参考是一个优势。

对该方法的优点的另一种观点是,只要基本操作的成本有上限和下限为正,分析中您所关心的就是对基本操作的数目进行计数。您不必担心个人成本。

但是,要获得这一优势的代价是,计算成本评估是使用未指定的单位进行的,例如,计算时间可能是纳秒或一千年-我们甚至都没有尝试知道。换句话说,常数因数是没有意义的,因为改变单位与改变常数因数是不可分割的,并且不使用参考单位。

Patrick87所述,这足以了解算法如何相对于输入大小进行缩放,但是,如果不依靠参考单元,就不能给出绝对的性能度量。当一个人实际上希望比较不同算法的性能时,可以取消通用参考抽象机的设置,但是很难确保比较不会因实现细节而产生偏差。在渐进复杂性中,可以避免这种风险,因为您可以将算法与其本身进行比较。

无论如何,只有天真的程序员会完全依赖渐近复杂度来选择算法。还有许多其他标准,包括无法说明的常数和基本操作的实际成本。此外,最坏情况的复杂性可能是一个较差的指标,因为最坏情况的复杂性的来源很少发生,并且在输入片段很小的情况下,其影响有限。例如,用于树邻接语法的通用解析器的理论复杂度为,在实践中非常有用。我知道的最坏情况是 Damas-Hindley-Milner多态类型推断O(n6)用于ML的算法,它具有指数级的最坏情况复杂度。但这似乎并不会打扰ML用户,也不会阻止使用ML编写大型程序。重要的不只是常量。实际上,渐近分析将计算成本的度量与输入复杂度的某种度量相关联。但是原始大小可能不是正确的度量。

复杂性就像可判定性一样,从理论上讲可能很糟糕,但是对于大多数数据空间来说可能无关紧要……有时。与所有工具一样,渐进复杂性分析是一个很好且设计良好的工具,具有其优势和局限性。不论是否明确显示常量,可能都是毫无意义的,因此使用判断是必要的。


2

根据Big-O的定义,其他答案为为什么提供了很好的解释,Øñ=Ø3ñ

至于为什么要在CS中实际执行此操作,是为了使我们对算法的效率有一个简洁的描述。例如,可能有一种算法具有if语句,其中一个分支执行条指令,而另一个分支执行3 n条指令。这意味着即使输入长度相同,确切的数字也会因每个输入而变化。我们可以为每个输入找到一个数字,但是使用big-O表示法可以衡量所有输入的时间复杂度。n3ñ

这在猜测算法将有多快时更有用。否则,我们将不得不考虑一个庞大的分段函数,这将很难理解。

另一个主要原因是这些测量与硬件无关。不同的编译器和体系结构会将相同的代码更改为非常不同的指令集。但是,如果我们知道指令的数量是线性的,指数的等,那么我们就可以知道算法的速度,而不论我们在其上编译或运行的是哪种实际计算机。


1

意味着 LIM SUP Ñ →交通 ˚F Ñ f(n)=O(g(n))lim supnf(n)g(n)<+

如果对于为真,则对于g n = 3 n也为真,反之亦然。g(n)=ng(n)=3n

类似地,。这里的相等意味着f属于LHS ,而f属于RHS。这里的=符号严重滥用了我个人讨厌的符号,因为它令人困惑。O(n2)=O(.00005321n2+1000000000n+1046803)f=


2
=O(...)

fO(g)fO(nn2)f(x)=h(x)xx=n=
yo'13

我通常认为只是明确地表明f是一个参数的函数。f(n)f
Jan Hudec

我通常也这样做,因为也知道这也是一种滥用;)
yo'13

-1

让我简单地解释一下。让我们取n =100000。现在,3n是多少?是300000(是的,是n的3倍)但是n ^ 2是什么?10000000000。(它是n的10万倍)。比较n ^ 2与n。当与10万比较时,3可以忽略不计。因此,我们可以将其删除。

考虑一下n是数十亿还是万亿。在这种情况下,我们再次将3与数十亿或数万亿美元进行比较。现在,您知道我们为什么可以忽略3。


2
三年仍然比一年更长。
Yuval Filmus

我看不出这如何以任何有用的方式回答问题。在现有的已有多年答案的情况下,它当然不会添加任何内容。
拉斐尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.