算法设计与分析2

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

2.1.4
非递归算法的时间复杂性分析
从算法是否递归调用的角度来说, 可以将算法分为非递归算法和递归算法。 对非递归算法时间复杂性的分析,关键是建立一个代表算法运行时间的求和表 达式,然后用渐进符号表示这个求和表达式。 例 2.6 分析例 2.2 中起泡排序算法的时间复杂性。 解: 起泡排序算法的基本语句是比较操作, 其执行次数取决于排序的趟数。 最好情况下,待排序记录序列为升序,算法只执行一趟,进行了 n-1 次比较, 时间复杂性为 O(n)。最坏情况下,待排序记录序列为降序,每趟排序在无序序 列中只有一个最大的记录被交换到最终位置,算法执行 n-1 趟,第 i(1≤i<n)
图 2.1 大 O 符号的含义
需要强调的是,定义 2.1 表明对于函数 f(n)来说,可能存在多个函数 T(n), 使得 T(n)=O(f(n)),换言之,O(f(n))实际上是一个函数集合,这个函数集合具 有同样的增长趋势,T(n)只是这个集合中的一个函数。而且定义 2.1 给了很大 的自由度来选择常量 c 和 n0 的特定值,例如,下列推导都是合理的: 100n+5≤100n+n=101n=O(n) (c=101,n0=5) 100n+5≤100n+5n=105n=O(n) (c=105,n0=1) 例 2.4 分析例 2.3 中合并算法的时间复杂性。 解:假设在退出第 1 个循环后 i 的值为 n,j 的值为 m',说明序列 A 处理 完毕,第 2 个循环将不执行,则第 1 个循环的时间复杂性为 O(n+m'),第 3 个 循 环 的 时 间 复 杂 性 为 O(m-m') , 因 此 , 算 法 的 时 间 复 杂 性 为 O(n+m'+m-m')=O(n+m);假设在退出第 1 个循环后 j 的值为 m,i 的值为 n',说 明序列 B 处理完毕,第 3 个循环将不执行,则第 1 个循环的时间复杂性为 O(n'+m),第 2 个循环的时间复杂性为 O(n-n'),因此,算法的时间复杂性为 O(n'+m+n-n')=O(n+m)。综上,三个循环将序列 A 和 B 分别扫描一遍,算法的 时间复杂性为 O(n+m)。
1
算法设计与分析教师教案
记录个数 n 和待查值 k 在数组中的位置,每执行一次 for 循环,都要执行一次 元素比较操作。因此,输入规模是待查找的记录个数 n,基本语句是比较操作 (A[i] == k) 。 例 2.2 对如下起泡排序算法,请找出输入规模和基本语句。 void BubbleSort(int r[ ], int n) { int bound, exchange = n - 1; //第一趟起泡排序的区间是[0, n-1] while (exchange != 0) //当上一趟排序有记录交换时 { bound = exchange; exchange = 0; for (int j = 0; j < bound; j++) //一趟起泡排序区间是[0, bound] if (r[j] > r[j + 1]) { int temp = r[j]; r[j] = r[j + 1]; r[j + 1] = temp; //交换记录 exchange = j; //记载每一次记录交换的位置 } } } 解:算法由两层嵌套的循环组成,内层循环的执行次数取决于每一趟待排 序区间的长度,也就是待排序记录个数,外层循环的终止条件是在一趟排序过 程中没有交换记录的操作, 是否有交换记录的操作取决于相邻两个元素的比较 结果,也就是说,每执行一次 for 循环,都要执行一次比较操作,而交换记录 的操作却不一定执行。因此,输入规模是待排序的记录个数 n,基本语句是比 较操作(r[j] > r[j + 1])。 例 2.3 如下算法实现将两个升序序列合并成一个升序序列,请找出输入 规模和基本语句。 void Union(int A[ ], int n, int B[ ], int m, int C[ ] ) //合并 A[n]和 B[m] { int i = 0, j = 0, k = 0; while (i < n && j < m) { if (A[i] <= B[j]) C[k++] = A[i++]; //A[i]与 B[j]中较小者存入 C[k] else C[k++] = B[j++]; } while (i < n) C[k++] = A[i++]; //收尾处理,序列 A 中还有剩余记录 while (j < m) C[k++] = B[j++]; //收尾处理,序列 B 中还有剩余记录 } 解: 算法由三个并列的循环组成, 三个循环将序列 A 和 B 扫描一遍, 因此, 输入规模是两个序列的长度 n 和 m。第 1 个循环根据比较结果决定执行两个赋 值语句中的一个,因此,可以将比较操作(A[i] <= B[j])作为基本语句,第 2 个循环的基本语句是赋值操作(C[k++] = A[i++]),第 3 个循环的基本语句是 赋值操作(C[k++] = B[j++])。
2
算法设计与分析教师教案
大 O 符号用来描述增长率的上限, 表示 T(n)的增长最多像 f(n)增长的那样 快,这个上限的阶越低,结果就越有价值。大 O 符号的含义如图 2.1 所示,为 了说明这个定义,将问题的输入规模 n 扩展为实数。
执 行 次 数 c×f(n) T (n ) n0 之前 的情况 不重要 n0 问题规模

2.1 算法的时间复杂性分析
算法的时间复杂性分析是一种事前分析估算的方法, 它是对算法所消耗资 源的一种渐进分析方法。 所谓渐进分析是指忽略具体机器、编程语言和编译器的影响,只关注在输 入规模增大时算法运行时间的增长趋势。 渐进分析的好处是大大降低了算法分析的难度, 是从数量级的角度评价算 法的效率。
算法设计与分析教师教案
第2章
教学重点 教学难点
算法分析基础
大 O 符号;非递归算法的时间复杂性分析;最优算法 问题的时间复杂性下界 教学要求 知识点 了解 理解 掌握 输入规模与基本语句 √ 大 O 符号 最好、最坏和平均情况 √ √
熟练掌握
教学内容 和 教学目标
非递归算法的时间复杂性分析 递归算法的时间复杂性分析 算法的空间复杂性分析 问题的时间复杂性下界 最优算法 平凡下界 判定树模型 √ √ √ √ √ √
(n i) 趟排序执行了 n-i 次比较,则记录的比较次数为
i 1 n 1
n(n 1) ,时间复杂 2
性为 O(n2)。 平均情况需要考虑初始序列中逆序的个数。 设 a1, a2,„, an 是集合{1, 2, „, n}的一个排列,如果 i<j 且 ai>aj,则序偶(ai, aj)称为该排列的一个逆序(inverse order)。例如,2, 3, 1 有两个逆序:(3, 1)和(2, 1)。为了确定相邻的两个记录是 否需要交换,必须对这两个记录进行比较,因此,初始序列中逆序的个数,也 就是记录比较次数的下界。n 个记录共有 n!种排列,所有排列中逆序的平均个 数,就是算法所需平均比较次数的下界。 例如, 集合{1, 2, 3}有 3!=6 种排列: 123(0), 132(1), 213(1), 231(2), 312(2), 321(3),括号中是每种排列的逆序个数。令 S(k)表示逆序个数为 k 的排列数目, 则有:S(0)=1,S(1)=2,S(2)=2,S(3)=1。令 mean(n)表示 n 个元素的所有排列 中逆序的平均个数,则
2.1.2
算法的渐进分析
算法的渐进分析不是从时间量上度量算法的运行效率, 而是度量算法运行 时间的增长趋势。换言之,只考察当输入规模充分大时,算法中基本语句的执 行次数在渐近意义下的阶,通常使用大 O(读做大欧)符号表示。 定义 2.1 若存Hale Waihona Puke Baidu两个正的常数 c 和 n0, 对于任意 n≥n0, 都有 T(n)≤c× f(n), 则称 T(n)=O(f(n))(或称算法在 O(f(n))中)。
2.1.1
输入规模与基本语句
输入规模是指输入量的多少,一般来说,它可以从问题描述中得到。 一个事实是:几乎所有的算法,对于规模更大的输入都需要运行更长的时 间。 运行算法所需要的时间 T 是输入规模 n 的函数,记作 T(n)。 要精确地表示算法的运行时间函数常常是很困难的,即使能够给出,也可 能是个相当复杂的函数,函数的求解本身也是相当复杂的。 考虑到算法分析的主要目的在于比较求解同一个问题的不同算法的效率, 为了客观地反映一个算法的运行时间, 可以用算法中基本语句的执行次数来度 量算法的工作量。 基本语句是执行次数与整个算法的执行次数成正比的语句, 基本语句对算 法运行时间的贡献最大,是算法中最重要的操作。 例 2.1 对如下顺序查找算法,请找出输入规模和基本语句。 int SeqSearch(int A[ ], int n, int k) //在数组 A[n]中查找值为 k 的记录 { for (int i = 0; i < n; i++) if (A[i] == k) break; if (i == n) return 0; //查找失败,返回失败的标志 0 else return (i + 1); //查找成功,返回记录的序号 } 解:算法的运行时间主要耗费在循环语句,循环的执行次数取决于待查找
mean(n)
n k 1 1 n( n1)/2 1 n(n 1) s (k ) k n ! k 0 4 k 1 2
因此,平均情况下,起泡排序的时间复杂性是 O(n2),与最坏情况同数量 级。 下面给出非递归算法分析的一般步骤: (1)确定问题的输入规模。在大多数情况下,输入规模是很容易确定的, 可以从问题的描述中得到。 (2)找出算法中的基本语句。算法中执行次数最多的语句就是基本语句, 通常是最内层循环的循环体。 (3)检查基本语句的执行次数是否只依赖于输入规模。如果基本语句的执行 次数还依赖于其他一些特性(如数据的初始分布),则需要分别研究最好
2.1.3
最好、最坏和平均情况
有些算法的时间代价只依赖于问题的输入规模,而与输入的具体数据无 关。例如,例 2.3 的合并算法对于任意两个有序序列,算法的时间复杂性都是 O(n+m)。但是,对于某些算法,即使输入规模相同,如果输入数据不同,其 时间代价也不相同。 例 2.5 分析例 2.1 中顺序查找算法的时间复杂性。 解:顺序查找从第一个元素开始,依次比较每一个元素,直至找到 k,而 一旦找到了 k,算法也就结束了。 1.数组的第一个元素恰好就是 k,算法只要比较一个元素就行了,这是最 好情况,时间复杂性为O(1); 2.数组的最后一个元素是 k,算法就要比较 n 个元素,这是最坏情况,时 间复杂性为 O(n); 3.在数组中查找不同的元素,假设数据是等概率分布的,则 ,即平均要比较大约一半的元素,这是平均情况, 时间复杂性和最坏情况同数量级。 一般来说,最好情况不能作为算法性能的代表,因为发生的概率太小,对 于条件的考虑太乐观了。但是,当最好情况出现的概率较大的时候,应该分析
3
算法设计与分析教师教案
最好情况。 分析最坏情况有一个好处:可以知道算法的运行时间最坏能坏到什么程 度,这一点在实时系统中尤其重要。 通常需要分析平均情况的时间代价,特别是算法要处理不同的输入时,但 它要求已知输入数据是如何分布的,也就是考虑各种情况发生的概率,然后根 据这些概率计算出算法效率的期望值(这里指的是加权平均值),因此,平均 情况分析比最坏情况分析更困难。通常假设是等概率分布,这也是在没有其他 额外信息时能够进行的唯一可能假设。
mean(3) 1 [ s(0) 0 s(1) 1 s(2) 2 s(3) 3] 1.5 3!
对于 n 个记录的所有初始排列,最好情况下,逆序的个数是 0,最坏情况 下, 逆序的个数是 n(n-1)/2, 其余所有排列, 逆序的个数在这两者之间。 Donald Kunth 对逆序的分布规律进行了研究,得出了下面的式子:
相关文档
最新文档