1 / 29

第四章 基本的算法策略

第四章 基本的算法策略. 4.1 迭代算法. 概念 用变量的旧值递推出新值的解决问题的方法 适合的范围 数值计算 类型 ( 1 )递推法 s n =s n-1 +A n ( 2 )倒推法. 4 . 1 . 1 递推 法. 【 例 1】 兔子繁殖问题 问题描述: 一对兔子从出生后第三个月开始,每月生一对小兔子。小兔子到第三个月又开始生下一代小兔子。假若兔子只生不死,一月份抱来一对刚出生的小兔子,问一年中每个月各有多少只兔子。 问题分析: 则繁殖过程如下: 一月 二月 三月 四月 五月 六月 ……

Download Presentation

第四章 基本的算法策略

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第四章 基本的算法策略

  2. 4.1 迭代算法 • 概念 用变量的旧值递推出新值的解决问题的方法 • 适合的范围 数值计算 • 类型 (1)递推法 sn=sn-1+An (2)倒推法

  3. 4.1.1 递推法 【例1】兔子繁殖问题 问题描述:一对兔子从出生后第三个月开始,每月生一对小兔子。小兔子到第三个月又开始生下一代小兔子。假若兔子只生不死,一月份抱来一对刚出生的小兔子,问一年中每个月各有多少只兔子。 问题分析:则繁殖过程如下: 一月 二月 三月 四月 五月 六月 …… 1 1 1+1=2 2+1=3 3+2=5 5+3=8 …… 数学建模:y1=y2=1,yn=yn-1+yn-2,n=3,4,5,……。

  4. 算法1: main( ) { int i,a=1,b=1; print(a,b); for(i=1;i<=10;i++) { c=a+b; print (c); a=b; b=c; } }

  5. 4.1.2 倒推法 所谓倒推法:是对某些特殊问题所采用的违反通常习惯的,从 后向前推解问题的方法。

  6. 【例】猴子吃桃问题 一只小猴子摘了若干桃子,每天吃现有桃的一半多一个, 到第10天时就只有一个桃子了,求原有多少个桃? 数学模型:每天的桃子数为:a10=1, a9=(1+a10)*2, a8=(1+a9)*2,……a10=1, 递推公式为:ai=(1+ai+1)*2 I = 9,8,7,6……1 算法如下 : main( ) { int i,s; s=1; for (i=9 ;i>=1;i=i-1) s=(s+1)*2 print (s); }

  7. 4.2 蛮力法 • 蛮力法是基于计算机运算速度快这一特性,在解决问题时采取的一种“懒惰”的策略。这种策略不经过(或者说是经过很少的)思考,把问题的所有情况或所有过程交给计算机去一一尝试,从中找出问题的解。 • 应用 • 蛮力策略的应用很广,具体表现形式各异,数据结构课程中学习的:选择排序、冒泡排序、插入排序、顺序查找、朴素的字符串匹配等,都是蛮力策略具体应用。

  8. 4.2.1 枚举法 枚举(enumerate)法(穷举法)是蛮力策略的一种表现形式,也是一种使用非常普遍的思维方法。它是根据问题中的条件将可能的情况一一列举出来,逐一尝试从中找出满足问题条件的解。但有时一一列举出的情况数目很大,如果超过了我们所能忍受的范围,则需要进一步考虑,排除一些明显不合理的情况,尽可能减少问题可能解的列举数目。 用枚举法解决问题,通常可以从两个方面进行算法设计: 1)找出枚举范围:分析问题所涉及的各种情况。 2)找出约束条件:分析问题的解需要满足的条件,并用逻辑表达式表示。

  9. 【例】解数字迷: A B C A B × A D D D D D D 算法设计1:按乘法枚举 1)枚举范围为: A:3——9(A=1,2时积不会得到六位数),B:0——9, C:0——9 六位数表示为A*10000+B*1000+C*100+A*10+B, 共尝试800次。 2)约束条件为: 每次尝试,先求5位数与A的积,再测试积的各位是否相 同,若相同则找到了问题的解。 测试积的各位是否相同比较简单的方法是,从低位开始, 每次都取数据的个位,然后整除10,使高位的数字不断变 成个位,并逐一比较。

  10. 算法1如下: main( ) { int A,B,C,D,E,E1,F,G1,G2,i; for(A=3; A<=9; A++) for(B=0; B<=9; B++) for(C=0; C<=9; C++) { F=A*10000+B*1000+C*100+A*10+B; E=F*A; E1=E; G1=E1 mod 10; for(i=1; i<=5; i++) { G2=G1; E1=E1/10; G1= E1 mod 10; if(G1<>G2 ) break; } if(i=6) print( F,”*”,A,”=”,E); } }

  11. 4.3 分而治之算法

  12. 4.3.1 分治算法框架 1.算法设计思想 分治法求解问题的过程是,将整个问题分解成若干个小问题后分而治之。如果分解得到的子问题相对来说还太大,则可反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生出方便求解的子问题,必要时逐步合并这些子问题的解,从而得到问题的解。 分治法的基本步骤在每一层递归上都有三个步骤: 1)分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;2)解决:若子问题规模较小而容易被解决则直接解,否则再继续分解为更小的子问题,直到容易解决;3)合并:将已求解的各个子问题的解,逐步合并为原问题的解。

  13. 说明: 有时问题分解后,不必求解所有的子问题,也就不必作第三步的操作。比如折半查找,在判别出问题的解在某一个子问题中后,其它的子问题就不必求解了,问题的解就是最后(最小)的子问题的解。分治法的这类应用,又称为“减治法”。 多数问题需要所有子问题的解,并由子问题的解,使用恰当的方法合并成为整个问题的解,比如合并排序,就是不断将子问题中已排好序的解合并成较大规模的有序子集。

  14. 2.适合用分治法策略的问题 当求解一个输入规模为n且取值又相当大的问题时,用蛮力策略效率一般得不到保证。若问题能满足以下几个条件,就能用分治法来提高解决问题的效率。 1)能将这n个数据分解成k个不同子集合,且得到k个子集合是可以独立求解的子问题,其中1<k≤n; 2)分解所得到的子问题与原问题具有相似的结构,便于利用递归或循环机制; 在求出这些子问题的解之后,就可以推解出原问题的解;

  15. 4.3.2 二分法 不同于现实中对问题(或工作)的分解,可能会考虑问题(或工作)的重点、难点、承担人员的能力等来进行问题的分解和分配。在算法设计中每次一个问题分解成的子问题个数一般是固定的,每个子问题的规模也是平均分配的。当每次都将问题分解为原问题规模的一半时,称为二分法。二分法是分治法较常用的分解策略,数据结构课程中的折半查找、归并排序等算法都是采用此策略实现的。

  16. 分治法的一般的算法设计模式如下: Divide-and-Conquer(int n) /n为问题规模/ { if (n≤n0) /n0 为可解子问题的规模/ { 解子问题; return(子问题的解);} for (i=1 ;i<=k;i++) /分解为较小子问题p1,p2,……pk/ yi=Divide-and-Conquer(|Pi|); /递归解决Pi/ T =MERGE(y1,y2,...,yk); /合并子问题/ return(T); }

  17. 【例1】金块问题: 老板有一袋金块(共n块),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块。假设有一台比较重量的仪器,我们希望用最少的比较次数找出最重的金块。

  18. 算法设计1:  比较简单的方法是逐个的进行比较查找。先拿两块比较重量,留下重的一个与下一块比较,直到全部比较完毕,就找到了最重的金子。算法类似于一趟选择排序, 算法如下: maxmin( float a[],int n) { max==min=a[1]; for(i=2; i<=n ;i++ ) if(max < a[i]) max=a[i]; else if(min > a[i]) min=a[i]; }

  19. 算法分析1: 算法中需要n-1次比较得到max。最好的情况是金块是由小到大取出的,不需要进行与min的比较,共进行n-1次比较。最坏的情况是金块是由大到小取出的,需要再经过n-1次比较得到min

  20. 算法设计2:   在含n(n是2的幂(n>=2))个元素的集合中寻找极大元和极小元。用分治法(二分法): 1)  将数据等分为两组(两组数据可能差1),   2)  递归分解直到每组元素的个数≤2,可简单地找   到最大(小)值。 3)  回溯时将分解的两组解大者取大,小者取小,   合并为当前问题的解。

  21. 算法2递归求取最大和最小元素 float a[n]; maxmin(int i, int j ,float &fmax, float &fmin){int mid; float lmax, lmin, rmax, rmin; if (i=j) {fmax= a[i]; fmin=a[i];} else if (i=j-1) if(a[i]<a[j]) { fmax=a[j];fmin=a[i];} else {fmax=a[i]; fmin=a[j];} else {mid=(i+j)/2;maxmin(i,mid,lmax,lmin);maxmin(mid+1,j,rmax,rmin);if(lmax>rmax) fmax=lmax; else fmax=rmax;if(lmin>rmin) fmin=rmin; else fmin=lmin;}

  22. 【例】大整数乘法 在某些情况下,我们需要处理很大的整数,它无法在计算机硬件能直接允许的范围内进行表示和处理。若用浮点数来存储它,只能近似地参与计算,计算结果的有效数字会受到限制。若要精确地表示大整数,并在计算结果中要求精确地得到所有位数上的数字,就必须用软件的方法来实现大整数的算术运算。请设计一个有效的算法,可以进行两个n位大整数的乘法运算。

  23. 图4-10 大整数X和Y的分段 算法设计:设X和Y都是n位的二进制整数,现在要计算它们的乘积X*Y。

  24. 按照乘法分配律,分解后的计算过程如下: 记:X=A*2n/2+B ,Y=C*2n/2+D。这样,X和Y的乘积为: X*Y=(A*2n/2+B)(C*2n/2+D)=A*C*2n+(AD+CB)*2n/2+B*D    (1)

  25. T(1)=1 T(n)=4T(n/2) (2) 由此递归式迭代过程如下: T(n)=4T(n/2) =4(4T(n/4))=4*4T(n/4) =16*4T(n/8) =…… =O(4k)= O(nlog4) =O(n2) 所以可得算法的时间复杂度为T(n)=O(n2)。

  26. 模型改进: 可以把X*Y写成另一种形式: X*Y=A*C*2n+[(A-B)(D-C)+AC+BD]*2n/2+B*D (3)

  27. 式(3)看起来比式(1)复杂,但它仅需做3次n/2位整数的乘法:AC,BD和(A-B)(D-C),6次加、减法和2次移位。由此可得:式(3)看起来比式(1)复杂,但它仅需做3次n/2位整数的乘法:AC,BD和(A-B)(D-C),6次加、减法和2次移位。由此可得: (4) 用解递归方程的迭代公式法,不妨设n=2k: T(n)=3T(n/2) =3(3T(n/4)) =9(T(n/8) =…… =3k +3k-1 *2c+3k-2 *4c+……+3c2k-1+c2k = O(nlog3) 则得到T(n)=O(nlog3)=O(n1.59)。

  28. 4.3.4 其它分治方法

  29. 【例】选择问题: 对于给定的n 个元素的数组a[0:n-1],要求从中找出第k小的元素。 问题分析:选择问题的一个应用就是寻找中值元素,此时k=[n/2]。 快速排序算法来解决选择问题,一趟排序分解出的左子集中元素个数nleft,可能是以下几种情况: 1)nleft=k-1,则分界数据就是选择问题的答案。 2)nleft>k-1,则选择问题的答案继续在左子集中找,问 题规模变小了。 3)nleft<k-1,则选择问题的答案继续在右子集中找,问 题变为选择第k-nleft-1小的数,问题的规模也变小了。

More Related