数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号
算法复杂度及渐进符号
一、算法复杂度
首先每个程序运行过程中,都要占用一定的计算机资源,比如内存,磁盘等,这些是空间,计算过程中需要判断,循环执行某些逻辑,周而反复,这些是时间。
那么一个算法有多好,多快,怎么衡量一个算法的好坏?所以,计算机科学在算法分析过程中,提出了算法复杂度理论,这套理论可以量化算法的效率,以此作为标准,方便我们能衡量到底选择哪一种算法。
复杂度有两个维度:时间和空间。
我们说,一个实现了某算法的程序:
- 如果计算的速度越快,那么这个算法时间复杂度越低。
- 如果占用的计算资源越少,那么空间复杂度越低。
我们要选择复杂度低的算法,衡量好空间和时间的消耗,选出适合特定场景的算法。
这两个复杂度维度的量化过程都是一样的,所以我们这里主要介绍时间复杂度。
二、算法规模
我们要计算公式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)
等。
算法的优先级排列如下,一般排在上面的要优于排在下面的:
- 常数复杂度:
O(1)
- 对数复杂度:
O(logn)
- 一次方复杂度:
O(n)
- 一次方乘对数复杂度:
O(nlogn)
- 乘方复杂度:
O(n^2)
,O(n^3)
- 指数复杂度:
O(2^n)
- 阶乘复杂度:
O(n!)
- 无限大指数复杂度:
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
。
也可以说,存在正常量c1
,c2
和n0
,对于所有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)
函数的阶。
也可以说,存在正常量c
和n0
,对于所有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)
函数的阶。
也可以说,存在正常量c
和n0
,对于所有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实现)(1)简单入门Golang-前言
- 数据结构和算法(Golang实现)(2)简单入门Golang-包、变量和函数
- 数据结构和算法(Golang实现)(3)简单入门Golang-流程控制语句
- 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法
- 数据结构和算法(Golang实现)(5)简单入门Golang-接口
- 数据结构和算法(Golang实现)(6)简单入门Golang-并发、协程和信道
- 数据结构和算法(Golang实现)(7)简单入门Golang-标准库
- 数据结构和算法(Golang实现)(8.1)基础知识-前言
- 数据结构和算法(Golang实现)(8.2)基础知识-分治法和递归
- 数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号
- 数据结构和算法(Golang实现)(10)基础知识-算法复杂度主方法
- 数据结构和算法(Golang实现)(11)常见数据结构-前言
- 数据结构和算法(Golang实现)(12)常见数据结构-链表
- 数据结构和算法(Golang实现)(13)常见数据结构-可变长数组
- 数据结构和算法(Golang实现)(14)常见数据结构-栈和队列
- 数据结构和算法(Golang实现)(15)常见数据结构-列表
- 数据结构和算法(Golang实现)(16)常见数据结构-字典
- 数据结构和算法(Golang实现)(17)常见数据结构-树
- 数据结构和算法(Golang实现)(18)排序算法-前言
- 数据结构和算法(Golang实现)(19)排序算法-冒泡排序
- 数据结构和算法(Golang实现)(20)排序算法-选择排序
- 数据结构和算法(Golang实现)(21)排序算法-插入排序
- 数据结构和算法(Golang实现)(22)排序算法-希尔排序
- 数据结构和算法(Golang实现)(23)排序算法-归并排序
- 数据结构和算法(Golang实现)(24)排序算法-优先队列及堆排序
- 数据结构和算法(Golang实现)(25)排序算法-快速排序
- 数据结构和算法(Golang实现)(26)查找算法-哈希表
- 数据结构和算法(Golang实现)(27)查找算法-二叉查找树
- 数据结构和算法(Golang实现)(28)查找算法-AVL树
- 数据结构和算法(Golang实现)(29)查找算法-2-3树和左倾红黑树
- 数据结构和算法(Golang实现)(30)查找算法-2-3-4树和普通红黑树
数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号的更多相关文章
- 数据结构和算法(Golang实现)(10)基础知识-算法复杂度主方法
算法复杂度主方法 有时候,我们要评估一个算法的复杂度,但是算法被分散为几个递归的子问题,这样评估起来很难,有一个数学公式可以很快地评估出来. 一.复杂度主方法 主方法,也可以叫主定理.对于那些用分治法 ...
- 算法导论 - 基础知识 - 算法基础(插入排序&归并排序)
在<算法导论>一书中,插入排序作为一个例子是第一个出现在该书中的算法. 插入排序: 对于少量元素的排序,它是一个有效的算法. 插入排序的工作方式像许多人排序一手扑克牌.开始时,我们手中牌为 ...
- 数据结构和算法(Golang实现)(8.1)基础知识-前言
基础知识 学习数据结构和算法.我们要知道一些基础的知识. 一.什么是算法 算法(英文algorithm)这个词在中文里面博大精深,表示算账的方法,也可以表示运筹帷幄的计谋等.在计算机科技里,它表示什么 ...
- 数据结构和算法(Golang实现)(8.2)基础知识-分治法和递归
分治法和递归 在计算机科学中,分治法是一种很重要的算法. 字面上的解释是分而治之,就是把一个复杂的问题分成两个或更多的相同或相似的子问题. 直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合 ...
- 数据结构和算法(Golang实现)(25)排序算法-快速排序
快速排序 快速排序是一种分治策略的排序算法,是由英国计算机科学家Tony Hoare发明的, 该算法被发布在1961年的Communications of the ACM 国际计算机学会月刊. 注:A ...
- 数据结构和算法(Golang实现)(1)简单入门Golang-前言
数据结构和算法在计算机科学里,有非常重要的地位.此系列文章尝试使用 Golang 编程语言来实现各种数据结构和算法,并且适当进行算法分析. 我们会先简单学习一下Golang,然后进入计算机程序世界的第 ...
- 数据结构和算法(Golang实现)(2)简单入门Golang-包、变量和函数
包.变量和函数 一.举个例子 现在我们来建立一个完整的程序main.go: // Golang程序入口的包名必须为 main package main // import "golang&q ...
- 数据结构和算法(Golang实现)(3)简单入门Golang-流程控制语句
流程控制语句 计算机编程语言中,流程控制语句很重要,可以让机器知道什么时候做什么事,做几次.主要有条件和循环语句. Golang只有一种循环:for,只有一种判断:if,还有一种特殊的switch条件 ...
- 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法
结构体和方法 一.值,指针和引用 我们现在有一段程序: package main import "fmt" func main() { // a,b 是一个值 a := 5 b : ...
随机推荐
- 机器学习- Attention Model结构解释及其应用
概述 Attention Model 的出现,在sequence model的领域中算是一个跨时代的事件.在Many-to-Many的sequence model中,在decoder network中 ...
- 使用WireShark进行网络流量安全分析
WireShark的过滤规则 伯克利包过滤(BPF)(应用在wireshark的捕获过滤器上) ** 伯克利包过滤中的限定符有下面的三种:** Type:这种限定符表示指代的对象,例如IP地址,子网或 ...
- Python3学习之路~9.3 GIL、线程锁之Lock\Rlock\信号量、Event
一 Python GIL(Global Interpreter Lock) 全局解释器锁 如果一个主机是单核,此时同时启动10个线程,由于CPU执行了上下文的切换,让我们宏观上看上去它们是并行的,但实 ...
- JDK_API关于时间的表达
判断日期是否是闰年 给定格式格式化 第二种表示时间的 Date java.util 类 Date 表示特定的瞬间,精确到毫秒. 构造方法 Date( ...
- 泛微E-cology OA 远程代码执行漏洞
分析文章:https://dwz.cn/bYtnsKwa http://127.0.0.1/weaver/bsh.servlet.BshServlet 若存在如上页面,则用下面数据包进行测试. POS ...
- div 3 frog jump
There is a frog staying to the left of the string s=s1s2…sn consisting of n characters (to be more p ...
- JavaScript----简介及基础语法
##JavaScript *概念:一门客户端脚本语言 *运行在客户端浏览器中的.每一个浏览器都有JavaScript的解析引擎. *脚本语言:不需要编译,直接就可以被浏览器解析执行. *功能: *可以 ...
- Map m = Collections.synchronizedMap(new HashMap())
Collections.synchronizedMap(new HashMap())让你创建的new HashMap()支持多线程数据的同步.保证多线程访问数据的一致性 来源:http://www.b ...
- 超参数、验证集和K-折交叉验证
本文首发自公众号:RAIS 前言 本系列文章为 <Deep Learning> 读书笔记,可以参看原书一起阅读,效果更佳. 超参数 参数:网络模型在训练过程中不断学习自动调节的变量,比如 ...
- Vue2.0 -- 钩子函数/ 过度属性/常用指令/以及Vue-resoure发送请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...