算法复杂度及渐进符号

一、算法复杂度

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

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

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

我们说,一个实现了某算法的程序:

  1. 如果计算的速度越快,那么这个算法时间复杂度越低。
  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无限大的时候,计算很慢。

所以,算法衡量要衡量的是在不同问题规模 n下,算法的速度。

在这里,因为要循环计算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)

三、渐进符号

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

渐进符号如下:

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 往低阶的评估,不能同阶

四、总结

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

系列文章入口

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

数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号的更多相关文章

  1. 数据结构和算法(Golang实现)(10)基础知识-算法复杂度主方法

    算法复杂度主方法 有时候,我们要评估一个算法的复杂度,但是算法被分散为几个递归的子问题,这样评估起来很难,有一个数学公式可以很快地评估出来. 一.复杂度主方法 主方法,也可以叫主定理.对于那些用分治法 ...

  2. 算法导论 - 基础知识 - 算法基础(插入排序&归并排序)

    在<算法导论>一书中,插入排序作为一个例子是第一个出现在该书中的算法. 插入排序: 对于少量元素的排序,它是一个有效的算法. 插入排序的工作方式像许多人排序一手扑克牌.开始时,我们手中牌为 ...

  3. 数据结构和算法(Golang实现)(8.1)基础知识-前言

    基础知识 学习数据结构和算法.我们要知道一些基础的知识. 一.什么是算法 算法(英文algorithm)这个词在中文里面博大精深,表示算账的方法,也可以表示运筹帷幄的计谋等.在计算机科技里,它表示什么 ...

  4. 数据结构和算法(Golang实现)(8.2)基础知识-分治法和递归

    分治法和递归 在计算机科学中,分治法是一种很重要的算法. 字面上的解释是分而治之,就是把一个复杂的问题分成两个或更多的相同或相似的子问题. 直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合 ...

  5. 数据结构和算法(Golang实现)(25)排序算法-快速排序

    快速排序 快速排序是一种分治策略的排序算法,是由英国计算机科学家Tony Hoare发明的, 该算法被发布在1961年的Communications of the ACM 国际计算机学会月刊. 注:A ...

  6. 数据结构和算法(Golang实现)(1)简单入门Golang-前言

    数据结构和算法在计算机科学里,有非常重要的地位.此系列文章尝试使用 Golang 编程语言来实现各种数据结构和算法,并且适当进行算法分析. 我们会先简单学习一下Golang,然后进入计算机程序世界的第 ...

  7. 数据结构和算法(Golang实现)(2)简单入门Golang-包、变量和函数

    包.变量和函数 一.举个例子 现在我们来建立一个完整的程序main.go: // Golang程序入口的包名必须为 main package main // import "golang&q ...

  8. 数据结构和算法(Golang实现)(3)简单入门Golang-流程控制语句

    流程控制语句 计算机编程语言中,流程控制语句很重要,可以让机器知道什么时候做什么事,做几次.主要有条件和循环语句. Golang只有一种循环:for,只有一种判断:if,还有一种特殊的switch条件 ...

  9. 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法

    结构体和方法 一.值,指针和引用 我们现在有一段程序: package main import "fmt" func main() { // a,b 是一个值 a := 5 b : ...

随机推荐

  1. MyBatis框架——延迟加载

    延迟加载也叫惰性加载或者懒加载,使⽤延迟是为了提⾼程序的运⾏效率,具体是通过尽量少执⾏ SQL 语句来提升效率.Java 程序与数据库的交互频率越低越好,MyBatis 提供的延迟加载功能就可以做到这 ...

  2. The instance of entity type 'manager' cannot be tracked because another instance with the same key value for {'id'} is already being tracked. When attaching existing entities, ensure that only one ent

    最近在用ASP.NET CORE时遇到一些问题,现记录下: 出现上述错误,即在更新实体数据时出现的错误 services.AddDbContext<StoreContext>(c => ...

  3. 渗透神器cobalt strike在数字杀软环境下的使用

    当我们拿到cobalt strike的beacon权限时,使用它如何渗透目标内网.因为我看网上的文章都是在无杀软下写的,这难免有点脱离实战环境,本文主要测试CS的beacon在数字杀软环境下进行常规渗 ...

  4. Flutter 实现不同样式(有样式) 的TextField (可自定义),类似微博#话题#、@用户,(给TextField加TextSpan)

    描述 先上效果图 在项目中,有 @ 和 话题功能,需要在编辑时即可回显,但是官方原生的TextField不支持对部分文字定义不同的样式,所以封装了一个. 注意:这不是富文本插件,不支持在输入框中显示图 ...

  5. Elasticsearch系列---使用中文分词器

    前言 前面的案例使用standard.english分词器,是英文原生的分词器,对中文分词支持不太好.中文作为全球最优美.最复杂的语言,目前中文分词器较多,ik-analyzer.结巴中文分词.THU ...

  6. OpenCV-Python 轮廓特征 | 二十二

    目标 在本文中,我们将学习 如何找到轮廓的不同特征,例如面积,周长,质心,边界框等. 您将看到大量与轮廓有关的功能. 1. 特征矩 特征矩可以帮助您计算一些特征,例如物体的质心,物体的面积等.请查看特 ...

  7. 基于 Spring Cloud 的微服务架构实践指南(上)

    show me the code and talk to me,做的出来更要说的明白 GitHub 项目learnSpringCloud同步收录 我是布尔bl,你的支持是我分享的动力! 一. 引入 上 ...

  8. iOS 第三方库

    网络 AFNetworking HTTP网络库 Reachability 网络监测 UI.布局 Masonry AutoLayout SnapKit AutoLayout Swift TOWebVie ...

  9. random方法

    random.randint(1,10)     # 产生 1 到 10 的一个整数型随机数 ,包括1和10random.random()        # 产生 0 到 1 之间的随机浮点数rand ...

  10. 一个基于深度学习回环检测模块的简单双目 SLAM 系统

    转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12634631.html 写在前面 最近在搞本科毕设,关于基于深度学 ...