算法设计及实验报告.docx
- 文档编号:11182309
- 上传时间:2023-02-25
- 格式:DOCX
- 页数:22
- 大小:23.54KB
算法设计及实验报告.docx
《算法设计及实验报告.docx》由会员分享,可在线阅读,更多相关《算法设计及实验报告.docx(22页珍藏版)》请在冰豆网上搜索。
算法设计及实验报告
算法设计及实验报告
实验报告1递归算法
一、实验目的
掌握递归算法的基本思想;
掌握该算法的时间复杂度分析;
二、实验环境
电脑一台,TurboC运行环境
三、实验内容、步骤和结果分析
以下是四个递归算法的应用例子:
用C语言实现
1.阶乘:
main()
{inti,k;
scanf("%d\n",&i);
k=factorial(i);
printf("%d\n",k);
}
intfactorial(intn)
{ints;
if(n==0)s=1;
elses=n*factorial(n-1);//执行n-1次
returns;
}
阶乘的递归式很快,是个线性时间,因此在最坏情况下时间复杂度为O(n)。
2.Fibonacci数列:
main()
{inti,m;
scanf("%d\n",&i);
m=fb(i);
printf("%d",m);
}
intfb(intn)
{ints;
if(n<=1)return1;
elses=fb(n-1)+fb(n-2);
returns;
}
Fibonacci数列则是T(n)=T(n-1)+T(n-2)+O
(1)的操作,也就是T(n)=2T(n)+O
(1),由递归方程式可以知道他的时间复杂度T(n)是O(2n),该数列的规律就是不停的赋值,使用的内存空间也随着函数调用栈的增长而增长。
3.二分查找(分治法)
#include
#defineconst8
main()
{inta[]={0,1,2,3,4,5,6,7,8,9};
intn=sizeof(a);
ints;
s=BinSearch(a,const,n);
printf("suochadeshushidi%dge",s);
}
BinSearch(inta[],intx,intn)
{intleft,right,middle=0;
left=0;right=n-1;
whlie(left<=right)
{middle=(left+right)/2;
if(x==a[middle])returnmiddle;
if(x>a[middle])left=middle+1;
elseright=middle-1;
}
return-1;
}
二分搜索算法利用了元素间的次序关系,采用分治策略,由上程序可知,每执行一次while循环,数组大小减少一半,因此在最坏情况下,while循环被执行了O(logn)次。
而循环体内部只需运算O
(1)的时间,因此该算法在最坏情况下的时间复杂度为O(logn+1),即O(logn)。
4.合并排序(分治法)
MergeSort(intlow,inthigh,int*array)
{
intmiddle=(high+low)/2;//将数组划分为2分
if(low { MergeSort(low,middle,array);//对前一部分进行递归处理 MergeSort(middle+1,high,array);//对后一部分进行递归处理 HeBing(low,middle,middle+1,high,array);//将排序后的,前后两部分,进行合并 } returntrue; } HeBing(intlow1,inthigh1,intlow2,inthigh2,int*array) { int*temp, i=low1, j=low2, k=0; temp=(int*)malloc((high2-low1+1)*sizeof(int));//temp用于临时存储合并后的数组 while(i<=high1&&j<=high2)//对两个有序数组进行合并 { if(array[i] { temp[k++]=array[i]; i++; } else { temp[k++]=array[j]; j++; } } if(i<=high1) { while(i<=high1) temp[k++]=array[i++]; } else { while(j<=high2) temp[k++]=array[j++]; } for(i=low1,j=0;i<=high2;i++,j++) //将合并后的数组,复制到array中 { array[i]=temp[j]; } free(temp); returntrue; } 这是一种分治算法。 是先进行长度为1的排序,再合并成长度为2的排序,递归直到长度为n。 而快速排序是先进行划分,然后排序,不需要额为的空间,合并排序需要N的额外空间。 是一种稳定的排序。 将数组划分为小数组,通过局部的有序合并,解决问题。 算法平均时间复杂度: O(nlogn) 递归算法的时间复杂度求法: 用通用公式: T(n)=f[T(n-1)] f(x)是递归函数的时间复查性函数! 如下面这个简单递归是: void Digui() { 1-----;/*假设该语句的复查性是a*/ 2----------;/*假设该语句的复查性是b*/ for()/*循环M次*/ {-----;/*假设该语句的复查性是c*/ Digui(); } 由上分析,它的时间复杂性计算公式是: T(n)=a+b+M*T(n-1); 另外,求算法的运行时间,可通过上一个实验那种方法,嵌入那个求时间的算法即可求出运行时间! 四.总结 通过本次实验,使我了解了递归算法的思想,以及掌握对该算法复杂度的分析。 对递归算法的几个典型实例算法进行了程序语言实现,使我更加掌握了递归算法的内涵,同时也初步掌握了分治法的基本思想。 相比与第一次实验,对算法的分析方面,显得更加深入理解! 往后的学习中再强化深入。 实验报告2动态规划 一、实验目的 掌握动态规划算法的基本思想和要素; 掌握该算法的的设计步骤; 二、实验环境 电脑一台,VC++运行环境 三、实验内容、步骤和结果分析 以下通过动态规划的两个实例应用算法来加深对动态规划算法的理解: 自身编程能力有限,此实现程序引用网上资料. 1.矩阵连乘问题 给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…,n-1。 考察这n个矩阵的连乘积A1A2…An。 由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序,这种计算次序可以用加括号的方式来确定。 若一个矩阵连乘积的计算次序完全确定,则可以依此次序反复调用2个矩阵相乘的标准算法(有改进的方法,这里不考虑)计算出矩阵连乘积。 若A是一个p×q矩阵,B是一个q×r矩阵,则计算其乘积C=AB的标准算法中,需要进行pqr次数乘。 下面使用动态规划法找出矩阵连乘积的最优计算次序。 voidMatrixChain(int*p,intn,int**m,int**s) {for(inti=1;i<=n;i++) m[i][i]=0;//单个矩阵相乘,所需数乘次数 //以下两个循环是关键之一,以个数组为例(为描述方便,m[i][j]用ij代替) //需按照如下次序计算 //0112233445 //02132435 //031425 //0415 //05 //下面行的计算结果将会直接用到上面的结果。 例如要计算13,就会用到12和23。 for(intr=2;r<=n;r++) {for(inti=1;i {intj=i+r-1; //首先在i断开,即(Ai*(Ai+1...Aj)) m[i][j]=m[i+1][j]++p[i-1]*p[i]*p[j]; s[i][j]=i; for(intk=i+1;k {//然后在k(从i+1开始遍历到j-1)断开,即((Ai...Ak)*(Ak+1...Aj)) intt=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]; if(t {m[i][j]=t;//记录最少数乘次数 s[i][j]=k;//记录断开位置} } } } //Traceback打印A[i: j]的加括号方式 voidTraceback(inti,intj,int**s) {//s[i][j]记录了断开的位置,即计算A[i: j]的加括号方式为: //(A[i: s[i][j]])*(A[s[i][j]+1: j]) if(i==j)return; Traceback(i,s[i][j],s);//递归打印A[i: s[i][j]]的加括号方式 Traceback(s[i][j]+1,j,s);//递归打印A[s[i][j]+1: j]的加括号方式 //能走到这里说明i等于s[i][j],s[i][j]+1等于j //也就是说这里其实只剩下两个矩阵,不必再分了 cout<<"A"< int_tmain(intargc,_TCHAR*argv[]) {intn=6;//矩阵的个数 int*p=newint[n+1]; //p[0]: 第一个矩阵的行数 //p[1]: 第一个矩阵的列数,第二个矩阵的行数 //p[2]: 第二个矩阵的列数,第三个矩阵的行数 p[0]=30; p[1]=35; p[2]=15; p[3]=5; p[4]=10; p[5]=20; p[6]=25; int**m,**s; m=newint*[n]; for(inti=0;i m[i]=newint[n]; s=newint*[n]; for(inti=0;i s[i]=newint[n]; MatrixChain(p,n,m,s); Traceback(0,n-1,s); for(inti=0;i { delete[]m[i]; m[i]=NULL; delete[]s[i]; s[i]=NULL; } delete[]m; m=NULL; delete[]s; s=NULL; delete[]p; p=NULL; return0;} 算法复杂度分析: 算法matrixChain的主要计算量取决于算法中对r,i和k的3重循环。 循环体内的计算量为O (1),而3重循环的总次数为O(n3)。 因此算法的计算时间上界为O(n3)。 算法所占用的空间显然为O(n2)。 2.0-1背包问题 给定n种物品和一背包。 物品i的重量是wi,其价值为vi,背包的容量为C。 问应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 0-1背包问题是一个特殊的整数规划问题。 程序实现如下: #defineN12 voidKnapsack(intv[],intw[],intc,intn,intm[6][N]) {inti,j,jMax,k; jMax=(w[n]-1 w[n]-1: c; for(i=0;i<=jMax;i++) {m[n][i]=0;} for(i=w[n];i<=c;i++) {m[n][i]=v[n];} for(i=n-1;i>1;i--) {jMax=(w[i]-1 w[i]-1: c; for(j=0;j<=jMax;j++) { m[i][j]=m[i+1][j]; } for(j=w[i];j<=c;j++) { k=j-w[i]; if(m[i+1][j] m[i][j]=m[i+1][k]+v[i]; else m[i][j]=m[i+1][j];} } m[1][c]=m[2][c]; if(c>=w[1]) { k=c-w[1]; m[1][c]=(m[2][c]>m[2][k]+v[1])? m[2][c]: m[2][k]+v[1];} } voidTraceback(intm[6][N],intw[],intc,intn,intx[]) { inti; for(i=1;i {if(m[i][c]==m[i+1][c]) x[i]=0; else {x[i]=1; c-=w[i]; } } x[n]=(m[n][c])? 1: 0; } main() {inti,c=10,n=5,w[]={0,2,2,6,5,4},v[]={0,6,3,5,4,6}; intm[6][N]={0}; intx[6]={0}; intj; Knapsack(v,w,c,n,m); for(i=1;i<=n;i++) {for(j=1;j<=c;j++) printf("%3d",m[i][j]); printf("\n"); } Traceback(m,w,c,n,x); for(i=1;i<=n;i++) { if(x[i]) printf("%4d: %4d",i,v[i]);} printf("\n"); } 算法复杂度分析: 从m(i,j)的递归式容易看出,算法需要O(nc)计算时间。 当背包容量c很大时,算法需要的计算时间较多。 例如,当c>2n时,算法需要Ω(n2n)计算时间。 四.总结 通过本次实验,使我了解了动态规划算法的思想以及该算法的设计步骤。 对动态规划算法的两个个典型实例算法通过搜索资料找实现程序,大体上读懂实现程序,使我更加深了对动态规划机制的了解。 但实验也存在着不足之处是自己无法用所学语言实现这两个算法,是自己学的不够深入,对程序设计方面不够熟练,在今后的学习中将不断加强自己这方面的努力。 实验报告3贪心算法 一、实验目的 掌握贪心算法的基本思想和要素; 掌握贪心算法的的设计步骤; 二、实验环境 电脑一台,VC++运行环境 三、实验内容、步骤和结果分析 通过贪心算法的两个实例应用算法来加深对贪心算法的理解: 3.活动安排问题 以下是用C语言编写的程序实现活动安排问题 #include main() {ints[8]={2,3,0,5,3,5,8,7}; intf[8]={6,7,8,9,10,11,12}; C=GreedySelector(s[],f[]); Printf(“算出有%d个活动安排”,C); } intGreedySelector(inta[],intc[]) {intn=8; charb[2]={‘T’,‘F’} b[0]=T; intj=1; intc=1; for(inti=2;i<=n;i++) {if(a[i]>=c[j]) { a[i]=b[0]; j=i; c++; } elsea[i]=b[1]; } returnc; } 当输入的活动已按结束时间的非减序排列,算法只需O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。 如果所给出的活动未按非减序排列,可以用O(nlogn)的时间重排。 对于活动安排问题,贪心算法却总能求得的整体最优解,即它最终所确定的相容活动集合A的规模最大。 4.哈夫曼编码问题 由于自身编程能力有限无法实现本该问题,以下程序是参考网上资料编写 C语言实现 #include #defineN7/*叶子数目,需要时更改此值即可*/ #defineM2*N-1/*节点总数*/ typedefstruct {charbits[N];/*编码存储,位串*/ intstart;/*编码在位串中的位置*/ }codetype; typedefstruct {floatweight; intlchild,rchild,parent; }hufmtree; voidHUFFMAN(tree1) hufmtreetree1[]; {inti,j,p1,p2; floatsmall1,small2,f; hufmtree*tree; tree=tree1; for(i=0;i {tree[i].parent=0; tree[i].lchild=0; tree[i].rchild=0; tree[i].weight=0.0; } printf("pleaseinputapossibledataweight: \n");/*输入信源数据*/ for(i=0;i {scanf("%f",&f); tree[i].weight=f; } for(i=N;i {p1=0,p2=0; small1=1;small2=1; for(j=0;j<=i-1;j++)/*从所有的节点中,选出两个权值最小的根节点*/ if(tree[j].parent==0)/*parent值为0,则显示根节点,否则便是非根节点*/ if(tree[j].weight {small2=small1;/*改变最小权,次小权及对应的位置*/ small1=tree[j].weight; p2=p1;/*p1、p2记住这两个根节点在向量tree中的下标*/ p1=j; } elseif(tree[j].weight {small2=tree[j].weight;/*次小权及位置*/ p2=j;} tree[p1].parent=i+1;/*节点分量与下标之间差值为1*/ tree[p2].parent=i+1;/*节点的标号i+1*/ tree[i].lchild=p1+1;/*最小值根节点是新节点的左孩子,分量标号是其下标加1*/ tree[i].rchild=p2+1;/*次小权根节点是新节点的右孩子*/ tree[i].weight=tree[p1].weight+tree[p2].weight; } }/*HUFFMANTREE()*/ voidHUFFMANCODE(code1,tree1)/*根据哈夫曼树求出哈夫曼编码*/ codetypecode1[];/*求出的哈夫曼编码所在*/ hufmtreetree1[];/*已知的哈夫曼树*/ {inti,j,c,p; codetypecd;/*缓冲变量*/ codetype*code; hufmtree*tree; code=code1; tree=tree1; for(i=0;i {cd.start=N; c=i+1;/*从叶节点出发向上回溯*/ p=tree[i].parent;/*tree[p-1]是tree[i]的双亲*/ while(p! =0) {cd.start--; if(tree[p-1].lchild==c) cd.bits[cd.start]='0';/*tree[i]是左子树。 生成代码'0'*/ else cd.bits[cd.start]='1';/*否则tree[i]是右子树。 生成代码'1'*/ c=p; p=tree[p-1].parent; } code[i]=cd;/*第i+1个字符的编码存入code[i]*/ } }/*HUFFMANCODE*/ #include"stdio.h" main() {intk1,k2; hufmtreetree_fina[M]; hufmtree*p11=tree_fina; codetypecode_fina[N]; codetype*p21=code_fina; HUFFMAN(p11);/*建立huffman树*/ HUFFMANCODE(p21,p11);/*haffman码*/ for(k1=0;k1 {printf("number%dhaffmancode: ",k1+1); for(k2=code_fina[k1].start;k2 printf("%c",code_fina[k1].bits[k2]); printf("\n"); } } 该算法在最坏情况下的时间复杂度为O(nlogn)。 四.总结 通过本次实验,使我了解了贪心算法的思想以及该算法的设计步骤。 实现贪心规划算法的两个实例算法,使我更加深了对动态规划机制的了解。 但还是对编程这方面存在不足,无法实现哈夫曼编码,在今后的学习中将不断加强自己这方面的努力。 实验报告4回溯算法 一、实验目的 掌握回溯法的基本思想和要素; 掌握回溯法的的设计步骤; 二、实验环境 电脑一台,VC++运行环境 三、实验内容、步骤和结果分析 回溯法的基本思想: 确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。 这个开始结点就成为一个活结点,同时也成为当前的扩展结点。 在当前的扩展结点处,搜索向纵深方向移至一个新结点。 这个新结点就成为一个新的活结点,并成为当前扩展结点。 如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。 换句话说,这个结点不再是一个活结点。 此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。 回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。 运用回溯法解题通常包含以下三个步骤: a.针对所给问题,定义问题的解空间; b.确定易于搜索的解空间结构; c.以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索; 下面是通过实例0-1背包问题的实现,来加深对回溯法的理解: 0-1背包问题 本程序是摘自网上,自身编程能力有限,无法实现这个算法。 通过读这个程序的主要实现模块,大致理解了
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 设计 实验 报告