Day-5 演算法分析工具 : 渐进式符号(Big-O, Big-Theta, Big-Omega)

前言

比较合并排序法与插入排序法,一旦输入n的规模足够大时,合并排序在最坏情况所需的时间Θhttps://chart.googleapis.com/chart?cht=tx&chl=(nlgn),而插入排序法在最坏情况所需的时间为Θhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E2),当n足够大时,合并排序法的效率远远超过插入排序法。对於足够大的输入,一个演算法所需的时间受到最高次项的影响要远远超过低次项与常数项,因此,在输入够大的情况会将其忽略。

当输入够大时,演算法所需的时间只和增加的量级有关,我们通常只关心演算法的渐进效率(asymptotic efficiency)。所谓渐进效率,即是当输入的数量够多时,演算法所需的时间和输入的数量呈现怎样的关系,是呈现线性关系,还是指数关系等等。

演算法常见的估计方法,渐进式符号(Asymptotic Notation)

用来描述演算法渐进运行时间的渐进式符号是以一个定义域为自然数集合https://chart.googleapis.com/chart?cht=tx&chl=N = { https://chart.googleapis.com/chart?cht=tx&chl=0%2C1%2C2%2C....}的函数来定义。而这样定义函数的定义域有助於我们在处理最坏的情况,当然,我们也可以按照渐进符号的使用情况,让这些符号的定义域在任意集合,如实数域等等集合。

前面我们写到插入排序法的最差情况为Θhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E2),使用Θ这个渐进符号来描述演算法所需要的时间。而Θhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E2)这样的表示法,是由我们推导出来的https://chart.googleapis.com/chart?cht=tx&chl=an%5E2%2Bbn%2Bc所写出来的,其中https://chart.googleapis.com/chart?cht=tx&chl=a%2Cb%2Cc这些皆为常数,通过写成,渐进符号通常应用在函数或是多项式上。

渐进式分析是基於以下几个观点

  1. 忽略掉关於电脑运行速度的因素,那些因素会以常数符号c作为替代
  2. 不关心一个演算法的运行时间,而是关心他的增长量,也就是输入规模n趋近於无限时。

所谓的渐进式符号,为描述演算法趋势的一个函数集合。

Θ

给定一个函数https://chart.googleapis.com/chart?cht=tx&chl=g(n),使用Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))来表示以下函数的集合:

  • Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n)) = {https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3A 存在正实数https://chart.googleapis.com/chart?cht=tx&chl=c_1%2C%20c_2https://chart.googleapis.com/chart?cht=tx&chl=n_0使得对所有https://chart.googleapis.com/chart?cht=tx&chl=n%20%3E%3D%20n_0,有https://chart.googleapis.com/chart?cht=tx&chl=0%20%3C%3D%20c_1g(n)%20%3C%3D%20f(n)%20%3C%3D%20c_2g(n)}
    如果存在正整数https://chart.googleapis.com/chart?cht=tx&chl=c_1%2Cc_2,使得对於足够大的n,https://chart.googleapis.com/chart?cht=tx&chl=f(x)能够被https://chart.googleapis.com/chart?cht=tx&chl=c_1g(n)https://chart.googleapis.com/chart?cht=tx&chl=c_2g(n)之间,我们就可以说https://chart.googleapis.com/chart?cht=tx&chl=f(x)是属於Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))这个函数集合中。

  • 我们具体的证明https://chart.googleapis.com/chart?cht=tx&chl=1%2F2n%5E2-3n%3DΘhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E2)这个式子,令https://chart.googleapis.com/chart?cht=tx&chl=c_1%2Cc_2%2Cn_0属於正整数集合,使得对所有https://chart.googleapis.com/chart?cht=tx&chl=n%3E%3Dn_0,有
    https://chart.googleapis.com/chart?cht=tx&chl=c_1n%5E2%3C%3D1%2F2n%5E2-3n%3C%3Dc_2n%5E2,把https://chart.googleapis.com/chart?cht=tx&chl=n%5E2除掉,得到
    https://chart.googleapis.com/chart?cht=tx&chl=c_1%3C%3D1%2F2-3%2Fn%3C%3Dc_2
    得到选择任意https://chart.googleapis.com/chart?cht=tx&chl=c_2%3E%3D1%2F2,可以使得https://chart.googleapis.com/chart?cht=tx&chl=1%2F2-3%2Fn对於任意https://chart.googleapis.com/chart?cht=tx&chl=n%3E%3D1的值成立,而对於https://chart.googleapis.com/chart?cht=tx&chl=c_1%3C%3D1%2F14,可以使https://chart.googleapis.com/chart?cht=tx&chl=1%2F2-3%2Fn对於任意https://chart.googleapis.com/chart?cht=tx&chl=n%3E%3D7成立。因此,当https://chart.googleapis.com/chart?cht=tx&chl=c_1%3D1%2F14%2C%20c_2%3D1%2F2https://chart.googleapis.com/chart?cht=tx&chl=n_0%3D7,可以证明https://chart.googleapis.com/chart?cht=tx&chl=1%2F2n%5E2-3n%3DΘhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E2)。当然,对於其他一些常数也是如此,重点是存在某一个常数,可以被包含在Θhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E2)这个函数集合中。

简单来说,一个式子忽略掉常数和低阶项,举例:https://chart.googleapis.com/chart?cht=tx&chl=3n%5E3%20%2B%2090n%5E2%20-%205n%20%2B%206789%20%3D%20Θhttps://chart.googleapis.com/chart?cht=tx&chl=(n%5E3)(注意: θ是大写),他所表示的概念是大於等於并且小於等於。

  • 以集合的观点看待,Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n)) = Ohttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))∩Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))

一般情况,会以https://chart.googleapis.com/chart?cht=tx&chl=f(n)%20%3D%20Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))描述这样的观念,这里的'='不是'等於'的意思,而是'属於'的意思

在上图我们可以看到函数https://chart.googleapis.com/chart?cht=tx&chl=c_1g(n)https://chart.googleapis.com/chart?cht=tx&chl=c_2g(n)将函数https://chart.googleapis.com/chart?cht=tx&chl=f(n)夹在中间。看到图形的x轴,我们可以看到当https://chart.googleapis.com/chart?cht=tx&chl=n_0往n的方向逼近,https://chart.googleapis.com/chart?cht=tx&chl=f(n)的值是高於https://chart.googleapis.com/chart?cht=tx&chl=c_1g(n)但低於https://chart.googleapis.com/chart?cht=tx&chl=c_2g(n)的,换句话说,对所有https://chart.googleapis.com/chart?cht=tx&chl=n%3E%3Dn_0,函数https://chart.googleapis.com/chart?cht=tx&chl=f(n)必然在n到https://chart.googleapis.com/chart?cht=tx&chl=n_0的范围中,找到一个k使得https://chart.googleapis.com/chart?cht=tx&chl=f(k)%20%3D%20g(n),这时候我们会称https://chart.googleapis.com/chart?cht=tx&chl=g(n)https://chart.googleapis.com/chart?cht=tx&chl=f(n)的一个渐进临界线(asymptotically tight bound)。


Big-O

如果存在一个正实数c和https://chart.googleapis.com/chart?cht=tx&chl=n_0使得当https://chart.googleapis.com/chart?cht=tx&chl=N%20%3E%3D%20n_0https://chart.googleapis.com/chart?cht=tx&chl=T(N)%20%3C%3D%20cf(N),则表示为https://chart.googleapis.com/chart?cht=tx&chl=T(N)%20%3D%20O(f(N)),通常用於评估一个演算法最久要花费多少时间,也就是一个演算法的上限,小於等於的概念,一个演算法至少会小於等於Big-O函数集合的概念。

  • 另一种Big-O定义方式 : 假设存在两常数https://chart.googleapis.com/chart?cht=tx&chl=c%20%3E%200%2C%20n_0%20%3E%200,使得https://chart.googleapis.com/chart?cht=tx&chl=0%20%3C%3D%20f(n)%20%3C%3D%20cg(n),对於所有https://chart.googleapis.com/chart?cht=tx&chl=n%20%3E%3D%20n_0皆成立,值得注意的一点为Big-O为非对称符号(asymmetric),A = B不代表B = A。
  • 以集合观点来定义Big-O,https://chart.googleapis.com/chart?cht=tx&chl=f(n)属於https://chart.googleapis.com/chart?cht=tx&chl=g(n)的子集,集合中的元素皆为函数,我们可以定义https://chart.googleapis.com/chart?cht=tx&chl=O(g(N))%20%3D%20{ https://chart.googleapis.com/chart?cht=tx&chl=f(n)%20%3A 为一个函数集合,存在https://chart.googleapis.com/chart?cht=tx&chl=c%20%3E%200%2C%20n_0%20%3E%200,使得https://chart.googleapis.com/chart?cht=tx&chl=f(n)以0和https://chart.googleapis.com/chart?cht=tx&chl=cg(n)为界,https://chart.googleapis.com/chart?cht=tx&chl=0%20%3C%3D%20f(n)%20%3C%3D%20cg(n),对所有https://chart.googleapis.com/chart?cht=tx&chl=n%3E%3D%20n_0皆成立 },也就是https://chart.googleapis.com/chart?cht=tx&chl=f(n)的渐进成长率小於https://chart.googleapis.com/chart?cht=tx&chl=g(n)

另外一个集合的观点,我们可以发现https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))这个观念中,其实蕴含https://chart.googleapis.com/chart?cht=tx&chl=f(n)%20%3D%20O(g(n))这个概念。

Example 1 : https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2%20%3D%20O(n%5E3)https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2小於或是等於https://chart.googleapis.com/chart?cht=tx&chl=n%5E3,根据上面的定义,我们不能说https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2等於https://chart.googleapis.com/chart?cht=tx&chl=O(n%5E3),我们只能说https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2是属於https://chart.googleapis.com/chart?cht=tx&chl=O(n%5E3)的子集。因此这里的等号需要理解成属於某个集合的概念,更精确的写法,也可以写https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2%20https://chart.googleapis.com/chart?cht=tx&chl=O(n%5E3)

Example 2 : https://chart.googleapis.com/chart?cht=tx&chl=f(n)%20%3D%20n%5E3%20%2B%20O(n%5E2),表示存在一个函数https://chart.googleapis.com/chart?cht=tx&chl=h(n)https://chart.googleapis.com/chart?cht=tx&chl=%20O(n%5E2)使得https://chart.googleapis.com/chart?cht=tx&chl=f(n)%20%3D%20n%5E3%20%2B%20h(n)

Example 3 : https://chart.googleapis.com/chart?cht=tx&chl=n%5E2%20%2B%20O(n)%20%3D%20O(n%5E3),对所有函数https://chart.googleapis.com/chart?cht=tx&chl=f(n)%20https://chart.googleapis.com/chart?cht=tx&chl=O(n),存在https://chart.googleapis.com/chart?cht=tx&chl=h(n)%20https://chart.googleapis.com/chart?cht=tx&chl=O(n%5E2),使得https://chart.googleapis.com/chart?cht=tx&chl=n%5E2%20%2B%20f(n)%20%3D%20h(n)


Ω

如果存在一个正实数c和https://chart.googleapis.com/chart?cht=tx&chl=n_0使得当https://chart.googleapis.com/chart?cht=tx&chl=N%20%3E%3D%20n_0https://chart.googleapis.com/chart?cht=tx&chl=T(N)%20%3E%3D%20cg(N),则表示为https://chart.googleapis.com/chart?cht=tx&chl=T(N)%20%3D Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(N)),表是一个演算法的下界,也就是最好的情况,花最少时间的时候,大於等於的概念,一个演算法至少会大於等於Ω。

    • 以集合的观点进行定义,Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))%20%3D { https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3A 存在常数https://chart.googleapis.com/chart?cht=tx&chl=%20c%20%3E%200%2C%20n_0%20%3E%200使得https://chart.googleapis.com/chart?cht=tx&chl=%200%20%3C%3D%20cg(n)%20%3C%3D%20f(n)%20对所有 https://chart.googleapis.com/chart?cht=tx&chl=n%20%3E%3D%20n_0}

Example 1 : √https://chart.googleapis.com/chart?cht=tx&chl=n = Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(lgn) 概念上为当n足够大时,√https://chart.googleapis.com/chart?cht=tx&chl=n总是大於等於Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(lgn)


Little-o

我们上面介绍到Big-O这个渐进符号,但是我们可以发现到Big-O所提供的渐进情况可能不是精确的,像是https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2%3DO(n%5E2),这个使用Big-O的确是精确的上界,但是https://chart.googleapis.com/chart?cht=tx&chl=2n%3DO(n%5E2),虽然是函数的上界,但却不是精确的。我们这里使用little-o来表示一个非渐进式的精确上界。

  • https://chart.googleapis.com/chart?cht=tx&chl=o(g(n))%20%3D%20{https://chart.googleapis.com/chart?cht=tx&chl=f(n)对於任一正整数https://chart.googleapis.com/chart?cht=tx&chl=c%3E0,存在常数https://chart.googleapis.com/chart?cht=tx&chl=n_0%3E0,使得对所有https://chart.googleapis.com/chart?cht=tx&chl=n%3E%3Dn_0,有https://chart.googleapis.com/chart?cht=tx&chl=0%3C%3Df(n)%3Ccg(n)}
    概念上就是https://chart.googleapis.com/chart?cht=tx&chl=f(n)函数的增长率严格小於https://chart.googleapis.com/chart?cht=tx&chl=g(n)

Example 1 : https://chart.googleapis.com/chart?cht=tx&chl=2n%3Do(n%5E2),但是https://chart.googleapis.com/chart?cht=tx&chl=2n%5E2!%3Do(n%5E2)

比较Big-O和little-o之间的差别,Big-O为https://chart.googleapis.com/chart?cht=tx&chl=0%20%3C%3D%20f(n)%20%3C%3D%20cg(n),而little-o为https://chart.googleapis.com/chart?cht=tx&chl=0%3C%3Df(n)%3Ccg(n),两者主要差别在https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3Do(g(n)),也就是函数的增长率是小於还是严格小於的差别,因此little-o是比Big-O还要来得强烈的叙述,little-o是比较小的函数集合。

  • ω(读做little-omega),概念上是大於的概念,也就是https://chart.googleapis.com/chart?cht=tx&chl=f(n)增长率严格大於https://chart.googleapis.com/chart?cht=tx&chl=g(n)


来源

函数集合之间的比较关系

https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))%5C%20and%5C%20g(n)%3DΘhttps://chart.googleapis.com/chart?cht=tx&chl=(h(n))蕴含https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3DΘhttps://chart.googleapis.com/chart?cht=tx&chl=(h(n))
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20O(g(n))%5C%20and%5C%20g(n)%3DO(h(n))蕴含https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3DO(h(n))
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))%5C%20and%5C%20g(n)%3DΩhttps://chart.googleapis.com/chart?cht=tx&chl=(h(n))蕴含https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3DΩhttps://chart.googleapis.com/chart?cht=tx&chl=(h(n))
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20o(g(n))%5C%20and%5C%20g(n)%3Do(h(n))蕴含https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3Do(h(n))
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))%5C%20and%5C%20g(n)%3Dωhttps://chart.googleapis.com/chart?cht=tx&chl=(h(n))蕴含https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3Dωhttps://chart.googleapis.com/chart?cht=tx&chl=(h(n))

https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20Θhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))类似於https://chart.googleapis.com/chart?cht=tx&chl=a%3Db
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20O(g(n))类似於https://chart.googleapis.com/chart?cht=tx&chl=a%3C%3Db
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20Ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))类似於https://chart.googleapis.com/chart?cht=tx&chl=a%3E%3Db
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20o(g(n))类似於https://chart.googleapis.com/chart?cht=tx&chl=a%3Cb
https://chart.googleapis.com/chart?cht=tx&chl=f(n)%3D%20ωhttps://chart.googleapis.com/chart?cht=tx&chl=(g(n))类似於https://chart.googleapis.com/chart?cht=tx&chl=a%3Eb

小节

以上这一些定义目的在於为两个函数之间提供一个可以相对比较的标的或是准则,给定两个函数,通常函数上会存在一些点,在这一些点上函数值小於另一个函数的值或是大於函数得值,因此,像https://chart.googleapis.com/chart?cht=tx&chl=f(x)%20%3C%20g(x)这样的表示事实上是不具有任何意义的。

於是,我们比较他们的相对递增率(relative rate of growth),当我们将这个概念应用到演算法效率分析时,我们所进行的比较就能够体现出意义了。

而上面这些用来描述一个演算法执行时间的函式,我们称为该演算法的时间复杂度,如果使用Big-O对https://chart.googleapis.com/chart?cht=tx&chl=f(n)进行描述,会称一个演算法的时间复杂度为https://chart.googleapis.com/chart?cht=tx&chl=O(f(n))

参考资料: Introduciton to algorithms 3rd


<<:  前言与安装

>>:  前导文 - 科学不能解决大自然的奥秘(↑订阅)

事件处理,延伸学习 function bind(Day 8)

<button onClick={this.deleteRow.bind(this, 'id'...

ESP32_DAY5 来新建一个专案吧!

程序架构 昨天只有介绍到Arduino的程序架构可以分为两大函式: setup()及loop()。 ...

每个人都该学的30个Python技巧|技巧 6:各种运算子(下)(字幕、衬乐、练习)

昨天认识了Python三种运算子中,分别是算术运算子、比较运算子以及逻辑运算子,你还记得分别是哪些吗...

D9-用 Swift 和公开资讯,打造投资理财的 Apps { 台股申购实作.2 -读取Big5码的csv}

现在开发者写程序,最方便的一点,就是不会的地方,可以问 Google 在 Google 中输入 Sw...

软件开发 五层次的用户体验

软件开发中,产品经理在规划产品方案时,都会注意用户体验的部分,其实关於用户体验的部分 James G...