为什么要了解算法的效率?

一般来说,编程就是把各种已知的算法代入到自己的代码当中,以此来解决问题。因此,了解各种算法的效率对于我们选择一个合适的算法有很大帮助。

算法的效率由什么确定?

从算法分析的理论来讲,算法的效率通常由它们的复杂度来评估,包括时间复杂度和空间复杂度。由于现代计算机RAM空间充足,因此一般优先考虑时间复杂度。

时间复杂度用渐近记号(asymptotic notation)来表示,通常有 O、 Θ和Ω 记号法。渐进的意思就是当问题的规模变大时,解决这个问题所耗费的时间增加了多少。

(注:当规模较小时,无论是高效的算法还是低效的算法,时间耗费差距不明显,很可能产生误导的结果。所以算法分析针对大规模输入。)

如何测量算法的时间复杂度?

一个算法是由控制结构(顺序、分支和循环3种)和基本操作(指固有数据类型的操作)构成的,算法的运行时间与算法中语句的执行次数成正比例,某个算法中语句执行次数多,它运行花费的时间就多。比较同一个问题的不同算法的效率,通常的做法是,选取该算法的基本操作,以其基本操作的重复执行次数作为算法的时间量度,记为时间频度T(n)。n为问题的规模,当n不断变化时,T(n)也会不断变化。一般情况下,算法中重复执行基本操作的次数是问题规模n的某个函数,用T(n)表示。若有某个辅助函数f(n),当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数,记作T(n)=O(f(n))。称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

(注:为什么不直接查看算法的运行时间呢?因为算法的运行时间受很多因素影响,例如计算机处理器的性能,计算机是否同时还在运行其他程序,等等。)

大O记号法(big O notation,O代表omicron,为希腊字母第15个字)的定义:

对于规模为 n的输入,当n增大时,运行这个函数所增加的运行时间的上界。

算法运行的几种情况(以在一个有n个元素的数列中查找某个元素为例):

  • 最好情况(Best Case):第一次就找到了,表示为O(1)
  • 期望情况(Expected Case):在数列中间的某个位置m找到,表示为O(m)
  • 最坏情况(Worst Case):在数列最后才找到,表示为O(n)

在实际使用中,我们一般仅考量算法在最坏情况下的运行情况,也就是对于规模为 n 的输入,算法的最长运行时间。这样做的理由是:

  1. 一个算法在最坏情况下的运行时间是在任何输入下运行时间的一个上界(Upper Bound),不管其他情况如何,运行时间不会更长
  2. 对于某些算法来说,最坏情况出现的次数还是比较频繁的,比如在数据库中检索一条实际并不存在的记录
  3. 很多时候,算法的期望情况和最坏情况一样差。例如插入排序在最坏情况下(数组事先逆序)和平均情况下(假设有一半逆序),复杂度均为O(n2)

算法复杂度的类型:

  • O(1) --- 常量复杂度(constant complexity)
  • O(n)  ---  线性复杂度(linear complexity)    its runtime grows "on the order of the size of the input"
  • O(n2)  ---  二次方复杂度(quadratic complexity)    its runtime grows "on the order of the square of the size of the input"
  • O(log n)  ---  对数复杂度(logarithmic complexity)
  • O(cn)  ---  指数复杂度((exponential complexity)

其他还有O(n3)三次方复杂度,O(n!)阶乘复杂度等等。

用例子来说明不同类型的算法复杂度:

假设我们现在要自己写一段代码来计算ab的结果(b为正整数)。

方法一:

def exp1(a,b):
ans=1
while b>0:
ans*=a
b-=1
return ans

此算法基本步骤为:3b+2步(每个循环里面有3步,一共循环b次,再加上初始ans赋值和返回ans值这两步)。当b足够大时,其他数字都不重要了,因此这个算法是一个线性复杂度的算法。

方法二:

def exp2(a,b):
if b==1:
return a
return a*exp2(a,b-1)

此算法基本步骤为:3b-1步(此处省略推算过程,具体可见参考视频第12分钟处)。因此这个算法也是一个线性复杂度的算法。

方法三:

def exp3(a,b):
if b==1:
return a
if b%2==0: # 如果b是偶数
return (a*a)**(b/2)
else: # 如果b是奇数,a的b次方等于a*a**(b-1)
return a*exp3(a,b-1)

此算法基本步骤为:log b步(此处省略推算过程,具体可见参考视频第17分钟处)。因此这个算法是一个对数复杂度的算法。

方法四:

def exp4(a,b):
ans=0
for i in range(a):
for j in range (b):
ans+=1
return ans

此算法基本步骤为:b2步(此处省略推算过程,具体可见参考视频第20分钟处)。因此这个算法是一个二次方复杂度的算法。

不同类型算法复杂度的时间增长对比:

O(1) --- 输入增大10倍,解决问题的时间不变

O(n) --- 输入增大10倍,解决问题的时间相应增大10倍

O(log n) --- 输入增大10倍,解决问题的时间相应增大1倍

O(n2) --- 输入增大10倍,解决问题的时间相应增大100倍

总结:

1,大O记号法表示算法运行时间的增速。

2,大O记号法并不是以秒为单位,而是用于比较最坏情况下的操作数。

3,当n很大时,O(log n)要比O(n)快很多。

参考:麻省理工学院公开课:计算机科学及编程导论 (第8课)

 

用大O记号法测量算法的效率(Algorithm efficiency Asymptotic notation Big O notation)的更多相关文章

  1. HMM隐马尔科夫算法(Hidden Markov Algorithm)初探

    1. HMM背景 0x1:概率模型 - 用概率分布的方式抽象事物的规律 机器学习最重要的任务,是根据一些已观察到的证据(例如训练样本)来对感兴趣的未知变量(例如类别标记)进行估计和推测. 概率模型(p ...

  2. 图像处理之泛洪填充算法(Flood Fill Algorithm)

    泛洪填充算法(Flood Fill Algorithm) 泛洪填充算法又称洪水填充算法是在很多图形绘制软件中常用的填充算法,最熟悉不过就是 windows paint的油漆桶功能.算法的原理很简单,就 ...

  3. 图像处理------泛洪填充算法(Flood Fill Algorithm) 油漆桶功能

    泛洪填充算法(Flood Fill Algorithm) 泛洪填充算法又称洪水填充算法是在很多图形绘制软件中常用的填充算法,最熟悉不过就是 windows paint的油漆桶功能.算法的原理很简单,就 ...

  4. 维特比算法(Viterbi Algorithm)

      寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于一个特殊的隐马尔科夫模型(HMM)及一个相应的观察序列,我们常常希望 ...

  5. 谷歌的网页排序算法(PageRank Algorithm)

    本文将介绍谷歌的网页排序算法(PageRank Algorithm),以及它如何从250亿份网页中捞到与你的搜索条件匹配的结果.它的匹配效果如此之好,以至于“谷歌”(google)今天已经成为一个被广 ...

  6. 隐马尔科夫模型,第三种问题解法,维比特算法(biterbi) algorithm python代码

    上篇介绍了隐马尔科夫模型 本文给出关于问题3解决方法,并给出一个例子的python代码 回顾上文,问题3是什么, 下面给出,维比特算法(biterbi) algorithm 下面通过一个具体例子,来说 ...

  7. 一致性哈希算法(Consistent Hashing Algorithm)

    一致性哈希算法(Consistent Hashing Algorithm) 浅谈一致性Hash原理及应用   在讲一致性Hash之前我们先来讨论一个问题. 问题:现在有亿级用户,每日产生千万级订单,如 ...

  8. EM算法(Expectation Maximization Algorithm)

    EM算法(Expectation Maximization Algorithm) 1. 前言   这是本人写的第一篇博客(2013年4月5日发在cnblogs上,现在迁移过来),是学习李航老师的< ...

  9. 学习 Rust cookbook 之算法篇(algorithm)

    原文作者:suhanyujie 永久链接:https://github.com/suhanyujie/rust-cookbook-note 博客链接:https://ishenghuo.cnblogs ...

随机推荐

  1. eclipse maven设置

    eclipse 4.4以上版本集成了maven,只需配置一下即可,如果你的eclipse 没有安装maven,可以参考这个文章.http://marketplace.eclipse.org/conte ...

  2. Dubbo负载均衡与集群容错机制

    1  Dubbo简介 Dubbo是一款高性能.轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 作为一个轻量级RPC框架,D ...

  3. Composer之搭建自己的包工具

    作为一个标准的PHPer,必须学会优雅的使用composer,最近,萌生了一个想法,我们每搭建一个项目,里面都会有许多的公用的方法和类库,每次使用的时候就是将其拷贝过来,或者重新写一遍,过于繁琐,效率 ...

  4. vue router 根据不同的id切换链接界面不刷新

    我们一般使用vue的router时候会根据不同的id来切换界面,但是界面没有立刻刷新.下面我们讲下如何解决这个问题. html: <template> <div id="a ...

  5. C#的修饰符

    C#的修饰符 废话少说,直接上总结: 一.在命名空间下: 类:默认修饰符为internal 接口:默认的修饰符为internal 结构体:默认的修饰符为internal 枚举:默认的修饰符为inter ...

  6. Mac 在terminal 上用命令打开sublime

    Step1. 安装Sublime Text编辑器 可直接到以下网址下载dmg安装文件: Sublime Text 3 Step2. 添加命令行别名 打开用户配置文件 vim ~/.bash_profi ...

  7. Linux启动/停止/重启Mysql数据库

    1.查看mysql版本 1)status; 2)select version(); 2.Mysql启动 1)使用 service 启动: service mysqld start (5.0版本是mys ...

  8. 【转帖】Linux定时任务Crontab命令详解

    Linux定时任务Crontab命令详解 https://www.cnblogs.com/intval/p/5763929.html 知道有crontab 以及 at 命令 改天仔细学习一下 讲sys ...

  9. 在linux上安装spark详细步骤

    在linux上安装spark ,前提要部署了hadoop,并且安装了scala. 提君博客原创 对应版本 >>提君博客原创  http://www.cnblogs.com/tijun/   ...

  10. Day 4-7 -configparser模块

    此模块用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser. 常用方法: import configparser conf = configpar ...