题目:运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。
- 设计思路
共有四种算法如下:
1、辗转相除法
辗转相除法(又名欧几里德法)C语言中用于计算两个正整数a,b的最大公约数和最小公倍数,实质它依赖于下面的定理:
根据这一定理可以采用函数嵌套调用和递归调用形式进行求两个数的最大公约数和最小公倍数,现分别叙述如下:
①函数嵌套调用
其算法过程为: 前提:设两数为a,b设其中a 做被除数,b做除数,temp为余数
1、大数放a中、小数放b中;
2、求a/b的余数;
3、若temp=0则b为最大公约数;
4、如果temp!=0则把b的值给a、temp的值给a;
5、返回第二步;
②函数递归调用
2.穷举法(利用数学定义)
穷举法(也叫枚举法)穷举法求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数 。
①定义1:对两个正整数a,b如果能在区间[a,0]或[b,0]内能找到一个整数temp能同时被a和b所整除,则temp即为最大公约数。
②定义2:对两个正整数a,b,如果若干个a之和或b之和能被b所整除或能被a所整除,则该和数即为所求的最小公倍数。
3. 更相减损法
更相减损术,是出自《九章算术》的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。《九章算术》是中国古代的数学专著,其中的“更相减损术”可以用来求两个数的最大公约数,即“可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。”
翻译成现代语言如下:
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
4.Stein算法
Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。来研究一下最大公约数的性质,发现有 gcd( k*x,k*y ) = k*gcd( x,y ) 这么一个非常好的性质。试取 k=2,则有 gcd( 2x,2y ) = 2 * gcd( x,y )。很快联想到将两个偶数化小的方法。那么一奇一个偶以及两个奇数的情况如何化小呢?
先来看看一奇一偶的情况: 设有2x和y两个数,其中y为奇数。因为y的所有约数都是奇数,所以 a = gcd( 2x,y ) 是奇数。根据2x是个偶数不难联想到,a应该是x的约数。我们来证明一下:(2x)%a=0,设2x=n*a,因为a是奇数,2x是偶数,则必有n是偶数。又因为 x=(n/2)*a,所以 x%a=0,即a是x的约数。因为a也是y的约数,所以a是x和y的公约数,有 gcd( 2x,y ) <= gcd( x,y )。因为gcd( x,y )明显是2x和y的公约数,又有gcd( x,y ) <= gcd( 2x,y ),所以 gcd( 2x,y ) = gcd( x,y )。至此,我们得出了一奇一偶时化小的方法。
再来看看两个奇数的情况:设有两个奇数x和y,不妨设x>y,注意到x+y和x-y是两个偶数,则有 gcd( x+y,x-y ) = 2 * gcd( (x+y)/2,(x-y)/2 ),那么 gcd( x,y ) 与 gcd( x+y,x-y ) 以及 gcd( (x+y)/2,(x-y)/2 ) 之间是不是有某种联系呢?为了方便设 m=(x+y)/2 ,n=(x-y)/2 ,容易发现 m+n=x ,m-n=y 。设 a = gcd( m,n ),则 m%a=0,n%a=0 ,所以 (m+n)%a=0,(m-n)%a=0 ,即 x%a=0 ,y%a=0 ,所以a是x和y的公约数,有 gcd( m,n )<= gcd(x,y)。再设 b = gcd( x,y )肯定为奇数,则 x%b=0,y%b=0 ,所以 (x+y)%b=0 ,(x-y)%b=0 ,又因为x+y和x-y都是偶数,跟前面一奇一偶时证明a是x的约数的方法相同,有 ((x+y)/2)%b=0,((x-y)/2)%b=0 ,即 m%b=0 ,n%b=0 ,所以b是m和n的公约数,有 gcd( x,y ) <= gcd( m,n )。所以 gcd( x,y ) = gcd( m,n ) = gcd( (x+y)/2,(x-y)/2 )。
整理一下,对两个正整数 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 );
- 算法构造
1、辗转相除法
2.穷举法
Stein算法
三、算法实现
源代码:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
//碾转相除法a.函数嵌套调用
int divisor1 (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 divisor2(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--; //不满足条件则变量自减
}
return(temp);
}
//更相减损法
int gcd(int a,int b)
{
int i=0,temp,x;
while(a%2==0&&b%2==0) //判断a,b是否为偶数,并分别求出能被多少2整除
{
a/=2;
b/=2;
i+=1;
}
if(a<b) //设置变量使a保存大数
{
temp=a;
a=b;
b=temp;
}
while(x) //利用等值法求最大公约数
{
x=a-b; //求a,b两数的差值,判断大小,a存储a与差之中较大的一个,
a=(b>x)?b:x;
b=(b<x)?b:x;
if(b==(a-b))
break;
}
if(i==0)
return b;
else
return(int)pow(2,i)*b;
}
//Stein算法
int Stein(unsigned int x,unsigned int y)
{
int factor = 0;
int temp;
if (x<y) //判断两数的大小,并交换
{
temp=x;
x=y;
y=temp;
}
if(0==y) //判断y是否为0
return 0;
while(x!=y) //判断两数是否相等
{
if(x&0x1)
{
if(y&0x1)
{
y=(x-y)>>1;
x-=y;
}
else
y>>=1; //y值除2,即右移
}
else
{
if(y&0x1)
{
x>>=1;
if(x<y)
{
temp=x;
x=y;
y=temp;
}
}
else
{
x>>=1; //x,y均右移
y>>=1;
++factor;
}
}
}
return(x<<factor);
}
int main()
{
clock_t start,finish;
int i,Time1,Time2,Time3,Time4,a,b;
int p;
int m[40],n[40],t;
int choose=1;
printf(" 求最大公约数 \n"); //目录
printf("1.辗转相除法 函数嵌套调用\n");
printf("2.穷举法\n");
printf("3.更相减损法\n");
printf("4.Stein算法\n");
srand(time(0)); //随机函数,随机产生40组数
printf("随机产生的第一个数组为: ");
for(i=0;i<40;i++)
{ m[i]=rand()%5000; //调用随机函数,将数分别存储在两个数组里
printf(" %d ",m[i]);
}
printf("\n");
printf("随机产生的第二个数组为: ");
for(i=0;i<40;i++)
{ n[i]=rand()%5000;
printf(" %d ",n[i]);
}
while(choose)
{
printf("\n");
printf("请输入你所选择的算法: ");
scanf("%d",&p);
switch( p )
{
case 1:
//碾转相除法a.函数嵌套调用
printf("\n");
printf("碾转相除法a.函数嵌套调用\n");
start=clock(); //调用时间函数获取初始时间
printf("最大公约数\n ");
for(i=0;i<40;i++)
{
a=m[i];
b=n[i];
t=divisor1(a,b); //调用辗转相除法理所用的函数
printf("%d ",t);
}
finish=clock(); //获取程序结束时的时间
Time1=finish-start; //用结束时的时间减去初始时间即为该算法所需时间
printf("\n消耗时间为%d毫秒\n",Time1);
break;
case 2:
//穷举法
printf("\n");
printf("穷举法\n");
start=clock(); //调用时间函数获取初始时间
printf("最大公约数为\n ");
for(i=0;i<40;i++)
{
a=m[i];
b=n[i];
t=divisor2(a,b); //调用穷举法所用的函数
printf("%d ",t);
}
finish=clock(); //获取程序结束时的时间
Time2=finish-start; //用结束时的时间减去初始时间即为该算法所需时间
printf("\n消耗时间为%d毫秒\n",Time2);
break;
case 3:
//更相减损法
printf("\n");
printf("更相减损法\n");
start=clock();
printf("最大公约数\n ");
for(i=0;i<40;i++)
{
a=m[i];
b=n[i];
t=gcd(a,b); //调用更相减损法所用的函数
printf("%d ",t);
}
finish=clock(); //获取程序结束时的时间
Time3=finish-start; //用结束时的时间减去初始时间即为该算法所需时间
printf("\n消耗时间为%d毫秒\n",Time3);
break;
case 4:
//Stein算法
printf("\n");
printf("Stein算法\n");
start=clock(); //调用时间函数获取初始时间
printf("最大公约数\n ");
for(i=0;i<40;i++)
{
a=m[i];
b=n[i];
t=Stein(a,b); //调用Stein算法所用的函数
printf("%d ",t);
}
finish=clock(); //获取程序结束时的时间
Time4=finish-start; //用结束时的时间减去初始时间即为该算法所需时间
printf("\n消耗时间为%d毫秒\n",Time4);
break;
}
printf("退出系统请选0,继续请选1: ");
scanf("%d",&choose);
if(choose==0)
return 0;
}
}
- 四、调试及测试
- 运行结果
测试
- 辗转相除法
#include "stdio.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 multiple (int a,int b) /*自定义函数求两数的最小公倍数*/ { int divisor (int a,int b); /*自定义函数返回值类型*/ int temp; temp=divisor(a,b); /*再次调用自定义函数,求出最大公约数*/ return (a*b/temp); /*返回最小公倍数到主调函数处进行输出*/ } main() { int m,n,t1,t2; /*定义整型变量*/ printf("please input two integer number:"); /*提示输入两个整数*/ scanf("%d%d",&m,&n); /*通过终端输入两个数*/ t1=divisor(m,n); /*自定义主调函数*/ t2=multiple(m,n); /*自定义主调函数*/ printf("The higest common divisor is %d\n",t1); /*输出最大公约数*/ printf("The lowest common multiple is %d\n", t2); /*输出最小公倍数*/ }
2、穷举法
#include "stdio.h"
int divisor (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); /*返回满足条件的数到主调函数处*/
}
main()
{
int m,n,t1;
printf("please input two integer number:");
scanf("%d%d",&m,&n);
t1=divisor(m,n);
printf("The higest common divisor is %d\n",t1);
}
3、更相减损法
#include<stdio.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;
}
main()
{
int m,n,x,temp,t1;
printf("please input two integer number:");
scanf("%d%d",&m,&n);
t1=gcd(m,n);
printf("The higest common divisor is %d\n",t1);
}
4、Stein算法
#include<stdio.h>
//Stein算法
int Stein(unsigned int x,unsigned int y)
{
int factor = 0;
int temp;
if (x<y) //判断两数的大小,并交换
{
temp=x;
x=y;
y=temp;
}
if(0==y) //判断y是否为0
return 0;
while(x!=y) //判断两数是否相等
{
if(x&0x1)
{
if(y&0x1)
{
y=(x-y)>>1;
x-=y;
}
else
y>>=1; //y值除2,即右移
}
else
{
if(y&0x1)
{
x>>=1;
if(x<y)
{
temp=x;
x=y;
y=temp;
}
}
else
{
x>>=1; //x,y均右移
y>>=1;
++factor;
}
}
}
return(x<<factor);
}
main()
{
int x,y,temp,t1;
printf("please input two integer number:");
scanf("%d%d",&x,&y);
t1=Stein(x,y);
printf("The higest common divisor is %d\n",t1);
}
5、测试随机函数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
int i;
int m[40],n[40];
srand(time(0)); //随机函数,随机产生40组数
printf("随机产生的第一个数组为: ");
for(i=0;i<40;i++)
{ m[i]=rand()%5000; //调用随机函数,将数分别存储在两个数组里
printf(" %d ",m[i]);
}
printf("\n");
printf("随机产生的第二个数组为: ");
for(i=0;i<40;i++)
{ n[i]=rand()%5000;
printf(" %d ",n[i]);
}
}
6、测试时间函数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//碾转相除法a.函数嵌套调用
int divisor1 (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()
{
clock_t start,finish;
int i,Time1,Time2,Time3,Time4,a,t,b;
int m[40],n[40];
srand(time(0)); //随机函数,随机产生40组数
printf("随机产生的第一个数组为: ");
for(i=0;i<40;i++)
{ m[i]=rand()%5000; //调用随机函数,将数分别存储在两个数组里
printf(" %d ",m[i]);
}
printf("\n");
printf("随机产生的第二个数组为: ");
for(i=0;i<40;i++)
{ n[i]=rand()%5000;
printf(" %d ",n[i]);
}
printf("碾转相除法a.函数嵌套调用\n");
start=clock(); //调用时间函数获取初始时间
printf("最大公约数\n ");
for(i=0;i<40;i++)
{
a=m[i];
b=n[i];
t=divisor1(a,b); //调用辗转相除法理所用的函数
printf("%d ",t);
}
finish=clock(); //获取程序结束时的时间
Time1=finish-start; //用结束时的时间减去初始时间即为该算法所需时间
printf("\n消耗时间为%d毫秒\n",Time1);
}
- 五、实验归纳
在这次编程中,首先了解了四种求最大公约数和最小公倍数的四种算法,我先是把四种算法分别编译运行了一次,在第三个算法运行时出现问题后来问了同学才知道是缺少一个头文件,第四个算法刚开始一直没看懂,后来用数字开始验证才懂了那个算法,>>右移两位即就是除二,之后将几个算法合并一块再加随机函数,使其规模变大便于测试时间,查了随机函数相关的知识,还有时间函数的相关知识。
随机函数:
rand(产生随机数)
相关函数
srand
表头文件
#include<stdlib.h>
定义函数
int rand(void)
函数说明
rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1
种子函数
表头文件
#include<stdlib.h>
定义函数
void srand (unsigned int seed);
函数说明
srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,通常可以利用geypid()或time(0)的返回值来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。
时间函数
1. <time.h>中函数clock(),返回类型clock_t,精确度,毫秒级别
实例:
#include <stdio.h>
#include <time.h>
#include <math.h>
void test()
{
int i = 0;
int j = 0;
double a = 0;
while (i++ < 1000000)
while (j++ < 1000000)
{
a = sqrt(2.0);
}
}
int main(void)
{
clock_t start, finish;
double duration = 0.0;
start = clock();
test();
finish = clock();
duration = (double)(finish - start);输出单位ms duration = (double)(finish - start) / CLOCKS_PER_SEC; //输出单位为妙,精确到毫秒级 //#define CLOCKS_PER_SEC 1000
printf('%f seconds', duration);
return 0;
}
2、<time.h>中函数time(&t), 精确度,秒级别 功能:取以秒为单位的,从1970年1月1日格林威治时间00:00:00算起的当前时间,并把它存在长整形变量t中,函数返回如前所述的时间秒值。
测试 程序如下:
#include 'stdio.h '
#include 'time.h '
#include 'stdlib.h '
main()
{
long start,end;
time(&start);
delay(50000);
time(&end);
printf( 'end-start=%ld ',end-start);
getch();
}
3. 最精确的计时:QueryPerformanceCounter来查询定时器的计数值,如果硬件里有定时器,它就会启动这个定时器,并且不断获取定时器的值,这样的定时器精度,就跟硬件时钟的晶振一样精确的。
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void test()
{
int i = 0;
int j = 0;
double a = 0;
while (i++ < 1000000)
while (j++ < 1000000)
{
a = sqrt(2.0);
}
}
int main(void)
{
LARGE_INTEGER start;
LARGE_INTEGER end;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
test();
QueryPerformanceCounter(&end);
printf('user time : %.10f seconds', (double)(end.QuadPart - start.QuadPart) / (double)freq.QuadPart);
return 0;
}