摘要
      本文论述了在算法分析领域一个重要问题——时间复杂度分析的基础内容。本文将首先明确时间复杂度的意义,而后以形式化方式论述其在数学上的定义及相关推导。从而帮助大家从本质上认清这个概念。

前言
      通常,对于一个给定的算法,我们要做  两项分析。 第一是从数学上证明算法的正确性 ,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式、数学归纳法等。而在证明算法是正确的基础上, 第二部就是分析算法的时间复杂度 。算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否。因此,作为程序员,掌握基本的算法时间复杂度分析方法是很有必要的。
      但是很多朋友并不能清晰的理解这一概念,究其原因,主要是因为没有从数学层面上理解其本质,而是习惯于从直观理解。下面,我们就一步步走近算法时间复杂度的数学本质。

算法时间复杂度的数学意义
      从数学上定义, 给定算法A,如果存在函数F(n),当n=k时,F(k)表示算法A在输入规模为k的情况下的运行时间,则称F(n)为算法A的时间复杂度 。
      这里我们首先要明确输入规模的概念。关于输入规模,不是很好下定义,非严格的讲,输入规模是指算法A所接受输入的自然独立体的大小。例如,对于排序算法来说,输入规模一般就是待排序元素的个数,而对于求两个同型方阵乘积的算法,输入规模可以看作是单个方阵的维数。为了简单起见,在下面的讨论中,我们总是假设算法的输入规模是用大于零的整数表示的,即n=1,2,3,……,k,……
      我们还知道,对于同一个算法,每次执行的时间不仅取决于输入规模,还取决于输入的特性和具体的硬件环境在某次执行时的状态。所以想要得到一个统一精确的F(n)是不可能的。为了解决这个问题,我们做一下两个说明:
      1.忽略硬件及环境因素,假设每次执行时硬件条件和环境条件是完全一致的。
      2.对于输入特性的差异,我们将从数学上进行精确分析并带入函数解析式。

算法时间复杂度分析示例
      为了便于朋友们理解,我将不会采用教科书上惯用的快速排序、合并排序等经典示例进行分析,而是使用一个十分简单的算法作为示例。我们先来定义问题。
      问题定义:
      输入——此问题输入为一个有序序列,其元素个数为n,n为大于零的整数。序列中的元素为从1到n这n个整数,但其顺序为完全随机。
      输出——元素n所在的位置。(第一个元素位置为1)

这个问题非常简单,下面直接给出其解决算法之一(伪代码):

LocationN(A)
      {
            for(int i=1;i<=n;i++)-----------------------t1
            {
                  if(A[i] == n) ----------------------------t2
                        { return i; }------------------------t3
            }
      }

      我们来看看这个算法。其中t1、t2和t3分别表示此行代码执行一次需要的时间。
      首先,输入规模n是影响算法执行时间的因素之一。在n固定的情况下,不同的输入序列也会影响其执行时间。最好情况下,n就排在序列的第一个位置,那么此时的运行时间为“t1+t2+t3”。最坏情况下,n排在序列最后一位,则运行时间为“n*t1+n*t2+t3=(t1+t2)*n+t3”。可以看到,最好情况下运行时间是一个常数,而最坏情况下运行时间是输入规模的线性函数。那么,平均情况如何呢?
      问题定义说输入序列完全随机,即n出现在1...n这n个位置上是等可能的,即概率均为1/n。而平均情况下的执行次数即为执行次数的数学期望,其解为:

E
      = p(n=1)*1+p(n=2)*2+...+p(n=n)*n
      = (1/n)*(1+2+...+n)
      = (1/n)*((n/2)*(1+n))
      = (n+1)/2

      即在平均情况下for循环要执行(n+1)/2次,则平均运行时间为“(t1+t2)*(n+1)/2+t3”。
      由此我们得出分析结论:
      t1+t2+t3 <= F(n) <= (t1+t2)*n+t3,在平均情况下F(n) = (t1+t2)*(n+1)/2+t3

算法的渐近时间复杂度
      以上分析,我们对算法的时间复杂度F(n)进行了精确分析。但是,很多时候,我们不需要进行如此精确的分析,原因有下:
      1.在较复杂的算法中,进行精确分析是非常复杂的。
      2.实际上,大多数时候我们并不关心F(n)的精确度量,而只是关心其量级。
      基于此,提出渐近时间复杂度的概念。在正式给出渐近时间复杂度之前,要先给出几个数学定义:

定义一:Θ(g(n))={f(n) | 如果存在正常数c1、c2和正整数n0,使得当n>=n0时,0<c1g(n)<=f(n)<=c2g(n)恒成立}
      定义二:Ο(g(n))={f(n) | 如果存在正常数c和正整数n0,使得当n>=n0时,0<=f(n)<=cg(n)恒成立}
      定义三:Ω(g(n))={f(n) | 如果存在正常数c和正整数n0,使得当n>=n0时,0<=cg(n)<=f(n)恒成立}

      可以看到,三个定义其实都定义了一个函数集合,只不过集合中的函数需要满足的条件不同。有了以上定义,就可以定义渐近时间复杂度了。
      不过这里还有个问题:F(n)不是确定的,他是在一个范围内变动的,那么我们关心哪个F(n)呢?一般我们在分析算法时,使用最坏情况下的F(n)来评价算法效率,原因有如下两点:
      1.如果知道了最坏情况,我们就可以保证算法在任何时候都不能比这个情况更坏了。
      2.很多时候,算法运行发生最坏情况的概率还是很大的,如查找问题中待查元素不存在的情况。且在很多时候,平均情况的渐近时间复杂度和最坏情况的渐近时间复杂度是一个量级的。

于是给出如下定义: 设F(n)为算法A在最坏情况下F(n),则如果F(n)属于Θ(g(n)),则说算法A的渐近时间复杂度为g(n),且g(n)为F(n)的渐近确界

还是以上面的例子为例,则在上面定义中F(n) = (t1+t2)*n+t3。则F(n)的渐近确界为n,其证明如下:

证明:
      设c1=t1+t2,c2=t1+t2+t3,n0=2
      又因为 t1,t2,t3均大于0
      则,当n>n0时,0<c1n<=F(n)<=c2n 即 0<(t1+t2)*n<=(t1+t2)*n+t3<=(t1+t2+t3)*n恒成立。
      所以 F(n)属于Θ(n)
      所以 n是F(n)的渐近确界
      证毕

在实际应用中,我们一般都是使用渐近时间复杂度代替实际时间复杂度来进行算法效率分析。一般认为,一个渐近复杂度为n的算法要优于渐近复杂度为n^2的算法。注意,这并不是说渐近复杂度为n的算法在任何情况下都一定更高效,而是说在输入规模足够大后(大于临界条件n0),则前一个算法的最坏情况总是好于后一个算法的最坏情况。 事实证明,在实践中这种分析是合理且有效的。
      类似的,还可以给出算法时间复杂度的上确界和下确界  :
       设F(n)为算法A在最坏情况下F(n),则如果F(n)属于Ο(g(n)),则说算法A的渐近时间复杂度上限为g(n),且g(n)为F(n)的渐近上确界。
      设F(n)为算法A在最坏情况下F(n),则如果F(n)属于Ω(g(n)),则说算法A的渐近时间复杂度下限为g(n),且g(n)为F(n)的渐近下确界。

      这里一定要注意, 由于我们是以F(n)最坏情况分析的,所以,我们可以100%保证在输入规模超过临界条件n0时,算法的运行时间一定不会高于渐近上确界,但是并不能100%保证算法运行时间不会低于渐近下确界,而只能100%保证算法的最坏运行时间不会低于渐近下确界。

总结
      算法时间复杂度分析是一个很重要的问题,任何一个程序员都应该熟练掌握其概念和基本方法,而且要善于从数学层面上探寻其本质,才能准确理解其内涵。在以上分析中,我们只讨论了“紧确界”,其实在实际中渐近确界还分为“紧确界”和“非紧确界”,有兴趣的朋友可以查阅相关资料。

好了,本文就到这里了,希望本文内容能对各位有所帮助。

from: http://www.cnblogs.com/leoo2sk/archive/2008/11/14/1332381.html

算法时间复杂度分析基础相关推荐

  1. 算法时间复杂度分析——大O、大Ω、大θ、小o,小ω

    最近开始转战传统算法分析的研究工作了,重新拾起以前学过的一些内容. 目录 一.概述 二.对常见的Ο和Ω进行分析 2.1 大O表示法 2.2 大Ω表示法 三.P问题,NP问题,NP-hard问题,NPC ...

  2. 随机选择算法时间复杂度分析

    随机选择算法时间复杂度分析 首先提供算法的伪代码: 算法是递归算法 时间复杂度分析思路:计算每一次递归语句所消耗的时间,再求和. 为了分析需要,我们先定义如下的量: 定义状态j:数组长度在[(3/4) ...

  3. 算法时间复杂度分析专题一(帮助快速解题)

    笔试: 题目告诉数据范围,根据题目的数据范围来考虑用什么解法 c++竞赛:一般时限1~2秒 时间范围内指令操作次数<10^8 不同数据范围下,代码时间复杂度和算法该如何选择: n<=30, ...

  4. python数据结构和算法 时间复杂度分析 乱序单词检测 线性数据结构 栈stack 字符匹配 表达式求值 queue队列 链表 递归 动态规划 排序和搜索 树 图

    python数据结构和算法 参考 本文github 计算机科学是解决问题的研究.计算机科学使用抽象作为表示过程和数据的工具.抽象的数据类型允许程序员通过隐藏数据的细节来管理问题领域的复杂性.Pytho ...

  5. 算法效率分析基础-算法四

    主要内容: 介绍研究算法效率的通用框架 介绍三种符号: O(读作O),Ω(读作omega),和Θ(读作theta).这些数学借来的符号已经成为讨论算法效率的特定语言 使用通用框架系统对非递归算法进行分 ...

  6. 【排序算法时间复杂度分析】递推式

    关于包含递归调用的时间复杂度分析-以归并排序为例子 T(n) = 2*T(n/2)+O(n) T(n/2) 代表一次递归 O(n)代表合并 T(1) = T(1) T(n)/n = T(n/2)/(n ...

  7. Dijkstra算法时间复杂度分析

    文章目录 Dijkstra算法的思路与关键点 Dijkstra算法的时间复杂度 之前一直默认Dijkstra算法时间复杂度为 o(n2)o(n^{2})o(n2),没有思考过具体的时间复杂度,今天把这 ...

  8. 排序算法时间复杂度分析

    排序算法中比较次数与初始元素序列排序无关的只有选择排序和基数排序,其他的都有关.元素的移动次数与关键字的初始排列次序无关的是:基数排序 元素的比较次数与初始序列无关是:选择排序.折半插入排序 算法的时 ...

  9. 《算法设计与分析基础》Chapt 2 算法效率分析基础

    2.1 分析框架 2.1.1 输入规模的度量 大多数情况,以输入数n 矩阵,维数 数值算法,数字的比特数 2.1.2 运行时间的度量单位 找出算法中最重要的操作,即基本操作 计算他们的运行次数 2.1 ...

最新文章

  1. 文字输入限制_输入框设计,设计样式及设计要点
  2. jQuery ajax 和 普通js ajax 笔记
  3. laravel api_如何在现有的Laravel应用中获取即时GraphQL API
  4. 【Computer Organization笔记10】单周期CPU设计:基于7条MIPS指令的数据通路
  5. Bug--Tomcat Error start child
  6. 一阶惯性环节如何实现跟踪性能与滤波性能共存(三)
  7. 句法结构中的语义分析
  8. Vue中插入jQuery插件
  9. 利用qiime2分析微生物组16S rRNA数据小结
  10. 2022年二级建造师《专业工程管理与实务(公路)》综合测试题及答案
  11. 大数据技术之Hive+Flume+Zookeeper+Kafka详解
  12. Failed to unmount /storage/udisk (Device or resource busy, retries 6, action 0)
  13. 解决Ubuntu Linux终端输入命令没有颜色提示的设置方法
  14. oracle通信通道的文件结尾_“ORA-03113: 通信通道的文件结尾”报错处理
  15. iamp是什么意思计算机网络,IAMP是什么意思
  16. 动作捕捉技术在四足仿生机器人研究中的应用
  17. 付费专栏-付费课程-【购买须知】
  18. JAVA Helloworld以及JAVA标识符命名规范
  19. MySQL查询缓存前世今生
  20. Python:实现knapsack背包问题算法(附完整源码)

热门文章

  1. 梯度下降和随机梯度下降为什么能下降?
  2. 机器学习是如何运作的?谷歌来告诉你
  3. 你认识它们吗?2014十大科技流行词
  4. Java Review - 并发编程_PriorityBlockingQueue原理源码剖析
  5. MySQL - 锁机制初探
  6. java线程占用CPU_在windows下揪出java程序占用cpu很高的线程并完美解决
  7. (三) LtRecyclerView v2.x (自定义上拉和下拉刷新View)
  8. kibana显示JAVA,elasticsearch kibana简单查询讲解
  9. anguarjs 上传图片预览_MIUI12 20.10.29更新,新版「模糊预览图」
  10. 控制显示隐藏_iOS13隐藏了5个超实用新功能:让iPhone的使用体验更好