由第二章中我们得知,算法的时间复杂度只与算法的高阶项有关。当输入规模n足够大时,高阶项的花费远超过低阶项。因此在计算算法的运行时间时,尽管可以求出精确的运行时间,但是并不值得我们这么做。

在n很大的情况下,我们更感兴趣的是算法的渐进效率,即,当输入规模无限增加时,在极限中,算法的运行时间如何随着输入规模的变大而变大。

这一章首先介绍了几个“渐进记号”,其中,我们见过Θ记号。然后,我们简单介绍几个函数以及性质。

一、渐进记号

我们用渐进记号来刻画算法的运行时间,以便于方便的描述最坏运行时间函数T(n)。
本章主要介绍Θ、O、Ω记号,以及相对应的θ、o、ω记号(注意大小写)。

上图给出了f(n)与g(n)的关系。标出的n0是最小的可能值。任何大于n0的值都有效。在图一中,表示Θ记号,即将f(n)限制在c1g(n)与c2g(n)之间,给出了f(n)的上下界。图二中表示O记号,给出函数f(n)的上界。图三中表示Ω记号,给出f(n)的下界

Θ记号

由第二章,我们知道插入排序的最坏时间是T(n)=Θ(n²)。我们给出这个记号的定义:对于一个给定的函数g(n),Θ(g(n))表示以下函数的集合
θ(g(n))={f(n):存在常量c1、c2 和 n0,使得对所有n≥n0,有0≤c1g(n)≤f(n)≤c2g(n)}(在集合中,冒号的含义是“使得”)

通俗的说,就是Θ(g(n))表示满足0≤c1g(n)≤f(n)≤c2g(n)的f(n)函数的集合。因为Θ(g(n))是一个集合,可以记作“f(n)∈Θ(g(n))”,以指出f(n)是Θ(g(n))集合中的一员。但是我们通常记作“f(n)=Θ(g(n))”。

由Θ(g(n))的定义可知,每个成员f(n)∈Θ(g(n))均渐进非负。因此,g(n)本身也是渐进非负,否则集合Θ(g(n))为空。我们假设在Θ记号内的每个函数都是渐进非负的。这个假设对其他记号也成立。

O记号

Θ记号给出了函数的上界和下界,而当只有一个渐进上界时使用O记号。对于一个给定的函数g(n),O(g(n))表示以下函数的集合:
O(g(n))={f(n):存在常量c和 n0,使得对所有n≥n0,有0≤f(n)≤cg(n)}

O记号给出了函数的一个在常量因子的上界,在衡量算法的时间复杂度时,我们通常比较算法的最坏时间,所以多数情况下时间复杂度用该记号表示,被称为大O表示法。

我们记f(n)=O(g(n))以指出f(n)是集合O(g(n))的成员。但是注意,f(n)=Θ(g(n))包含着f(n)=O(g(n))。

Ω记号

与O记号相反,当只有一个渐进下届时使用Ω记号,相应的定义为:
Ω(g(n))={f(n):存在常量c和 n0,使得对所有n≥n0,有0≤cg(n)≤f(n)}

Ω记号通常表示最好的情况,例如,插入排序的最好运行时间为Ω(n)。

等式和不等式中的渐进记号

当渐进记号独立于等式或不等式的右边时,我们已经定义等号意味着指出了集合的成员关系。如对n= O(n²),我们是指n∈O(n²)。然而,当渐进记号出现在某个公式中,我们通常解释为我们所不关注名称的匿名函数。

例如,在公式2n²+3n+1=2n²+Θ(n)指的是2n²+3n+1=2n²+f(n)。f(n)是集合Θ(n)中的某个函数。安好这种方式可以帮助消除一个等式中无关紧要的细节与混乱。使得等式更清晰,方便我们抓住重点。例如,在第二章中,归并排序的最坏运行时间我们表示为递归式T(n)=2T(n/2)+Θ(n)。如果只对T(n)的渐进行为感兴趣,那么没有必要把所有低阶项都准确说出来,用包含在Θ(n)中的匿名函数代指他们。

在某些例子中,渐进记号也出现在等式的左边,如:2n²+Θ(n)=Θ(n²),这该如何解释?

我们将其解释为:无论怎样选择等号左边的匿名函数,总有一种办法来选择等号右边的匿名函数使得等式成立。也就是说,对任意函数f(n)∈Θ(n),存在某个函数g(n)∈Θ(n²),使得对所有的n,有2n²+f(n)=g(n)。可以看出,等式左边提供了更多的细节,等式右边更粗糙。当我们把很多个这样的关系链在一起,你就会看的更明白:

2n²+3n+1=2n²+Θ(n)=Θ(n²)

看到了吧,最左边的式子提供了很多具体细节,他将二次项、一次项、常数项都清楚的指出来。而当到第二个式子时,3n+1被泛化成了Θ(n),而在最右边,所有的项都被泛化成最高阶项的集合Θ(n²)。

o记号

由大O记号提供的渐进上界可能是渐进紧确的也可能不是渐进紧确的。例如,界2n²=O(n²)时渐进紧确的,而界2n=O(n²)就不是。当出现这种情况时,我们用o记号来表示一个非渐进紧确的上界。o(g(n))定义为:

o(g(n))={f(n):对任意正常量c>0,存在常量n0>0,使得对所有n>=n0,有0<=f(n)<=cg(n)}

例如2n=o(n²),但是2n²≠o(n²)

从定义上来看,O与o类似,区别是在f(n)=O(g(n))中,界0<=f(n)<=cg(n)对某个常量c>0成立,而在f(n)=o(g(n))中,界0<=f(n)<cg(n)对所有c>0都成立。

在o记号中,当n趋于无穷时,函数f(n)相较与g(n)变得微不足道了,即
limn→∞f(n)/g(n)=0

ω记号

ω与Ω记号的关系和o与O记号关系类似。我们使用ω来表示一个渐进非紧确的下界。定义为:

ω(g(n))={f(n):对任意正常量c>0,存在常量n0>0,使得对所有n>=n0,有0<=cg(n)<f(n)}

他的另一种定义方式是:

f(n)∈ω(g(n))当且仅当g(n)∈o(f(n))

例如:n²/2=ω(n),但是n²/2≠ω(n²)。关系f(n)=ω(g(n))蕴含着
limn→∞f(n)/g(n)=∞
也就是说,如果这个极限存在,当n趋于∞时,f(n)相对于g(n)来说变得任意大了。

各种函数性质

实数的许多关系性质也适用于渐进比较。下面假定f(n)和g(n)渐进为正。

传递性

f(n)=Θ(g(n))且g(n)=Θ(h(n)),则蕴含着f(n)=Θ(h(n))
f(n)=O(g(n))且g(n)=O(h(n)),则蕴含着f(n)=O(h(n))
f(n)=Ω(g(n))且g(n)=Ω(h(n)),则蕴含着f(n)=Ω(h(n))
f(n)=o(g(n))且g(n)=o(h(n)),则蕴含着f(n)=o(h(n))
f(n)=ω(g(n))且g(n)=ω(h(n)),则蕴含着f(n)=ω(h(n))

自反性

f(n)=Θ(f(n))
f(n)=O(f(n))
f(n)=Ω(f(n))

对称性

f(n)=Θ(g(n))当且仅当g(n)=Θ(f(n))

转置对称性

f(n)=O(g(n))当且仅当g(n)=Ω(f(n))
f(n)=o(g(n))当且仅当g(n)=ω(f(n))

二、标准记号与常用函数

本书里用到的大多数数学函数与记号在学习生涯中基本都学过,这里不再赘述,只简单介绍一下斐波那契数列。

斐波那契数列
按照下面的递归式定义斐波那契数列:
F0=0
F1=1
F2=F0+F1=1
F3=F2+F1=2

Fn=Fn-1 + Fn-2,n大于等于2

即从第三项开始,每一项都是前两项的和。
斐波那契数列的通项公式为:

扩展:通项公式推导(来源:百度百科——斐波那契数列)

利用特征方程(线性代数解法)

线性递推数列的特征方程为:

x²=x+1

解得 





解得


(完)

《算法导论》第三章 函数的增长 个人心得——记号与常用函数相关推荐

  1. 算法导论第三版第十一章11.1-4

    算法导论第三版第十一章11.1-4 我们希望在一个非常大的数组上,通过利用直接寻址的方式来实现一个字典.开始时,该数组中可能包含一些无用信息,但要堆整个数组进行初始化时不太实际的,因为该数组的规模太大 ...

  2. 带权中位数-算法导论第三版第九章思考题9-2

    带权中位数-算法导论第三版第九章思考题9-2 b 时间复杂度O(nlgn) float find_median_with_weights_b(float *array,int length) {qui ...

  3. 算法导论第三版第二章思考题答案

    算法导论第三版第二章思考题答案 第二章思考题 算法导论第三版第二章思考题答案 2.1 2.2 2.3 2.4 汇总传送门 2.1 #include<iostream> using name ...

  4. 找出第i个小元素(算法导论第三版9.2-4题)

    找出第i个小元素(算法导论第三版9.2-4题) 期望时间复杂度:Θ(n) 最坏情况的时间复杂度Θ(n^2) int randomized_select_based_loop(int *array,in ...

  5. 软件工程导论第三章复习总结附思维导图

    软件工程导论第三章复习总结附思维导图 概述 准确回答"系统必须做什么" 必须理解并描述问题的信息域,根据这条准则应该建立数据模型 必须定义软件应该完成的功能,这条准则要求建立功能模 ...

  6. 算法导论第三版3.1答案

    算法导论第三版3.1答案 这一章数学公式实在太多了..打不过来,为了节约时间就用纸笔写了. 2.2 算法导论第三版3.1答案 汇总传送门 汇总传送门 链接: [算法导论习题答案汇总]

  7. 给出TREE_INSERT过程的非递归版本(算法导论第三版12.3-1)

    给出TREE_INSERT过程的非递归版本(算法导论第三版12.3-1) template<typename T> void insert_recursive(BinaryTree< ...

  8. 写出TREE-PREDECESSOR的伪代码(算法导论第三版12.2-3)

    写出TREE-PREDECESSOR的伪代码(算法导论第三版12.2-3) TREE-PREDECESSOR(x)if x.left != NILreturn TREE-MAXIMUM(x.left) ...

  9. 写出TREE-MINIMUM 和TREE-MAXIMUM的递归版本(算法导论第三版12.2-2)

    写出TREE-MINIMUM 和TREE-MAXIMUM的递归版本(算法导论第三版12.2-2) template<typename T> BinaryTreeNode<T>* ...

最新文章

  1. VINS-mono详细解读与实现
  2. 「图文直播」CSDN 二十年,AI 赋能全新出发
  3. 与 Linux 一起学习:学习打字
  4. Python字典理解
  5. databricks使用
  6. mysql dba系统学习(2)了解mysql的源码目录及源文件
  7. Python3的unittest用例按编写顺序执行
  8. socket编程实现文件传输功能
  9. Linux工作笔记033---Linux(CentOS7)安装zip、unzip命令
  10. mysql对sql的支持并不是太好_MySQL数据库优化总结
  11. javascript 编程指南
  12. 构成vspher虚拟化平台所需构建概念
  13. iwconfig命令
  14. Ubuntu 16.04 安装 搜狗输入法
  15. gtk3基础知识的学习(C语言)
  16. 错误Could not locate executable null\bin\winutils.exe in the Hadoop binaries的解决方案
  17. codeforces 884B Japanese Crosswords Strike Back
  18. AssertionError: Attempted unscale_ but _scale is None
  19. Hutool - 对于网络的一些方法和增强
  20. vue项目落地(qiankun.js)微前端服务

热门文章

  1. 非科班的我拿到了网易自然语言处理实习offer
  2. MySQL数据类型int、bigint、smallint 和 tinyint的区别
  3. 二本计算机考研简单吗,二本考生考研一本院校难不难?不要听那些奇怪论调,而要看这些!...
  4. RPGMaker MV 插件基础02:插件的参数定义
  5. C和C++的区别大赏
  6. Docker 完整基础 (续) (二)
  7. 15.OpenWrt-U盘和TF卡存储
  8. java字符集编码_Java字符集编码
  9. 安忆往昔的唯美伤感空间日志发布:那杯白开水,很伤很美
  10. 日常英语-从起床到出门