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

算法引论

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))。

猜你喜欢

转载自blog.csdn.net/qq_36879493/article/details/108682954