C/C++ 算法设计与分析实验报告

算法设计与分析实验报告

  • 算法实验整体框架的构建
    • 菜单代码块
    • 选择函数代码块
    • 主函数代码块
    • 实验模块Ⅰ:算法分析基础——Fibonacci序列问题
    • 实验解析
    • Fibonacci序列问题代码块
    • 实验模块Ⅱ:分治法在数值问题中的应用——矩阵相乘问题
    • 实验分析
    • 矩阵相乘问题代码块
    • 实验模块Ⅲ:减治法在组合问题中的应用——8枚硬币问题
    • 程序分析
    • 8枚硬币问题代码块
    • 实验模块Ⅳ:变治法在排序问题中的应用——堆排序问题
    • 程序分析
    • 堆排序问题代码块
    • 实验模块Ⅴ:动态规划法在图问题中的应用——全源最短路径问题
    • 程序分析
    • 全源最短路径问题代码块
    • 程序源代码

算法实验整体框架的构建

  1. 实验题目
    算法实验主菜单的设计。
    2.实验目的
    ⑴ 熟悉实验环境 VC ++ 6.0 ;
    ⑵ 复习 C、C++语言以及数据结构课程的相关知识,实现课程间的平滑过度;
  2. 实验要求
    1)设计的主菜单可以是图形模式的,也可以是控制台模式的。以控制台为例,主菜单大
    致如下:
    -------------------------
    《算法设计与分析》实验
    -------------------------
  3. 算法分析基础 —— Fibonacci 序列问题
  4. 分治法在数值问题中的应用 —— 最近点对问题
  5. 减治法在组合问题中的应用 —— 8枚硬币问题
  6. 变治法在排序问题中的应用 —— 堆排序问题
  7. 动态规划法在图问题中的应用 —— 全源最短路径问题
  8. 退出本实验
    -------------------------
    请输入您所要执行的操作( 1,2,3,4,5,99):
    2)点击操作后进入相应的实验项目或是相应项目的下一级菜单;
    3)可以反复执行,直到退出实验。

菜单代码块

void menu()//主菜单 
{	
//标题头 
	cout<<"				-----------------------------------------------------				"<

选择函数代码块

void select ()//主菜单选择函数 
{
	AdjMatrix G;
	int n;
	cout<<"请输入:";
	cin>>n;
	//输入菜单数字判断 
	switch(n)
	{
		case 1:fibonacci();
		break;
		case 2:matrix();
		break;
		case 3:yingbi();
		break;
		case 4:HEAP(); 
		break;
		case 5:
				 CreateGraph(&G);
    			 Floyd(&G);
    			 DispGraph(G);
		break;
		case 99:exit();
		break;
		default :
			cout<<"输入错误"; 
			menu();
			select();
		
	}
}

主函数代码块

int main(void)
{	
	int m =1;//控制菜单,始终进入 
	while (m == 1)
	{
			menu();
			select();
			cout<>m;
			system("cls");
	}
	
}

C/C++ 算法设计与分析实验报告_第1张图片

实验模块Ⅰ:算法分析基础——Fibonacci序列问题

实验题目
给定一个非负整数 n,计算第 n 个 Fibonacci 数

实验目的
1)理解递归算法和迭代算法的设计思想以及递归程序的调式技术
2)掌握并应用递归算法和迭代算法效率的理论分析 (前验分析 ) 和实际分析 ( 后验分析)方法;
3)理解这样一个观点:不同的算法可以解决相同的问题,这些算法的解题思路不同,复杂程度不同,效率也不同;

实验要求
1)使用迭代算法 Fib(n), 找出最大的 n, 使得 第 n 个 Fibonacci
数不超过计算机所能表示的最大整数,并给出具体的执行时间;
2)对于要求 1), 使用教材 2.5 节中介绍的递归算法 F(n) 进行计算 , 同样给出具体的执行时间,并同 1) 的执行时间进行比较;
3)对于输入同样的非负整数 n,比较上述两种算法基本操作的执行次数;
4)对 1) 中的迭代算法进行改进,使得改进后的迭代算法其空间复杂度为 Θ(1) ;
5)设计可供用户选择算法的交互式菜单 ( 放在相应的主菜单下 )。

实验解析

1、递归法基本思想
递归就是定义一个函数,让它自己调用自己。
Fib (int n )
// 输入整数 n,输出第 n 个斐波那契数
{if(n=0)return 0;
Else if(n=1)return 1;
Else return Fib(n-1)+Fib(n-2)
}
2、迭代法
这种方法相对于递归法来说在时间复杂度上减小了不少,但代码相对就要复杂些了。
Fib (int n )
// 输入整数 n,输出第 n 个斐波那契数
{if(n=0)return 0;
Else if(n=1)return 1;
Else
F0 ←0;
F1 ←1;
for(i=2;i {
F2=f1+f0; //f1 表示当前的值
F0 ←F1
F1 ←F2;
}
Return F2;
}

Fibonacci序列问题代码块

//Fibonacci数列
int Fi(int i)
{
	if(i<=1)
		return i;
	else
		return Fi(i-1)+Fi(i-2);
}

void Fn()
{
	int i=0,j=0;
	do
	{
		j=Fi(i);
		if(j>=0)
			printf("%d  ",j);
		++i;
	}while(j>=0);
	printf("\n计算机所能计算的最大整数是第%d个fibonacci整数。\n",i-1);
}

void Fib()
{ 
	int i=1,f[3]={0,1};
	printf("%d  \n",f[0]);
	do
	{	
		printf("%d  \n",f[1]);
		f[2]=f[1]+f[0];
		f[0]=f[1];
		f[1]=f[2];
		i++;
	}while(f[1]>=f[0]);
	printf("\n\t计算机所能表示的最大fibonacci整数是第%d个fibonacci整数。\n",i);
}

void fibonacci()
{
	double start;
	start=clock();
	Fib();
	printf("\t迭代所用时间是%lf \n\n",clock()-start);
	start=clock();
	Fn();
	printf("\t递归所用时间是%lf \n\n",clock()-start);
}

实验模块Ⅱ:分治法在数值问题中的应用——矩阵相乘问题

实验题目
设 M1 和 M2 是两个 n×n 的矩阵,设计算法计算 M1×M2 的乘积。

实验目的
1)提高应用蛮力法设计算法的技能;
2)深刻理解并掌握分治法的设计思想;
3)理解这样一个观点:用蛮力法设计的算法,一般来说,经过适度的努力后,都可以对其进行改进,以提高算法的效率。

实验要求
1)设计并实现用 BF方法求解矩阵相乘问题的算法;
2)设计并实现用 DAC方法求解矩阵相乘问题的算法;
3)以上两种算法的输入既可以手动输入,也可以自动生成;
4)对上述两个算法进行时间复杂性分析,并设计实验程序验证分析结果;
5)设计可供用户选择算法的交互式菜单 (放在相应的主菜单下 ) 。

实验分析

1)蛮力法求解 蛮力法比较简单,直接使用矩阵相乘公式计算矩阵的每行每列的值,很容易就能得出答案
2)分治法求解 分治法思想 将问题实例划分为同一问题的几个较小的实例。对这些较小实例求解,通常使用 递归方法,但在问题规模足够小时,也会使用另一种算法。如果有必要,合并这些问题的 解,以得到原始问题的解。 求解矩阵相乘的 DAC 算法,使用了 strassen 算法。 DAC(A[] ,B[] ,n)
{ If n=2 使用 7 次乘法的方法求得解
Else
Divide (A)// 把 A分成 4 块 Divide (B)// 把 B分成 4 块
调用 7 次 strassen 算法求得解的 4 块 合并这 4 块得到解并返回
}

矩阵相乘问题代码块

//矩阵相乘问题求解 
void PrintIn(Array A,Array B)
{
	int n=A.n;
	int i,j;
	printf("请输入A数据:\n");
	for(i=0;i>A.a[i*n+j];
    printf("请输入B数据:\n");
	for(i=0;i>B.a[i*n+j];
}

void RandomIn(Array A,Array B)
{
	int n=A.n;
	srand((unsigned)time(NULL));
	int i,j;
	for(i=0;i

实验模块Ⅲ:减治法在组合问题中的应用——8枚硬币问题

实验题目
在 8 枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是较重。可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测这枚假币。
实验目的
1)深刻理解并掌握减治法的设计思想并理解它与分治法的区别;
2)提高应用减治法设计算法的技能。
3)理解这样一个观点:建立正角的模型对于问题的求解是非常重要的。
实验要求
1)设计减治算法实现 8枚硬币问题;
2)设计实验程序,考察用减治技术设计的算法是否高效;
3)扩展算法,使之能处理 n 枚硬币中有一枚假币的问题。

程序分析

减治法主要有三种变种 减去一个常量 减去一个常量因子 减可变规模 与分治法的区别:分治法把实例分为几部分分别求解 减治法把实例的规模减为一个 更小的实例求解 A(n)//输入硬币个数 n,要求必须有假币且已知假币只有 1 个(假设更重) {if n=1, 这枚就是假币 Else if n=2 ,较重的那枚是假币
Else 把 n 个硬币分为 3 份, 2 份相等,另一份与这 2 份的相差个数尽可能小 称 2 份个数相等的 If 这 2 份重量相等,假币在第 3 堆中,对第 3 堆使用该算法 Else 在较重的那堆中,对该堆硬币使用该算法
}

8枚硬币问题代码块

//硬币算法实现阶段
void print(int jia, int zhen, int i)
{
	if(jia > zhen)
	{
		cout<<"位置在:"<<(i + 1)<<"是假币!"<<"且偏重!";
	}
	else {
		cout<<"位置在:"<<(i + 1)<<"是假币!"<<"且偏轻!";
	}
}
void compare(int a, int b,int real, int index1,int index2)
{
	if(a == real)
	{
		print(b,real,index2);
	}
	else
	{
		print(a, real,index1);
	}
}
void eightcoin(int arr[])
{
	//1. 取数组中的前6个元素分为两组进行比较abc,def
	//会有a+b+c > d+e+f | a+b+c == d+e+f | a+b+c < d+e+f 三种情况
	int abc = arr[0] + arr[1] + arr[2];
	int def = arr[3] + arr[4] + arr[5];
	int a = arr[0];
	int b = arr[1];
	int c = arr[2];
	int d = arr[3];
	int e = arr[4];
	int f = arr[5];
	int g = arr[6];
	int h = arr[7];
	if(abc > def)		//6枚硬币必有一枚假币,g,h为真币
	{
		if((a + e) > (d + b))	//去掉c,f,且b,e互换后,没有引起天平变化,说明假币必然是a,d中的一个
		{
			compare(a, d, g,1,3);
			
		}
		else if((a + e) == (d + b))
		{
			compare(c,f,g,2,5);
		}
		else
		{
			compare(b,e,g,1,4);
		}
	}
	else if(abc == def)	//假币在g,h之中,最好状态
	{
		
		if(g == a)
		{
			print(h,g,7);
		} 
		else
		{
			print(g,h, 6);
		}
 
	}
	else				//abc < def 这两组存在一枚假币,g,h为真币
	{
		if((a + e) > (d + b))
		{
			compare(b,e,g,1,4);
		}
		else if((a + e) == (d + b))
		{
			compare(c,f,g,2,5);
		}
		else
		{
			compare(a, d, g,1,3);
		}
	}
 
}
void yingbi()//主函数 
{
	cout<<"实验3"<>arr[i];
	}
	eightcoin(arr);
	
}

实验模块Ⅳ:变治法在排序问题中的应用——堆排序问题

实验题目
用基于变治法的堆排序算法对任意一组给定的数据进行排序

实验目的
1)深刻理解并掌握变治法的设计思想;
2)掌握堆的概念以及如何用变治法把任意给定的一组数据改变成堆;
3)提高应用变治法设计算法的技能。

实验要求
1)设计与实现堆排序算法;
2)待排序的数据可以手工输入 (通常规模比较小, 10 个数据左右),用以检测程序的正确性;也可以计算机随机生成 (通常规模比较大, 1500-3000 个数据左右),用以检验 (用计数法 ) 堆排序算法的时间效率。

程序分析

堆排序利用了大根堆 (或小根堆 )堆顶记录的关键字最大 (或最小 )这一特征, 使得在当前 无序区中选取最大 ( 或最小 ) 关键字的记录变得简单。
堆排序 (HeapSort) 是一树形选择排序。
堆排序的特点是: 在排序过程中, 将 R[l…n]看成是一棵完全二叉树的顺序存储结构, 利用完全二叉树中双亲结点和孩子结点之间的内在关系 ( 参见二叉树的顺序存储结构 ) ,在当前无序区中选择关键字最大 ( 或最小 ) 的记录。堆排序的最坏时间复杂度为 O(nlogn) 。堆序的平均性能较接近于最坏性能

堆排序问题代码块

//堆排序
void in(int h[N],int n)
{
	int i;
	printf("手动输入要排序的数:");
	for(i=1;i<=n;i++)
		scanf("%d",&h[i]);
}
void random(int h[N],int n)
{
	int i;
    for(i=1;i<=n;i++)
		h[i]=rand();
    printf("随机产生要排序的数如下所示:\n");
    for(i=1;i<=n;i++)
        printf("%d\t",h[i]);
	printf("\n\n");
}
//自底向上构造一个堆
void HeapBottomUp(int h[],int n)
{
	int heap,i,k,v,j; 
	for(i=(n/2);i>0;i--)
	{
		k=i;
	    v=h[k];
        heap=0;
	    while((heap==0)&&(2*k<=n))
		{
			j=2*k;
			if(jh[j])
				heap=1;
			else
			{
				h[k]=h[j];
				k=j;
				h[k]=v;
			}
		}
	}
}
void HEAP()
{
	int h[N]={0},i,n,elem,m;
    double start, finish;
    char ch=' ';
    printf("\n\t\t输入要排序的数的规模:");
	scanf("%d",&n);
    printf("\n---1 手动输入---\n");
    printf("---2 自动生成---\n");
    printf("请选择:");
	do{
		ch=getch();
		if(ch=='1')
			in(h,n);
		if(ch=='2')
			random(h,n);
		else
			printf("\n输入出错,请重输:");
	}while(ch!='1'&&ch!='2');
	start=clock();
	m=n;
	while(m>1)
	{
	     HeapBottomUp(h,m);
	     //for(i=1;i<=n;i++)
			 //printf("%d\t\t",h[i]);
		 //printf("\n");
		 //根键(最大)与堆中最后一个键交换
         elem=h[1];
	     h[1]=h[m];
	     h[m]=elem;
         m=m-1;//堆的规模减一
         //for(i=1;i<=n;i++)
			 //printf("%d  ",h[i]);
         //printf("\n");
	}
	finish=clock();
	printf("\n输出排好的数组元素:\n");
    for(i=1;i<=n;i++)
	  printf("%d\t",h[i]);
	printf("\n输出算法执行的时间%f ",finish-start);
}

实验模块Ⅴ:动态规划法在图问题中的应用——全源最短路径问题

实验题目
给定一个加权连通图(无向的或有向的),要求找出从每个定点到其他所有定点之间的最短路径以及最短路径的长度。

实验目的
(1)深刻掌握动态规划法的设计思想并能熟练运用,理解它与分治法的区别;
(2)掌握最优性原理和最优子结构性质;
(3)理解这样一个观点:用动态规划方法求解问题的关键在于确定动态规划函数的递推式。

实验要求
(1)实现Floyd算法;
(2)算法的输入可以手动输入,也可以自动生成;
(3)算法不仅要输出从每个顶点到其他所有顶点之间的最短路径,还有输出最短路径的长度;
(4)设计一个权重为负的图或有向图的例子,对于它,Floyd算法不能输出正确的结果。

程序分析

通过Floyd计算图G=(V,E)中各个顶点的最短路径时,需要引入两个矩阵,矩阵S中的元素a[i][j]表示顶点i(第i个顶点)到顶点j(第j个顶点)的距离。矩阵P中的元素b[i][j],表示顶点i到顶点j经过了b[i][j]记录的值所表示的顶点。

假设图G中顶点个数为N,则需要对矩阵D和矩阵P进行N次更新。

初始时,矩阵D中顶点a[i][j]的距离为顶点i到顶点j的权值;如果i和j不相邻,则a[i][j]=∞,矩阵P的值为顶点b[i][j]的j的值。

接下来开始,对矩阵D进行N次更新。第1次更新时,如果”a[i][j]的距离” > “a[i][0]+a[0][j]”(a[i][0]+a[0][j]表示”i与j之间经过第1个顶点的距离”),则更新a[i][j]为”a[i][0]+a[0][j]”,更新b[i][j]=b[i][0]。

同理,第k次更新时,如果”a[i][j]的距离” > “a[i][k-1]+a[k-1][j]”,则更新a[i][j]为”a[i][k-1]+a[k-1][j]”,b[i][j]=b[i][k-1]。更新N次之后,操作完成。

全源最短路径问题代码块

 // 全源最短路径
void CreateGraph(AdjMatrix *G) //图的生成函数
{ 
    int n,e,vi,vj,w,i,j,choice,ret,k;
    FILE *fp;
    
	printf("请选择数据输入方式\n");
	printf("1.手动输入\n");
 	printf("2.自动输入\n"); 
    ret = scanf("%d",&choice); 
    
    while(ret != 1)
	{
		cout<<"您的输入有误,请重新输入!"<numV=n;G->numE=e;
   		for(i=0;iEdge[i][j]=0;
          	  else 
           	     G->Edge[i][j]=32767;
            }
         } 
   		 for(i=0;inumV;i++) //将顶点存入数组中
       		 { 
      		  //printf("请输入第%d个顶点的信息(整型):",i+1);  
     		 
     		   G->Vertices[i]=rand();
     		  // printf("第%d个顶点的信息(整型):%d",i+1,G->Vertices[i]);
      	 	 }
      	 } 
   		 printf("\n");

    	for(i=0;inumE;i++)
   	    { 
       	   //printf("请输入边的信息i,j,w(以空格分隔):");
       	   vi=rand()%10;vj=rand()%10;w=rand()%10;
    		//printf("%d  %d  %d\n",vi,vj,w);
      	   //若为不带权值的图,则w输入1
           //若为带权值的图,则w输入对应权值

       	   G->Edge[vi][vj]=w;//①
      	   G->Edge[vj][vi]=w;//②
           //无向图具有对称性的规律,通过①②实现
           //有向图不具备此性质,所以只需要①
   		}
	}
	else
	{
		printf("请输入图的顶点数和边数(以空格分隔):");
  			scanf("%d%d",&n,&e);
   		G->numV=n;G->numE=e;
   		for(i=0;iEdge[i][j]=0;
          	  else 
           	     G->Edge[i][j]=32767;
            }
         } 
   		 for(i=0;inumV;i++) //将顶点存入数组中
       		 { 
      		  printf("请输入第%d个顶点的信息(整型):",i+1);  
     		 // G->adjlist[i].vertex=getchar(); 
     		   scanf(" %d",&G->Vertices[i]);
      	 	 }
      	 } 
   		 printf("\n");

    	for(i=0;inumE;i++)
   	    { 
       	   printf("请输入边的信息i,j,w(以空格分隔):");
     	   scanf("%d%d%d",&vi,&vj,&w); 
      	   //若为不带权值的图,则w输入1
           //若为带权值的图,则w输入对应权值

       	   G->Edge[vi][vj]=w;//①
      	   G->Edge[vj][vi]=w;//②
           //无向图具有对称性的规律,通过①②实现
           //有向图不具备此性质,所以只需要①
   		}
	}
}
void DispGraph(AdjMatrix G) //输出邻接矩阵的信息
{ 
    int i,j;
    printf("\n输出顶点的信息(整型):\n");
    for(i=0;i",k);
    Ppath(path,k,j);
}

void Dispath(int A[][MaxVertices],int path[][MaxVertices],int n)
{
    int i,j;
    for (i=0;i",i);

                Ppath(path,i,j);//两点i,j之间还有其他中继结点,则循环套用次函数
                printf("%d\n",j);
            }
        }
    }
}
void Floyd(AdjMatrix *G)
{
    int A[MaxVertices][MaxVertices],path[MaxVertices][MaxVertices];
    int i,j,k;
    //初始化
    for (i=0;inumV;i++)
    {
        for (j=0;jnumV;j++)
        {
            A[i][j]=G->Edge[i][j];
            path[i][j]=-1;
        }
    }
//三重循环,floyd算法核心
    for (k=0;knumV;k++)
    {
        for (i=0;inumV;i++)
        {
            for (j=0;jnumV;j++)
            {
                if (A[i][j]>A[i][k]+A[k][j])
                {
                    A[i][j]=A[i][k]+A[k][j];
                    path[i][j]=k;
                }
            }
        }
    }
    Dispath(A,path,G->numV);//输出函数
}

程序源代码

main.cpp
若链接失效,请前往我的主页自取

你可能感兴趣的:(算法设计与分析,算法分析实验报告)