数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号

算法复杂度及渐进符号

1、算法复杂度

首先每一个程序运行过程当中,都要占用必定的计算机资源,好比内存,磁盘等,这些是空间,计算过程当中须要判断,循环执行某些逻辑,周而反复,这些是时间。算法

那么一个算法有多好,多快,怎么衡量一个算法的好坏?因此,计算机科学在算法分析过程当中,提出了算法复杂度理论,这套理论能够量化算法的效率,以此做为标准,方便咱们能衡量到底选择哪种算法。segmentfault

复杂度有两个维度:时间和空间。数组

咱们说,一个实现了某算法的程序:数据结构

  1. 若是计算的速度越快,那么这个算法时间复杂度越低。
  2. 若是占用的计算资源越少,那么空间复杂度越低。

咱们要选择复杂度低的算法,衡量好空间和时间的消耗,选出适合特定场景的算法。并发

这两个复杂度维度的量化过程都是同样的,因此咱们这里主要介绍时间复杂度。数据结构和算法

2、算法规模

咱们要计算公式1 + 2 + 3 + ... + 100,那么按照最直观的算法来写:函数

package main

import "fmt"

func sum(n int) int {
    total := 0
    // 从1加到N, 1+2+3+4+5+..+N
    for i := 1; i <= n; i++ {
        total = total + i
    }
    return total
}

func main() {
    fmt.Println(sum(100))
}

n = 10时就等于咱们要计算的公式。这个算法要循环n-1次,当n很小时,计算很快,但当n无限大的时候,计算很慢。spa

因此,算法衡量要衡量的是在不一样问题规模 n下,算法的速度。code

在这里,由于要循环计算n-1次,而当n无限大时,常数项基本忽略不计,因此这个算法的时间复杂度,咱们用O(n)来表示。协程

咱们有另一种计算方式:

func sum2(n int) int {
    total := ((1 + n) * n) / 2
    return total
}

此次算法只需执行1次,因此这个算法的时间复杂度是O(1)。能够看出,时间复杂度为O(1)的算法优于复杂度为O(n)的算法。

固然,还有指数级别的好比以前的汉诺塔算法,对数级别的,阶乘级别的复杂度,如O(2^n)O(n!)O(logn)等。

算法的优先级排列以下,通常排在上面的要优于排在下面的:

  1. 常数复杂度:O(1)
  2. 对数复杂度:O(logn)
  3. 一次方复杂度:O(n)
  4. 一次方乘对数复杂度:O(nlogn)
  5. 乘方复杂度:O(n^2)O(n^3)
  6. 指数复杂度:O(2^n)
  7. 阶乘复杂度:O(n!)
  8. 无限大指数复杂度:O(n^n)

3、渐进符号

如何量化一个复杂度,到底有多复杂,计算机科学抽象出了几个复杂度渐进符号。

渐进符号以下:

OοΘΩω

分别读做:Omicron(大欧),omicron(小欧),Theta(西塔),Omega(大欧米伽),omega(小欧米伽)。

3.1. 渐进符号:Θ

假设算法A的运行时间表达式:

T(n)= 5 * n^3 + 4 * n^2

若是问题规模n足够大,那么低次方的项将无足轻重,运行时间主要取决于高次方的第一项:5*n^3

随着n的增大,第一项的5*n^3中的常数5也无足轻重了。

因此算法A的运行时间T(n)约等于n^3。记为:

T(n) = Θ(n^3)

Θ的数学含义:

f(n)g(n)是定义域 n为天然数集合的函数,两个函数同阶,也就是当 n无穷大时, f(n)/g(n)等于某个大于0的常数 c
也能够说,存在正常量 c1c2n0,对于全部 n >= n0,有 0 <= c1 * g(n) <= f(n) <= c2 * g(n)
那么能够记 f(n) = Θ(g(n))g(n)f(n)的渐进紧确界。

3.2. 渐进符号:O

O的数学含义:

f(n)g(n)是定义域 n为天然数集合的函数, f(n)函数的阶不高于 g(n)函数的阶。
也能够说,存在正常量 cn0,对于全部 n >= n0,有 0 <= f(n) <= c * g(n)
那么能够记 f(n) = O(g(n))g(n)f(n)的渐进上界。

3.3. 渐进符号:Ω

Ω的数学含义:

f(n)g(n)是定义域 n为天然数集合的函数, f(n)函数的阶不低于 g(n)函数的阶。
也能够说,存在正常量 cn0,对于全部 n >= n0,有 0 <= cg(n) <= f(n)
那么能够记 f(n) = Ω(g(n))g(n)f(n)的渐进下界。

3.4. 渐进分析

上面的定义很复杂,咱们能够来看图:

n值超过某个值时,f(n)g(n)两条线夹在中间,那么g(n)就是渐进紧确界。

若是g(n)的线在上面,就是渐进上界。

若是g(n)线在下面,就是渐进下界。

咱们通常会评估一个算法的渐进上界O,由于这表示算法的最坏状况,这个上界能够十分不许确,但咱们通常会评估得足够准确,好比:

设 f(n) = 5 * n^3 + 4 * n^2,咱们要求渐进上界。

那么:

f(n) = O(n^3),g(n) = n^3
f(n) = O(n^4),g(n) = n^4

两个g(n)都是上界,由于令c = 5时都存在:0 <= f(n) <= c * g(n))

咱们会取乘方更小的那个,由于这个界更逼近f(n)自己,因此咱们通常说f(n) = O(n^3),算法的复杂度为大欧n的三次方,表示最坏状况。

同理,渐进下界Ω恰好与渐进上界相反,表示最好状况。好比仍是这个假设:

设 f(n) = 5 * n^3 + 4 * n^2,咱们要求渐进下界。

那么:

f(n) = Ω(n^3),g(n) = n^3
f(n) = Ω(n^2),g(n) = n^2

两个g(n)都是下界,由于令c =5时都存在:0 <= cg(n) <= f(n)

咱们准确评估的时候,要取乘方更大的那个,由于这个界更逼近f(n)自己,因此咱们通常说f(n) = Ω(n^3),算法的复杂度为大欧米伽n的三次方,表示最好状况。

咱们发现当f(n) = Ω(n^3) = O(n^3)时,其实f(n) = Θ(n)

另外两个渐进符号οω通常不多使用,指不那么紧密的上下界。

也就是评估的时候,不那么准确去评估,在评估最坏状况的时候使劲地往坏了评估,评估最好状况则使劲往好的评估,可是它不能刚恰好,好比上面的结果:

f(n) = O(n^3),g(n) = n^3
f(n) = O(n^4),g(n) = n^4
f(n) = Ω(n^3),g(n) = n^3
f(n) = Ω(n^2),g(n) = n^2

咱们能够说:

f(n) = ο(n^4),g(n) = n^4  往高阶的评估,不能同阶
f(n) = ω(n^2),g(n) = n^2  往低阶的评估,不能同阶

4、总结

咱们通常用O渐进上界来评估一个算法的时间复杂度,表示逼近的最坏状况。其余渐进符合基本不怎么使用。

系列文章入口

我是陈星星,欢迎阅读我亲自写的 数据结构和算法(Golang实现),文章首发于 阅读更友好的GitBook

相关文章
相关标签/搜索