算法分析与设计(一):概论
学习目标
- 现实:存在诸多问题,求同一问题往往存在多种方法,但每种方法的效率不同。
- 算法分析:因此我们就需要比较各种算法的运行效率,从中选出“更适”算法。此过程称为“算法分析”。
- 算法设计:掌握算法设计方法论及一些通用技巧,来设计出更正确高效的算法。
- 学习目标:①设计高效算法 ②分析算法性能 ③二者之间的交互验证(设计高效算法的同时要兼顾分析此算法的性能,循环进行迭代设计。)
算法的历史
-
公元前300年,古希腊数学家欧几里得提出辗转相除法。
-
公元前1世纪,中国成书《周髀算经》,介绍了勾股定理、确定天文历法的可行方法等。
-
公元3世纪中期,魏晋时期的中国数学家刘徽首创割圆术,为计算圆周率建立了严密的理论和完善的算法。
-
公元7世纪,唐高宗李治组织学者收录汉唐十部著作《周髀算经》、《九章算术》、《海岛算经》、《张丘建算经》、《夏侯阳算经》、《五经算术》、《缉古算经》、《缀术》、《五曹算经》、《孙子算经》,成书《算经十书》,用以作国家算术教科书。
-
公元9世纪,波斯著名数学家、天文学家、地理学家花拉子密成书《印度数学算书》,译成拉丁语“Algoritmi de numero
Indorum”,花拉子密的拉丁文音译即为“算法”(Algorithm)一词的由来。
-
20世纪30年代至40年代,艾伦·图灵与冯·诺依曼将算法与计算机进行结合。
-
1936年,艾伦·图灵提出图灵机,通过建立通用计算机模型,刻画计算机的计算行为。
-
1946年,冯·诺依曼提出存储程序原理。
-
图灵奖(计算机界的“诺贝尔奖”): 1966年由计算机协会(ACM)设立,奖励对计算机事业做出突出贡献的个人。其中算法与计算复杂性领域图灵奖得主占比很大,说明算法在目前的计算机界中的地位举足轻重。
-
算法的定义
计算问题
-
定义:给定数据输入,计算满足某种性质输出的问题。
-
图示
-
示例:排序问题
算法
-
定义:给定计算问题,算法是一系列良定义的计算步骤,逐一执行计算步骤即可得预期的输出。
- 换言之算法是步骤。目前是计算机可实现的步骤。
- 其中良定义是指“明确无歧义”的定义。
- 其中计算步骤是指“计算机可实现的指令”。
-
图示
-
示例:排序问题
-
示例1:插入排序算法
- 算法思想:将数组待排序元素依次插入到已排序部分,使已排序部分保持升序的性质。
- 算法思想:将数组待排序元素依次插入到已排序部分,使已排序部分保持升序的性质。
······
-
示例2:选择排序
- 算法思想
- 算法思想
······
······
算法的性质
输入
- 含义:算法接受零个或多个输入(可能是空的),这些输入是问题的实例。输入用来定义算法需要解决的具体问题。
输出
- 含义:算法产生一个或多个输出,这些输出是对问题的解决方案。输出可以是一个值、一个数据结构、一组数据或其他形式,具体取决于问题的性质。
有穷性
- 含义:算法必须在有限个计算步骤后终止。
- 反例算法思想:给定输入数组,不断交换首尾元素的位置。
- 原因:不断交换意味着“动作序列没有终结”。
确定性
- 含义:算法必须是没有歧义的。
- 反例:对于给定输入数组,交换两个数的位置。
- 原因:没有具体指明是哪两个数?
可行性
- 含义:可以机械地一步一步执行基本操作步骤。
- 反例:将大元素放数组后部,小元素放数组前部。
- 原因:多大算大?多小算小?前、后部具体是哪里?说法含糊,不可执行。
算法的表示
如何表示一个算法?
-
两个角度:人类和机器。
-
自然语言
- 优点
- 贴近人类思维,易于理解主旨
- 缺点
- 语言描述繁琐,容易产生歧义
- 使用了一些不严谨的描述
- 示例(选择排序)
- 优点
-
编程语言
- 优点
- 精准表达逻辑,规避表述歧义
- 缺点
- 不同编程语言间语法存在差异
- 过于关注算法实现的细枝末节
- 示例(选择排序)
- 优点
-
伪代码
- 非正式语言
- 移植编程语言书写形式作为基础和框架
- 按照接近自然语言的形式表达算法过程
- 兼顾自然语言与编程语言优势
- 简洁表达算法本质,不拘泥于实现细节
- 准确反映算法过程,不产生矛盾和歧义
- 示例(选择排序)
- 非正式语言
算法的表示方式比较
表示方式 | 语言特点 |
---|---|
自然语言 | 贴近人类思维,易于理解主旨。表述不够精准,存在模糊歧义。 |
编程语言 | 精准表达逻辑,规避表述歧义。受限语法细节,增大理解难度。 |
伪代码 | 关注算法本质,便于书写阅读。 |
算法的分析
影响算法运行时间的因素
算法分析;换言之,如何对一个算法进行分析?;亦如是,如何比较不同算法性能?
答案就是分析算法的运行时间(又称算法的时间复杂度)。
- 影响算法运行时间的因素
-
机器运算速度
-
原因:由于各机器的性能不同,相同算法运行在不同机器上,其运行时间是不同的;不同算法运行在不同机器上,其运行时间是无法公平比较的。
-
示例:两种算法的运行时间无法进行公平比较。
机器 运算速度 运行算法 天河三号 百亿亿次/秒 插入排序 个人电脑 十亿次/秒 选择排序 -
结论:因此算法分析的第一个原则就是统一机器性能,排除掉机器带来的影响。
-
做法(如何统一机器性能?)
①归纳基本操作:如运算、赋值、比较等。
②确定基本操作代价:假设基本操作代价均为1。
-
-
运行的算法实例
- 原因:统一机器性能后,算法运行时间依赖于问题输入规模与运行的算法实例。而对于相同问题规模大小的同一算法来说,运行的实例不同,算法运行的时间是不同的。
- 示例:对于同一算法(相同输入规模):插入排序,实例影响运行。
①运行排序最好情况的实例:数组按升序排好
②运行排序最坏情况的实例:数组按降序排好
- 分析
输入情况 情况说明 最好情况 不常出现,不具普遍性。 最坏情况 确定上界,更具一般性。 一般情况 情况复杂,分析难度大。
- 结论:因此算法分析的第二个原则就是分析最坏情况,确定好问题实例的上界,作最坏的打算,则万无一失。
-
问题输入规模n
-
原因:统一好机器性能并用最坏的实例情况进行分析后,算法运行的时间仅依赖于问题输入规模𝒏,表示为𝑻(𝒏)。因此如何用n表示𝑻(𝒏)便是算法分析的关键一环。
-
表示𝑻(𝒏)步骤:
①计算每行代码操作次数,并将其加和。
②利用渐进分析简化𝑻(𝒏)表达式。其中渐近分析是指忽略𝑻(𝒏)的系数与低阶项,仅关注高阶项,用渐近记号(θ/O/Ω)表示。之所以可以仅关注表达式的高阶项,是因为在𝒏充分大时,表达式函数的高阶项可以表达整个函数的大致趋势。 -
示例(插入排序/选择排序)
①计算每行代码操作次数,并将其加和。
②利用渐进分析简化𝑻(𝒏)表达式。
-
关于渐进记号
渐进记号 名称 示例 θ 渐近紧确界 𝑻(𝒏) = 𝚯(𝒈(𝒏)) O 渐近上界 𝑻(𝒏)= 𝑶(𝒈(𝒏)) Ω 渐近下界 𝑻(𝒏) = 𝛀(𝒈(𝒏)) ①关于θ
(1)定义:对于给定的函数𝒈(𝒏),𝚯(𝒈(𝒏))表示以下函数的集合: 𝚯(𝒈(𝒏)) = {𝑻(𝒏) : ∃ 𝒄𝟏, 𝒄𝟐, 𝒏₀ > 𝟎, 使得∀ 𝒏 ≥ 𝒏₀, 𝒄𝟏𝒈(𝒏) ≤𝑻(𝒏) ≤ 𝒄𝟐𝒈(𝒏)}
(2)含义:这个定义表明,集合𝚯(𝒈(𝒏)) 包含了满足定义中条件的函数𝑻(𝒏)。这些函数在自变量𝒏足够大(大于或等于𝒏₀)时,都与函数𝒈(𝒏) 有类似的增长趋势,其中𝒄₁和𝒄₂是用于控制函数增长的正常数,𝒏₀是一个足够大的值,超过这个值后条件始终成立。
(3)示例
②关于O
(1)定义:对于给定的函数𝒈(𝒏),𝑶(𝒈(𝒏))表示以下函数的集合:𝑶(𝒈(𝒏)) = {𝑻(𝒏) : ∃ 𝒄, 𝒏₀ > 𝟎, 使得∀ 𝒏 ≥ 𝒏₀, 𝟎 ≤ 𝑻(𝒏) ≤ 𝒄𝒈(𝒏) }
(2)含义:这个定义表明,集合𝑶(𝒈(𝒏)) 包含了满足上述条件的函数𝑻(𝒏)。这些函数在自变量𝒏足够大(大于或等于𝒏₀)时,都有类似于函数𝒈(𝒏)的增长趋势,其中𝒄是用于控制函数增长的正常数,𝒏₀是一个足够大的值,超过这个值后条件始终成立。
(3)示例
①关于Ω
(1)定义:对于给定的函数𝒈(𝒏),𝛀(𝒈(𝒏))表示以下函数的集合:𝛀(𝒈(𝒏)) = {𝑻(𝒏) : ∃ 𝒄, 𝒏₀ > 𝟎, 使得∀ 𝒏 ≥ 𝒏₀, 𝟎 ≤ 𝒄𝒈(𝒏) ≤ 𝑻(𝒏) }
(2)含义:这个定义表明,集合𝛀(𝒈(𝒏)) 包含了满足上述条件的函数𝑻(𝒏)。这些函数在自变量𝒏足够大(大于或等于𝒏₀)时,都有类似于函数𝒈(𝒏)的增长趋势,其中𝒄是用于控制函数增长的正常数,𝒏₀是一个足够大的值,超过这个值后条件始终成立。
(3)示例
-
渐进记号采用O表示
由于算法的示例选用最坏的情况考虑更具一般性,并且渐进记号O表示渐进上界。因此渐进记号采用O表示即代表着T(n)选用最坏的情况考虑 ,又是与算法示例选用最坏情况度量的一种呼应。
-
-
算法分析的原则
综上算法分析有三个原则
-
统一机器性能
-
分析最坏情况
-
采用渐进分析(渐进记号选用O)