算法设计与分析学习笔记之——算法引论

本文介绍了算法的基本概念,探讨了算法与程序的区别,以及如何通过抽象数据类型实现算法设计的模块化。此外,还详细讲解了算法复杂度分析,包括时间复杂度和空间复杂度的定义与计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法引论

1、算法与程序

通俗地来讲,算法是指解决问题的方法或过程。严格地来将,算法是满足以下性质的指令序列

  • 输入:有零个或多个外部量作为算法的输入。
  • 输出:算法产生至少一个量作为输出。
  • 确定性:组成算法的每条指令是清晰的、无歧义的。
  • 有限性:算法中每条指令的执行次数有限,执行每条指令的时间也有限。

程序与算法有所不同。程序是算法用某种程序设计语言的具体实现。程序可以不满足算法的性质(4)即有限性。例如操作系统,它是在无限的循环中执行的程序,因而不是算法。


2、表达算法的抽象机制

算法从非形式的自然语言表达形式转换为形式化的高级语言是一个复杂的过程,仍然要做很多繁杂琐碎的事情,因而需要进一步抽象。

对于一个明确的数学问题,设计它的算法,总是先选用该问题的一个数据模型。接着弄清楚该问题数据模型在已知条件下的初始状态和要求的结果状态以及这两个状态之间的隐含关系

按照自顶向下逐步求精的原则,在探索运算步骤时,首先应该考虑算法顶层的运算步骤,然后再考虑底层的运算步骤。所谓的顶层的运算步骤是指数据模型级上的运算步骤,或称宏观步骤。它们组成算法的主干部分。这部分算法通常用非形式的自然语言表达。其中,涉及的数据是数据模型中的变量,暂时不关心它的数据结构涉及的运算以数据模型中的数据变量作为运算对象,或作为运算结果。所谓的底层运算步骤是指顶层抽象运算的具体实现。它们依赖于数据模型的结构,依赖于数据模型结构的具体表示。因此,底层运算步骤包括两部分:一是数据模型的具体表示;二是定义在该数据模型上的运算的具体实现为了将顶层运算与底层运算算法分隔开,使二者在设计时不互相牵制、互相影响,必须对二者的接口进行抽象。让底层只通过接口为顶层服务,顶层也只能通过接口调用底层运算。这个接口就是抽象数据类型,简记为 ADT

严格地来说,抽象数据类型是算法的一个数据模型连同定义在该模型上并作为算法构件的一组运算。这个概念明确地把数据模型与该模型上的运算紧紧地联系起来。一方面,数据模型上的运算依赖于数据模型的具体表示,数据模型上的运算以数据模型中的数据变量为运算对象,或作为运算结果,或二者兼而为之;另一方面,有了数据模型的具体表示,有了数据模型上运算的具体实现,运算效率随之确定。数据模型与定义在该模型上的运算之间存在的这种密不可分的联系是抽象数据类型概念产生的背景和依据

使用抽象数据类型带给算法设计的好处主要有:

  1. 算法顶层设计与底层实现分离;
  2. 算法设计与数据结构设计隔开;
  3. 数据模型和该模型上的运算统一在抽象数据类型中,反映他们之间内在的互相依赖性和互相制约的关系,便于空间和时间的消耗的折衷,灵活地满足用户要求;
  4. 由于顶层设计和底层设计实现局部化,在设计中出现差错也是局部的,因而容易查找也容易纠正;
  5. 算法自然呈现模块化,抽象数据类型的表示和实现可以封装,便于移植和重用;
  6. 为自顶向下逐步求精和模块化提供有效的途径和工具;
  7. 算法结构清晰,层次分明,便于算法正确性的证明和复杂性的分析。

3、算法复杂度分析

时间复杂度空间复杂度

3.1、时间复杂度的定义

如果分别用 N、I 和 A 表示算法要解决的问题的规模、算法的输入和算法本身,而且用 C 来表示复杂性,那么,应该有 C = F(N,I,A)。其中 ,F(N,I,A) 是 N、I 和 A 的确定的 3 元函数。如果把时间复杂度和空间复杂度分开,并分别用 T 和 S 来表示,那么应该有 T = T(N,I,A) 和 S = S(N,I,A)。通常,让 A 隐含在复杂性函数名当中,因为将 T 和 S 分别简写为 T = T(N,I) 和 S = S(N,I)。

根据 T(N,I) 的概念,它应该是算法在一台抽象的计算机上运行所需要的时间。设此抽象的计算机所提供的元运算有 k 种,它们分别记为 O1,O2,…,Ok。又设每执行一次这些元运算所需要的时间分别为 t1,t2,…,tk。对于给定的算法 A,设经统计用到元运算 Oi 的次数为 ei,i = 1,2,…,k。很清楚,对于每个 i,1<= i <= k,ei 是 N 和 I 的函数,即 ei = ei(N,I)。因此:
T ( N , I ) = ∑ i = 1 k t i e i ( N , I ) T(N , I)=\sum_{i=1}^{k} t_{i} e_{i}(N, I) T(N,I)=i=1ktiei(N,I)
其中,ti(i = 1,2,…,k)是与 N 和 I 无关的常数。

显然,不可能对规模 N 的每一种合法的输入 I 都去统计 ei(N,I),i = 1,2,…,k。因此,T(N,I) 的表达式还得进一步简化。或者说,只能在规模为 N 的某些或某类有代表性的合法输入中统计相应的 ei,以及评价时间复杂度。

考虑 3 种时间复杂性,即最坏时间复杂性最好时间复杂性平均时间复杂性,并记为 Tmax(N)、Tmin(N)和 Tavg(N):

T max ⁡ ( N ) = max ⁡ I ∈ D N T ( N , I ) = max ⁡ I ∈ D N ∑ i = 1 k t i e i ( N , I ) = ∑ i = 1 k t i e i ( N , I ∗ ) = T ( N , I ∗ ) T_{\max }(N)=\max _{I \in D_{N}} T(N, I)=\max _{I \in D_{N}} \sum_{i=1}^{k} \mathrm{t}_{i} e_{i}(N, I)=\sum_{i=1}^{k} \mathrm{t}_{i} e_{i}\left(N, I^{*}\right)=T\left(N, I^{*}\right) Tmax(N)=IDNmaxT(N,I)=IDNmaxi=1ktiei(N,I)=i=1ktiei(N,I)=T(N,I)

T min ⁡ ( N ) = min ⁡ I ∈ D N T ( N , I ) = min ⁡ I ∈ D N ∑ i = 1 k t i e i ( N , I ) = ∑ i = 1 k t i e i ( N , I ~ ) = T ( N , I ~ ) \mathrm{T}_{\min }(N)=\min _{I \in D_{N}} T(N, I)=\min _{I \in D_{N}} \sum_{i=1}^{k} t_{i} e_{i}(N, I)=\sum_{i=1}^{k} \mathrm{t}_{i} e_{i}(N, \tilde{I})=T(N, \tilde{I}) Tmin(N)=IDNminT(N,I)=IDNmini=1ktiei(N,I)=i=1ktiei(N,I~)=T(N,I~)

T a v g ( N ) = ∑ I ∈ D N P ( I ) T ( N , I ) = ∑ I ∈ D N P ( I ) ∑ i = 1 k t i e i ( N , I ) \mathrm{T}_{a v g}(N)=\sum_{I \in D_{N}} P(I) T(N, I)=\sum_{I \in D_{N}} P(I) \sum_{i=1}^{k} \mathrm{t}_{i} e_{i}(N, I) Tavg(N)=IDNP(I)T(N,I)=IDNP(I)i=1ktiei(N,I)

其中,Dn 是规模为 N 的合法输入集合;I* 是 Dn 中使 T(N,I*) 达到 Tmax(N)的合法输入;I 是 Dn 中使 T(N,I) 达到 Tmin(N) 的合法输入;而 P(I) 是在算法的应用中出现输入 I 的概率。

以上三种时间复杂度,实践表明,可操作性最好且最有实际价值的是最坏情况下的时间复杂度

3.2、简化时间复杂性的分析——复杂性渐近性态

随着要求计算机解决的问题越来越复杂,规模越来越大。引入了复杂性渐近性态的概念。设 T(n) 是前面所提到的关于算法 A 的复杂性函数。一般来说,当 N 单调增加且趋于 ∞ 时,T(n) 也将单调增加趋于 ∞。对于 T(n),如果存在 ~T(n),使得当 N → ∞ 时有
T ( n ) − T ~ ( n ) T ( n ) → O \frac{T(n)-\tilde{T}(n)}{T(n)} \rightarrow O T(n)T(n)T~(n)O
那么,就说 ~T(n) 是 T(n) 当 N → ∞ 时的渐近性态,或称 ~T(n) 为算法 A 当 N → ∞ 的渐近复杂性而与 T(n) 相区别。在直观上,~T(n) 是 T(n) 中省略去低阶项留下的主项,所以它确实比 T(n) 简单。

考虑到分析算法的复杂性的目的在于比较求解同一问题的两个不同算法的效率。而当要比较的两个算法的渐近复杂性的阶不相同时,只要确定出各自的阶,就可以判定哪一个算法的效率高换句话说,这时的渐近复杂性分析只要关心 ~T(n) 中的阶就够了,不必关心包含在 ~T(n) 中的常数因子。

3.3、复杂性渐近性态下的一些符号

综上简化了算法复杂性分析的方法和步骤即只要考查当问题的规模充分大时,算法复杂性在渐进意义下的阶。与此简化的复杂性分析相配套,需要引入以下渐近意义下的记号:
O , Ω , θ , o O, \Omega, \theta, o O,Ω,θ,o

3.3.1、符号—— O

以下设 f(N) 和 g(N) 是定义在正数集上的正函数。

如果存在正的常数 C 和自然数 N0,使得当 N >= N0 时有 f(N) <= Cg(N),则称函数 f(N) 当 N 充分大时上有界,且 g(N) 是它的一个上界,记为 f(N) = O(g(N))。这时还说 f(N) 的阶不高于 g(N) 的阶。

按照符号 O 的定义,容易得到它有如下运算规则:

  1. O(f) + O(g) = O(max(f,g))
  2. O(f) + O(g) = O(f+g)
  3. O(f)O(g) = O(fg)
  4. 如果 g(N) = O(f(N)),则 O(f) + O(g) = O(f)
  5. O(Cf(N)) = O(f(N)),其中 C 是一个正的常数
  6. f = O(f)

应该指出,根据符号 O 的定义,用它来评估算法的复杂性,得到的只是当规模充分大时的一个上界这个上界的阶越低时评估就越精确,结果就越有价值

3.3.2、符号—— Ω

符号 Ω 的定义:如果存在正的常数 C 和自然数 N0,使得当 N >= N0 时有 f(N) >= Cg(N),则称函数 f(N) 当 N 充分大时下有界,且 g(N) 是它的一个下界,记为 f(N) = Ω(g(N))。这时还说 f(N) 的阶不低于 g(N) 的阶。

Ω 的这个定义的优点是与 O 的定义对称,缺点是当 f(N) 对自然数的不同无穷子集有不同的表达式时,且有不同的阶时,未能很好地刻画 f(N) 的下界。比如:
f ( N ) = { 100 N  为正偶数  6 N 2 N  为正奇数  f(N)=\left\{\begin{array}{ll} 100 & N \text { 为正偶数 } \\ 6 N^{2} & N \text { 为正奇数 } \end{array}\right. f(N)={1006N2N 为正偶数 N 为正奇数 
时,按照上面的定义,只能得到 f(N) = Ω(1)。这个并没有什么意义。

同样需要指出,用 Ω 评估算法的复杂性,得到的只是该复杂性的一个下界。这个下界的阶越高,则评估就越精确,结果就越有价值。它通常与符号 O 配合以证明某问题的一个特定算法是该问题的最优算法或该问题的某算法类中的最优算法。

3.3.3、符号—— θ

θ 的定义:f(N) = θ(g(N)) 当且仅当 f(N) = O(g(N)) 且 f(N) = Ω(g(N))。这时称 f(N) 与 g(N) 同阶

3.3.4、符号—— o

o 的定义:任意给定的 ε > 0,都存在正整数 N0,使得当 N >= N0 时有 f(N) / g(N) <= ε,则称函数 f(N) 当 N 充分大时的阶比 g(N) 低,记为 f(N) = o(g(N))。

本书是国际算法大师乌迪·曼博(Udi Manber)博士撰写的一本享有盛誉的著作。全书共分12章:第1章到第4章为介绍性内容,涉及数学归纳法、算法分析、数据结构等内容;第5章提出了归纳证明进行类比的算法设计思想;第6章到第9章分别给出了4个领域的算法,如序列和集合的算法、图算法、几何算法、代数和数值算法;第10章涉及归约,也是第11章的序幕,而后者涉及NP完全问题;第12章则介绍了并行算法;最后是部分习题的答案及参考文献。本书的特色有二,旨在提高读者的问题求解能力,使读者能够理解算法设计的过程和思想:一是强调算法设计的创造性过程,注重算法设计背后的创造性思想,而不拘泥于某个具体算法的详细讨论;二是将算法设计类比于定理归纳证明,揭示了算法设计的基本思想和本质。 本书的组织结构清晰且易于理解,强调了创造性,具有浓郁特色,时至今日仍有其巨大的价值,并且适合作为计算机及相关专业算法和高级算法课程的教材。 第1章 引论 第2章 数学归纳法 2.1 引言 2.2 三个简单的例子 2.3 平面内区域的计数 2.4 简单的着色问题 2.5 复杂一些的加法题 2.6 一个简单的不等式 2.7 欧拉公式 2.8 图论中的一个问题 2.9 格雷码 2.10 在图上寻找无重边的路 2.11 数学平均数和几何平均数定理 2.12 循环不变量:将十进制数转换为二进制数 2.13 常见的错误 2.14 小结 第3章 算法分析 3.1 引言 3.2 符号O 3.3 时间空间复杂度 3.4 求和 3.5 递推关系 3.5.1 巧妙地猜测 3.5.2 分治关系 3.5.3 涉及全部历史的递推关系 3.6 一些有用的证明论据 3.7 小结 第4章 数据结构简介 4.1 引言 4.2 基本数据结构 4.2.1 元素 4.2.2 数组 4.2.3 记录 4.2.4 链表 4.3 树 4.3.1 树的表示 4.3.2 堆 4.3.3 二叉搜索树 4.3.4 AVL树 4.4 散列 4.5 合并?查找问题 4.6 图 4.7 小结 第5章 基于归纳的算法设计 5.1 引言 5.2 多项式求值 5.3 最大导出子图 5.4 寻找一对一映射 5.5 社会名流问题 5.6 分治算法:轮廓问题 5.7 在二叉树中计算平衡因子 5.8 寻找最大连续子序列 5.9 增强归纳假设 5.10 动态规划:背包问题 5.11 常见的错误 5.12 小结 第6章 序列和集合的算法 6.1 引言 6.2 二叉搜索的几种形式 6.2.1 纯二叉搜索 6.2.2 循环序列的二叉搜索 6.2.3 二叉搜索特殊下标 6.2.4 二叉搜索长度未知的序列 6.2.5 重叠子序列问题 6.2.6 解方程 6.3 内插搜索 6.4 排序 6.4.1 桶排序和基数排序 6.4.2 插入排序和选择排序 6.4.3 归并排序 6.4.4 快速排序 6.4.5 堆排序 6.4.6 排序问题的下界 6.5 顺序统计 6.5.1 最大数和最小数 6.5.2 查找第k小的数 6.6 数据压缩 6.7 串匹配 6.8 序列比较 6.9 概率算法 6.9.1 随机数 6.9.2 着色问题 6.9.3 将拉斯维加斯算法变换成确定性算法 6.10 查找众数 6.11 三个展现有趣证明方法的问题 6.11.1 最长递增序列 6.11.2 查找集合中两个最大的元素 6.11.3 计算多重集合的模 6.12 小结 第7章 图算法 7.1 引言 7.2 欧拉图 7.3 图的遍历 7.3.1 深度优先搜索 7.3.2 广度优先搜索 7.4 拓扑排序 7.5 单源最短路径 7.6 最小代价生成树 7.7 全部最短路径 7.8 传递闭包 7.9 图的分解 7.9.1 双连通分支 7.9.2 强连通分支 7.9.3 利用图分解的例子 7.10 匹配 7.10.1 非常稠密图中的完美匹配 7.10.2 偶图匹配 7.11 网络流量 7.12 哈密尔顿旅行 7.12.1 反向归纳 7.12.2 在非常稠密图中找哈密尔顿回路 7.13 小结 第8章 几何算法 8.1 引言 8.2 判定点是否在多边形内部 8.3 构造简单多边形 8.4 凸包 8.4.1 直接方法 8.4.2 礼品包裹算法 8.4.3 Graham扫描算法 8.5 最近点对 8.6 水平线段和竖直线段的交点 8.7 小结 第9章 代数和数值算法 9.1 引言 9.2 求幂运算 9.3 欧几里得算法 9.4 多项式乘法 9.5 矩阵乘法 9.5.1 Winograd算法 9.5.2 Strassen算法 9.5.3 布尔矩阵 9.6 快速傅里叶变换 9.7 小结 第10章 归约 10.1 引言 10.2 归约的例子 10.2.1 简单字符串匹配问题 10.2.2 特殊代表集 10.2.3 关于序列比较的归约 10.2.4 在无向图中寻找三角形 10.3 有关线性规划的归约 10.3.1 概述定义 10.3.2 归约到线性规划的例子 10.4 下界的归约 10.4.1 寻找简单多边形算法复杂度的下界 10.4.2 关于矩阵的简单归约 10.5 常见的错误 10.6 小结 第11章 NP完全问题 11.1 引言 11.2 多项式时间归约 11.3 非确定性和Cook定理 11.4 NP完全性的证明例子 11.4.1 顶点覆盖问题 11.4.2 支配集问题 11.4.3 3SAT问题 11.4.4 团问题 11.4.5 3着色问题 11.4.6 一般经验 11.4.7 更多的NP完全问题 11.5 处理NP完全问题的技术 11.5.1 回溯法和分枝限界法 11.5.2 确保性能的近似算法 11.6 小结 第12章 并行算法 12.1 引言 12.2 并行计算模型 12.3 共享存储器算法 12.3.1 并行加 12.3.2 寻找最大数的算法 12.3.3 并行前缀问题 12.3.4 在链表中查寻秩 12.3.5 欧拉遍历技术 12.4 互连网络上的算法 12.4.1 阵列上的排序 12.4.2 排序网络 12.4.3 在树中查找第k个最小元素 12.4.4 网孔上的矩阵乘法 12.4.5 超立方体中的路由 12.5 脉动计算 12.5.1 矩阵向量相乘 12.5.2 卷积问题 12.5.3 序列的比较 12.6 小结 部分习题答案 参考文献
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值