一. 实验目的
- 明确算法的概念和特点。
- 通过对问题的分析,设计合理的算法解决问题;
二. 实验内容
运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。比较4种GCD算法在给定不同规模测试数据的情况下的平均运行时间
三. 题目分析
首先,要比较不同算法在求解同一组数据所运行的时间,数据方面采用随机函数srand(time(0))产生两组随机数,分别保存在两个数组中。
其次,运行计算公约数的不同函数,从两组数组中分别对应拿出一个数,这为一组数,直到运行完数组中的数,每一组数输出一个公约数。
最后,调用时间函数,在程序运行的开始和结束分别记录时间,然后用运行完的时间减去开始运行的时间,就得出了每一种算法在计算相同的一组数的公约数时所需要的时间。
四. 算法构造(流程图)
- 辗转相除法
2. 穷举法
.
3. 更相减损法
4. Stein算法
5. main函数流程图
五. 算法实现
程序源代码:
#include<stdio.h>
#include<time.h>//时间函数,获得时间信息的函数
#include<stdlib.h>//随机数生成函数
#include<math.h>//数学函数,程序后面用于计算次方
//①辗转相除法函数嵌套调用
int divisor (int a,int b)//自定义函数求两数的最大公约数
{
int temp;//定义整型变量
if(a<b)//通过比较求出两个数中的最大值和最小值
{ temp=a;a=b;b=temp;}//设置中间变量进行两数交换
while(b!=0)//通过循环求两数的余数,直到余数为0
{
temp=a%b;
a=b;//变量数值交换
b=temp;
}
return (a);//返回最大公约数到调用函数处
}
//②辗转相除法函数递归调用
int gcd (int a,int b)
{ if(a%b==0)
return b;
else
return gcd(b,a%b);
}
//穷举法
int divisor1 (int a,int b)//自定义函数求两数的最大公约数
{
int temp;//定义义整型变量
temp=(a>b)?b:a;//采种条件运算表达式求出两个数中的最小值
while(temp>0)
{
if (a%temp==0&&b%temp==0)//只要找到一个数能同时被a,b所整除,则中止循环
break;
temp--;//如不满足if条件则变量自减,直到能被a,b所整除
}
return (temp);//返回满足条件的数到主调函数处
}
//更相减损法
int gcd1(int m,int n)
{
int i=0,temp,x;
while(m%2==0 && n%2==0)//判断m和n能被多少个2整除
{
m/=2;
n/=2;
i+=1;
}
if(m<n)//m保存大的值
{
temp=m;
m=n;
n=temp;
}
while(x)//
{
x=m-n;
m=(n>x)?n:x;
n=(n<x)?n:x;
if(n==(m-n))
break;
}
if(i==0)
return n;
else
return (int)pow(2,i)*n;//pow计算2的i次方
}
//
//①Stein算法函数非递归调用
int Stein(unsigned int x,unsigned int y)
{
int factor = 0;
int temp;
if (x<y)//进行两数交换,将大值赋给x小值赋给y
{
temp=x;
x=y;
y=temp;
}
if(0==y)
return 0;
while(x!=y)
{
if(x&0x1)//判断x的奇偶性
{
if(y&0x1)//判断y的奇偶性
{
y=(x-y)>>1;//就是(x-y)/2的意思,这个表达式运行时间更短
x-=y;
}
else
y>>=1;
}
else
{
if(y&0x1)//判断y的奇偶性
{
x>>=1;//等价于x=x>>1,即x=x/2
if(x<y)
{
temp=x;
x=y;
y=temp;
}
}
else
{
x>>=1;
y>>=1;
++factor;
}
}
}
return(x<<factor);
}
//②Stein算法函数递归调用
int gcd2(int u,int v)
{
if (u == 0)
return v;
if (v == 0)
return u;
if (~u & 1) //u时偶数
{
if (v & 1) //v是奇数
return gcd(u >> 1, v);
else //u和v都是偶数
return gcd2(u >> 1, v >> 1);
}
if (~v & 1) //u是奇数,v是偶数
return gcd2(u, v >> 1);
//进一步细分
if (u > v)
return gcd2((u - v) >> 1, v);
return gcd2((v - u) >> 1, u);
}
#define N 100
void main()
{
float start,end,T1,T2,T3,T4,T5,T6;
int i,A[N],B[N];//产生两组随机数,分别保存在两个数组中
int F,S,T;
srand(time(0));//产生随机数
printf("第一组数为:");
for(i=0; i<N; i++)
{
A[i] = rand()%500;
printf("%d ",A[i]);
}
printf("\n\n");
printf("第二组数为:");
for(i=0;i<N;i++)
{
B[i]=rand()%500;
printf("%d ",B[i]);
}
printf("\n\n");
//
printf("辗转相除法函数嵌套调用:\n");
start=(float)clock();
for(i=0;i<N;i++)
{
F=A[i];S=B[i];
T=divisor(F,S);
printf("第%d组数为:%d %d ",i+1,A[i],B[i]);
printf("最大公约数为: %d\n",T);
}
end=(float)clock();
T1=(float)((end-start)/CLK_TCK);
printf("辗转相除法函数嵌套调用运行时间为:%fs\n\n",T1);
//
printf("辗转相除法函数递归调用:\n");
start=(float)clock();
for(i=0;i<N;i++)
{
F=A[i];S=B[i];
T=gcd(F,S);
printf("第%d组数为:%d %d ",i+1,A[i],B[i]);
printf("最大公约数为:%d\n",T);
}
end=(float)clock();
T2=(float)((end-start)/CLK_TCK);
printf("辗转相除法函数递归调用运行时间为:%fs\n\n",T2);
//
printf("穷举法:\n");
start=(float)clock();
for(i=0;i<N;i++)
{
F=A[i];S=B[i];
T=divisor1(F,S);
printf("第%d组数为:%d %d ",i+1,A[i],B[i]);
printf("最大公约数为: %d\n",T);
}
end=(float)clock();
T3=(float)((end-start)/CLK_TCK);
printf("穷举法运行时间为:%fs\n\n",T3);
//
printf("更相减损法:\n");
start=(float)clock();
for(i=0;i<N;i++)
{
F=A[i];S=B[i];
T=gcd1(F,S);
printf("第%d组数为:%d %d ",i+1,A[i],B[i]);
printf("最大公约数为: %d\n",T);
}
end=(float)clock();
T4=(float)((end-start)/CLK_TCK);
printf("更相减损法运行时间为:%fs\n\n",T4);
//
printf("Stein算法函数非递归调用:\n");
start=(float)clock();
for(i=0;i<N;i++)
{
F=A[i];S=B[i];
T=Stein(F,S);
printf("第%d组数为:%d %d ",i+1,A[i],B[i]);
printf("最大公约数为: %d\n",T);
}
end=(float)clock();
T5=(float)((end-start)/CLK_TCK);
printf("Stein算法函数非递归调用运行时间为:%fs\n\n",T5);
//
printf("Stein算法函数递归调用:\n");
start=(float)clock();
for(i=0;i<N;i++)
{
F=A[i];S=B[i];
T=Stein(F,S);
printf("第%d组数为:%d %d ",i+1,A[i],B[i]);
printf("最大公约数为: %d\n",T);
}
end=(float)clock();
T6=(float)((end-start)/CLK_TCK);
printf("Stein算法函数递归调用运行时间为:%fs\n\n",T6);
}
六. 调试、测试及运行结果
1.T1时间
2.T2时间
3.T3时间
4.T4时间
5.T5时间
6.T6时间
7.10组数据运行结果
8.100组数据运行结果(因为数据太多,所以只截取了每种算法所运行的时间)
七. 结论
- 刚开始用10组数据进行运算后,有运算时间很难对比出辗转相除法函数嵌套调用和递归调用哪个算法用的时间更短,其他算法运行时间比较好比较。
- 接下来用了100组数据进行运算后,这时可以明显对比出哪个算法运行更快。算法运行时间由短到长依次为:更相减损法、Stein算法非递归调用、辗转相除法递归调用、穷举法、辗转相除法嵌套调用、Stein算法递归调用。
*在计算多组数据的公约数时,更相减损法性能相对比较好些,相比于其他算法。
八. 总结
首先,我在一开始看到作业要求比较算法运行时间的时候,就想到了用随机函数产生随机数,这个和上学期数据结构期末作业设计的要求差不多,有了之间的经验,我在写主函数时还是比较顺利的。在调试的过程中,发现错误都是敲代码时不小心弄错的。接下来,我钻了牛角尖,我一直在考虑一整个程序不同函数函数运行时间有先后顺序,所以一直在找怎样让多个函数同时运行的方法,通过百度,我找到了一个方法就是把要同时运行的函数放在不同的.cpp文件中,然后采用包命令调用.cpp中的函数,这种方法我试过不过失败了。然后,我才想到程序运行的先后时间不一样,但是运行每个函数的结束时间减去开始时间,所得的运行时间与执行的先后顺序没有关系。
问题:虽然程序能够正常运行,但是,我发现不同求最大公约数的算法再每次变换了数据之后要编译好几次才能得到算法运行的正确时间。