程序设计方法学上机——(一)
- 题目:
运行最大公约数的常用算法,进行程序的调试与测试,要求程序设计风良好,并添加异常处理模块(如非法输入)。
- 辗转相除法:
其算法过程为: 前提:设两数为a,b设其中a 做被除数,b做除数,temp为余数
==>大数放a中、小数放b中;
==>求a/b的余数;
==>若temp=0则b为最大公约数;
==>如果temp!=0则把b的值给a、temp的值给a;
==>返回第二步;
(1)非递归
(2)递归
- 穷举法:
穷举法(也叫枚举法)穷举法求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数 。
- 更相减损法:
==>任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
==>以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
- Stein算法
对两个正整数 x>y :
1.均为偶数 gcd( x,y ) =2gcd( x/2,y/2 );
2.均为奇数 gcd( x,y ) = gcd( (x+y)/2,(x-y)/2 );
2.x奇y偶 gcd( x,y ) = gcd( x,y/2 );
3.x偶y奇 gcd( x,y ) = gcd( x/2,y ) 或 gcd( x,y )=gcd( y,x/2 );
现在已经有了递归式,还需要再找出一个退化情况。注意到 gcd( x,x ) = x ,就用这个。
- 非递归
- 递归
- 算法构造:
- 辗转相除流程图:
(1)非递归:
(2)递归:
- 穷举法流程图:
- 更相减损法流程图:
- Stein算法流程图:
- 非递归:
- 递归:
- 算法实现:(源码)
- 辗转相除法:
(1)非递归:
//辗转相除法
/*函数嵌套调用*/
#include<stdio.h> /*输入输出类头文件*/
#include<stdlib.h>
#include<time.h>
#include<windows.h>
int GCD (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 main()
{
/*计时函数所需定义语句*/
double dur;
LARGE_INTEGER time_start;
LARGE_INTEGER time_over;
double dqFreq;
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
dqFreq=(double)f.QuadPart;
int gcd; /*公约数*/
int i,a[100001],b[100001]; /*定义两个数组*/
srand(1); /*随机函数*/
for(i=0;i<20;i++) /*循环来控制数据规模*/
{
a[i]=1+rand()%100;
b[i]=1+rand()%100;
}
QueryPerformanceCounter(&time_start);
for(i=0;i<20;i++)
{
gcd=GCD(a[i],b[i]); /*自定义函数*/
printf("随机数为%d,%d",a[i],b[i]);
printf("这两个整数的最大公约数是 %d\n\n",gcd); /*输出最大公约数*/
QueryPerformanceCounter(&time_over);
dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
}
printf("所需时间%f seconds\n",dur);
return 0;
}
- 递归:
//辗转相除法
/*函数递归*/
#include<stdio.h> /*输入输出类头文件*/
#include<stdlib.h>
#include<windows.h>
int GCD (int a,int b) /*自定义函数求两数的最大公约数*/
{ if(a%b==0) /*看b是否是最大公约*/
return b;
else
return GCD(b,a%b);
}
int main() /*主函数*/
{
/*计时函数所需定义语句*/
double dur;
LARGE_INTEGER time_start;
LARGE_INTEGER time_over;
double dqFreq;
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
dqFreq=(double)f.QuadPart;
int gcd; /*变量用于存最大公约数*/
int i,a[100001],b[100001]; /*定义两个数组*/
srand(10); /*随机函数*/
for(i=0;i<100000;i++)
{
a[i]=1+rand()%100;
b[i]=1+rand()%100;
}
QueryPerformanceCounter(&time_start);
for(i=0;i<100000;i++)
{
gcd=GCD(a[i],b[i]); /*自定义主调函数*/
printf("随机数为%d,%d",a[i],b[i]);
printf("这两个整数的最大公约数是 %d\n\n",gcd); /*输出最大公约数*/
QueryPerformanceCounter(&time_over);
dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
}
printf("所需时间%f seconds\n",dur);
return 0;
}
- 穷举法:
//穷举法
#include<stdio.h> /*输入输出类头文件*/
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<math.h>
int GCD (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 main()
{
/*计时函数所需定义语句*/
double dur;
LARGE_INTEGER time_start;
LARGE_INTEGER time_over;
double dqFreq;
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
dqFreq=(double)f.QuadPart;
int gcd; /*变量用于存最大公约数*/
int i,a[100001],b[100001]; /*定义两个数组*/
srand(10); /*随机函数*/
for(i=0;i<100000;i++) /*通过循环输入规模*/
{
a[i]=1+rand()%100;
b[i]=1+rand()%100;
}
QueryPerformanceCounter(&time_start);
for(i=0;i<100000;i++)
{
gcd=GCD(a[i],b[i]); /*自定义主调函数*/
printf("随机数为%d,%d",a[i],b[i]);
printf("这两个整数的最大公约数是 %d\n\n",gcd); /*输出最大公约数*/
QueryPerformanceCounter(&time_over);
dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
}
printf("所需时间%f seconds\n",dur);
return 0;
}
- 更相减损:
//更相减损
#include<stdio.h> /*输入输出类头文件*/
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<math.h>
int GCD(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;
}
int main()
{
//计时函数所需的定义语句
double dur;
LARGE_INTEGER time_start;
LARGE_INTEGER time_over;
double dqFreq;
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
dqFreq=(double)f.QuadPart;
int gcd; //定义变量用于存放最大公约数
int i,a[100001],b[100001];//定义两个数组
srand(10); //随机函数
for(i=0;i<100000;i++) //通过循环控制规模
{
a[i]=1+rand()%100;
b[i]=1+rand()%100;
}
QueryPerformanceCounter(&time_start);
for(i=0;i<100000;i++)
{
gcd=GCD(a[i],b[i]); //自定义主调函数
printf("随机数为%d,%d",a[i],b[i]);
printf("这两个整数的最大公约数是 %d\n\n",gcd); /*输出最大公约数*/
QueryPerformanceCounter(&time_over);
dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
}
printf("所需时间%f seconds\n",dur);
return 0;
}
- Stein算法:
- 非递归:
//stein
/*非递归*/
#include<stdio.h> /*输入输出类头文件*/
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<math.h>
int SteinGCD( unsigned int x, unsigned int y )
/* return the greatest common divisor of x and y */
{
int factor = 0;
int temp;
if ( x < y )
{
temp = x;
x = y;
y = temp;
}
if ( 0 == y )
{
return 0;
}
while ( x != y )
{
if ( x & 0x1 )
{/* when x is odd */
if ( y & 0x1 )
{/* when x and y are both odd */
y = ( x - y ) >> 1;
x -= y;
}
else
{/* when x is odd and y is even */
y >>= 1;
}
}
else
{/* when x is even */
if ( y & 0x1 )
{/* when x is even and y is odd */
x >>= 1;
if ( x < y )
{
temp = x;
x = y;
y = temp;
}
}
else
{/* when x and y are both even */
x >>= 1;
y >>= 1;
++factor;
}
}
}
return ( x << factor );
}
int main()
{
//计时函数所需要定义的语句
double dur;
LARGE_INTEGER time_start;
LARGE_INTEGER time_over;
double dqFreq;
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
dqFreq=(double)f.QuadPart;
int gcd;//定义变量用于存放最大公约数
int i,a[100001],b[100001];//定义两个数组
srand(10); //随机函数
for(i=0;i<100000;i++)//通过循环控制规模
{
a[i]=1+rand()%100;
b[i]=1+rand()%100;
}
QueryPerformanceCounter(&time_start);
for(i=0;i<100000;i++)
{
gcd=SteinGCD(a[i],b[i]);
printf("随机数为%d,%d",a[i],b[i]);
printf("这两个整数的最大公约数是 %d\n\n",gcd); /*输出最大公约数*/
//printf("所需时间%f\n",(double)(end-start)/CLOCKS_PER_SEC);
QueryPerformanceCounter(&time_over);
dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
}
printf("所需时间%f seconds\n",dur);
return 0;
}
- 递归:
//stein
/*递归实现*/
#include<stdio.h> /*输入输出类头文件*/
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<math.h>
int SteinGCD(int u,int v)
{
if (u == 0) return v;
if (v == 0) return u;
// look for factors of 2
if (~u & 1) // u is even
{
if (v & 1) // v is odd
return SteinGCD(u >> 1, v);
else // both u and v are even
return SteinGCD(u >> 1, v >> 1) << 1;
}
if (~v & 1) // u is odd, v is even
return SteinGCD(u, v >> 1);
// reduce larger argument
if (u > v)
return SteinGCD((u - v) >> 1, v);
return SteinGCD((v - u) >> 1, u);
}
int main()
{
//计时函数所需要定义的语句
double dur;
LARGE_INTEGER time_start;
LARGE_INTEGER time_over;
double dqFreq;
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
dqFreq=(double)f.QuadPart;
int gcd;//定义变量用于存放最大公约数
int i,a[100001],b[100001];//定义两个数组
srand(10); //随机函数
for(i=0;i<100000;i++)//通过循环控制规模
{
a[i]=1+rand()%100;
b[i]=1+rand()%100;
}
QueryPerformanceCounter(&time_start);
for(i=0;i<100000;i++)
{
gcd=SteinGCD(a[i],b[i]);
printf("随机数为%d,%d",a[i],b[i]);
printf("这两个整数的最大公约数是 %d\n\n",gcd); /*输出最大公约数*/
//printf("所需时间%f\n",(double)(end-start)/CLOCKS_PER_SEC);
QueryPerformanceCounter(&time_over);
dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
}
printf("所需时间%f seconds\n",dur);
return 0;
}
- 调试、测试:
- 调试:
- 随机数调试:(以3组数据为例)
===>观察a[i],b[i]当i在变化是每次的不同随机数,此时为未开始,二者为0;
===>i=0时,a[i]=72,b[i]=100
===>i=1时,a[i]=73,b[i]=95
===>i=2,a[i]=98,b[i]=97
- 测试:
--Stein递归--
--Stein非递归--
--更相减损--
--穷举法--
--辗转相除递归--
--辗转相除非递归--
- 分析:
--测试时间的表格结果--
- 6种算法比较:规模相同,数据范围相同:
在100组数据和1000组数据:辗转相除非递归算法效率比较高;
在10000组数据:stein算法效率较高
- 同一种算法比较,数据范围不同时,运行时间的变化幅度:
对于6种方法,当规模相同,随着随机数的范围不同,运行时间都有所增加,其中辗转相除非递归增加最为明显,
- 总结:
本程序运用六种代码来测试效率,采取了平均值来避免偶然性误差。
本实验为了测试求最大公约数的算法效率,在实验过程中,遇到了一些问题。
- 刚开始手动输入两位两位数据,发现效率低且不能进行大规模数据测试
- 计时函数在用的时候是在网上看了一些程序采用到本程序上
不足:
- 测试数据没有实现数据为0时求公约数
- 每次运行时间差异较大,没有找到原因所在
- 感觉数据测试不是很准确,数据规模范围没有达到比较好的地步
如果有什么错误请各位大神指出来,完成之后记得撒花撒花~~