1 算法分析概述(实现技术的模型)
分析算法目的:预测算法需要的资源。
资源度量:
- 计算时间
- 硬件资源,比如内存、通信带宽等
tip:随着现代技术水平进步和工艺提升,硬件资源逐渐提高,我们更关心 时间问题。
在能分析一个算法之前,我们必须有一个要使用的实现技术的模型,包括描述所有资源及其代价的模型。我们假定一种通用的单处理器计算模型-随机访问(random-access machine,RAM)来作为我们的实现技术,算法可以用计算机程序实现。在RAM模型中,指令一条接一条的执行,没有并发操作。
- RAM模型指令及其代价:
- 真实计算机常见指令:算术指令、数据移动指令(装入、存储、复制)和控制指令(条件与无条件转移、子进程调用与返回)。
- 每条这样的指令所需时间都为常量。
- RAM模型的数据类型:
- 整数型和浮点实数型。
- 我们对每个数据字的规模假定一个范围
- 在RAM模型中,我们不对当代计算机中常见的内存层次进行建模。
我们想要一种表示方法,它的书写和处理都比较简单,并能够表明算法资源需求的重要特征,同时能够抑制乏味的细节。
2 插入排序算法的分析
过程INSERTION-SORT需要的时间依赖:
- 输入规模
- 排序程序
一般来说,算法需要的时间与输入的规模同步增长,所以通常把一个程序的运行时间描述成器输入规模的函数。
输入规模的最佳概念依赖于研究的问题。对许多问题,如排序或快速傅里叶变换,最自然的度量是输入中的项。对于其他问题,如两个整数相乘,输入规模的最佳度量是用通常的二进制记号表示输入所需的总位数。有时,用两个数而不是一个数来描述输入规模更合适,比如输入是一个图,则输入规模可以用该图中的定点数和边数描述。
一个算法再特定输入上的运行时间是指执行的基本操作数或步数。定义“步”的概念以便尽量独立于机器。目前,我们采纳一下观点:执行每行伪代码需要常量时间,假定第 i i i行的每次执行时间为 c i c_i ci(是一个常量),一行与另一行可能不同。
我们首先给出过程INSERTION-SORT中,每条语句的执行时间和执行次数。对 j = 2 , 3 , ⋯ , n , n = A . l e n g t h j=2,3,\cdots,n,n=A.length j=2,3,⋯,n,n=A.length,假设 t j t_j tj表示对第5行执行while测试的次数。当一个for或者while循环按通常的方式(即由于循环头中的测试)退出时,执行测试的次数比执行循环体的次数多1.我们假定住宿是不可执行的语句,所以它们不需要时间。
行 | 伪代码 | 代价 | 次数 |
---|---|---|---|
1 | for j = 2 j=2 j=2 to A.length | c 1 c_1 c1 | n |
2 | k e y = A [ j ] key=A[j] key=A[j] | c 2 c_2 c2 | n-1 |
3 | // 把 A [ j ] A[j] A[j]插入有序子数组 A [ 1 ⋯ j − 1 ] A[1\cdots j-1] A[1⋯j−1] | 0 | n-1 |
4 | i = j − 1 i = j - 1 i=j−1 | c 4 c_4 c4 | n-1 |
5 | w h i l e i > 0 a n d A [ i ] > k e y while i > 0\quad and\quad A[i] \gt key whilei>0andA[i]>key | c 5 c_5 c5 | ∑ j = 2 n t j \sum_{j=2}^n t_j ∑j=2ntj |
6 | A [ i + 1 ] = A [ i ] A[i+1] = A[i] A[i+1]=A[i] | c 6 c_6 c6 | ∑ j = 2 n ( t j − 1 ) \sum_{j=2}^n (t_j-1) ∑j=2n(tj−1) |
7 | i = i - 1 | c 7 c_7 c7 | ∑ j = 2 n ( t j − 1 ) \sum_{j=2}^n (t_j-1) ∑j=2n(tj−1) |
8 | A[i+1] = key | c 8 c_8 c8 | n -1 |
该算法的运行时间是执行每条语句的运行时间之和。需要执行 c i c_i ci步且执行 n n n次的一条语句将贡献 c i n c_i n cin给运行时间。计算具有n个输入的INSERTION-SORT的运行时间 T [ n ] T[n] T[n]为:
T [ n ] = c 1 n + c 2 ( n − 1 ) + c 4 ( n − 1 ) + c 5 ∑ j = 2 n t j + c 6 ∑ j = 2 n ( t j − 1 ) + c 7 ∑ j = 2 n ( t j − 1 ) + c 8 ( n − 1 ) T[n]=c_1n+c_2(n-1)+c_4(n-1)+c_5\sum_{j=2}^nt_j+c_6\sum_{j=2}^n(t_j-1)+c_7\sum_{j=2}^n(t_j-1)+c_8(n-1) T[n]=c1n+c2(n−1)+c4(n−1)+c5∑j=2ntj+c6∑j=2n(tj−1)+c7∑j=2n(tj−1)+c8(n−1)
依据输入规模n个数已被排序的程序,如输入数组已排序,则出现最佳情况。当 j = 2 , 3 , ⋯ , n j=2,3,\cdots,n j=2,3,⋯,n时,第5行,当 i = j − 1 时,都有 A [ i ] ≥ k e y ,此时 t j = 1 i = j - 1时,都有A[i]\ge key,此时t_j=1 i=j−1时,都有A[i]≥key,此时tj=1,则运行时间:
T [ n ] = c 1 n + c 2 ( n − 1 ) + c 4 ( n − 1 ) + c 5 ( n − 1 ) + + c 8 ( n − 1 ) = ( c 1 + c 2 + c 4 + c 5 + c 8 ) n − ( c 2 + c 4 + c 5 + c 8 ) T[n]=c_1n+c_2(n-1)+c_4(n-1)+c_5(n-1)++c_8(n-1)=(c_1+c_2+c_4+c_5+c_8)n-(c_2+c_4+c_5+c_8) T[n]=c1n+c2(n−1)+c4(n−1)+c5(n−1)++c8(n−1)=(c1+c2+c4+c5+c8)n−(c2+c4+c5+c8)
我们可以把运行时间表示为 a n + b , 常量 a 和 b 依赖于语句代价 c i an+b,常量a和b依赖于语句代价c_i an+b,常量a和b依赖于语句代价ci,它是n的线性函数。
若输入数组已反向排序,则导致最坏情况:
j
=
2
,
3
,
⋯
,
t
j
=
j
∑
j
=
2
n
j
=
n
(
n
+
1
)
2
−
1
∑
j
=
2
n
(
j
−
1
)
=
n
(
n
−
1
)
2
T
(
n
)
=
c
1
n
+
c
2
(
n
−
1
)
+
c
4
(
n
−
1
)
+
c
5
[
n
(
n
+
1
)
2
−
1
]
+
c
6
[
n
(
n
−
11
)
2
]
+
c
7
[
n
(
n
−
1
)
2
]
+
c
8
(
n
−
1
)
=
(
c
5
2
+
c
6
2
+
c
7
2
)
n
2
+
(
c
1
+
c
2
+
c
4
+
c
8
+
c
5
2
−
c
6
2
−
c
7
2
)
n
−
(
c
2
+
c
4
+
c
5
+
c
8
)
j=2,3,\cdots,t_j=j\\ \sum_{j=2}^nj=\frac{n(n+1)}{2}-1\\ \sum_{j=2}^n(j-1)=\frac{n(n-1)}{2}\\ T(n)=c_1n+c_2(n-1)+c_4(n-1)+c_5[\frac{n(n+1)}{2}-1]+c_6[\frac{n(n-11)}{2}]+c_7[\frac{n(n-1)}{2}]+c_8(n-1)\\ =(\frac{c_5}{2}+\frac{c_6}{2}+\frac{c_7}{2})n^2+(c_1+c_2+c_4+c_8+\frac{c_5}{2}-\frac{c_6}{2}-\frac{c_7}{2})n-(c_2+c_4+c_5+c_8)
j=2,3,⋯,tj=jj=2∑nj=2n(n+1)−1j=2∑n(j−1)=2n(n−1)T(n)=c1n+c2(n−1)+c4(n−1)+c5[2n(n+1)−1]+c6[2n(n−11)]+c7[2n(n−1)]+c8(n−1)=(2c5+2c6+2c7)n2+(c1+c2+c4+c8+2c5−2c6−2c7)n−(c2+c4+c5+c8)
我们可以把最坏情况运行时间表示为
a
n
2
+
b
n
+
c
,
其中常量
a
,
b
,
c
依赖于语句代价
c
i
an^2+bn+c,其中常量a,b,c依赖于语句代价c_i
an2+bn+c,其中常量a,b,c依赖于语句代价ci,因此它是n的二次函数。
3 最坏情况与平均情况分析
在本书余下部分,我们主要计算 最坏情况运行时间,即对规模为n的任何输入,算法的最长运行时间。理由如下:
- 一个算法的最坏情况运行时间给出了任何输入的运行时间的一个上界。
- 对某些算法,最坏情况经常出现。例如,检索数据库中一条不存在的信息时,需遍历全部数据。
- “平均情况”往往与最坏情况大致一样差
4 增长量级
我们使用某些简化的抽象来使过程INSERTION-SORT的分析更容易。
- 首先,通过使用常量 c i c_i ci表示这些代价来忽略每条语句的实际代价。
- 其次,注意到这些常量也提供了比我们真正需要的更多的细节:最坏情况运行时间表示为 a n 2 + b n + c , 其中常量 a , b , c 依赖于语句代价 c i an^2+bn+c,其中常量a,b,c依赖于语句代价c_i an2+bn+c,其中常量a,b,c依赖于语句代价ci.这样我们不但忽略了实际的预计代价,也忽略抽象的代价 c i c_i ci。
我们做出一种更简化的抽象:即我们真正感兴趣的运行时间的增长率或增长量级。所以我们只考虑公式中最重要的项(例如二次函数中的 a n 2 an^2 an2),因为当n的值很大时,低阶项相对来说不太重要。我们也可以忽略最重要项的常系数,因为对于大的输入,在确定计算效率时常量因子不如增长率重要。
对于插入排序,当我们忽略低阶项和最重要项的常系数时只剩下最重要项的因子 n 2 n^2 n2.我们记插入排序最坏情况运行时间 O ( n 2 ) O(n^2) O(n2)
结语
欢迎小伙伴一起学习交流,需要啥工具或者有啥问题随时联系我。
❓QQ:806797785
⭐️源代码地址:https://gitee.com/gaogzhen/algorithm
[1]算法导论(原书第三版)/(美)科尔曼(Cormen, T.H.)等著;殷建平等译 [M].北京:机械工业出版社,2013.1(2021.1重印).p13-16
[2]插入排序-排序-算法[CP/OL]