四种方法计算两个数的最大公约数~~~

1.题目名称
计算两个数的最大公约数

2.题目分析
计算两个数的最大公约数,可以采用最大公约数的四种常用算法,分别是辗转相除法、穷举法、更相减损法、Stein算法。每种方法写一个函数,分别计算出最大公约数,主函数的菜单里有选择功能,用户可以选择自行输入两个数,然后调用这四种方法的任一种;也可以选择程序的测试功能,通过自己选择的组数,计算机自动产生随机函数,然后调用四种方法的任一种,并计算出程序运行的时间。

3.算法构造
3.1辗转相除法

设两数为a,b设其中a 做被除数,b做除数,temp为余数
1、大数放a中、小数放b中;
2、求a/b的余数;
3、若temp=0则b为最大公约数;
4、如果temp!=0则把b的值给a、temp的值给a;
5、返回第二步;

3.2穷举法(利用数学定义)
穷举法(也叫枚举法)穷举法求两个正整数的最大公约数的解题步骤:
从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数 。
对两个正整数a,b如果能在区间[a,0]或[b,0]内能找到一个整数temp能同时被a和b所整除,则temp即为最大公约数。

3.3更相减损法
更相减损术,是出自《九章算术》的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。《九章算术》是中国古代的数学专著,其中的“更相减损术”可以用来求两个数的最大公约数,即“可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。”
翻译成现代语言如下:
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。

3.4 Stein算法
Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。来研究一下最大公约数的性质,发现有 gcd( kx,ky ) = kgcd( 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 );
现在已经有了递归式,还需要再找出一个退化情况。注意到 gcd( x,x ) = x ,就用这个。

4.算法实现
4.1辗转相除法

int max1(int a,int b)             //辗转相除法-------1.函数嵌套调用
{
	int temp;
	if(a<b)                    //若存在a小于b的情况,则交换数值
	{
		temp=a;
		a=b;
		b=temp;
	}
	while(b!=0)          //直到b为0,通过赋值得到最大公约数为a的值
	{
		temp=a%b;           //temp为余数                      
		a=b;                //将b的值赋给a
		b=temp;            //将余数temp的值赋给b
	}
	return a;
}
int max2(int m,int n)        //辗转相除法-------2.函数递归调用
{
	if(m%n==0)          //若m和n取余后余数为0,则返回最大公约数n
		return n;
	else
		return max2(n,m%n);     //返回到max2这个函数中,并给m,n赋值
}

4.2穷举法(利用数学定义)

int max3(int x,int y)               //3.穷举法
{
	int temp;
	if(x<y)                        //把较小的值赋给temp
		temp=x;
	else
		temp=y;
	while(temp>0)
	{
		if(x%temp==0&&y%temp==0)   //若大数取余小数余数为0,则结束循环,小数就是两个数的最大公约数
			break;
		else
			temp--;                //两个数中较小的数temp减一,直到找到最大公约数或temp为0为止
	}
	return temp;
}

4.3更相减损法

int max4(int c,int d)             //4.更相减损法
{
	int i=0;
	int temp,x;
	while(c%2==0&&d%2==0)    //检测c和d是否偶数,若是,则用2约简
	{
		c=c/2;                           
		d=d/2;
		i++;                 //计算c和d被2约了几次
	}
	if(c<d)                  //始终令c<d
	{
		temp=c;
		c=d;
		d=temp;
	}
	while(x)                //当x不等于0时
	{
		x=c-d;              //以较大的数减较小的数                        
		c=(d>x)?d:x;        //把所得的差与较小的数比较
		d=(d<x)?d:x;
		if(x==d)           //所得的减数和差相等
			break;
	}
	if(i)                                //当i不等于0
		return (int)pow(2,i)*d;            //pow(2,i)表示2的i次方,即2的i次方与d的乘积就是最大公约数
}

4.4 Stein算法

int max5(unsigned int x,unsigned int y)       //5.Stein算法
{
	int factor=0;
	int temp;
	if(x<y)                              //赋值令x>y
	{
		temp=x;
		x=y;
		y=temp;
	}
	if(0==y)
	{
		return 0;
	}
	while(x!=y)                       //当x和y不相等
	{
		if(x & 0x1 )                   //0x是十六进制的表示方式,这里表示十六进制的1,即x相与1,也就是判断x是奇数
		{
			if(y & 0x1 )              //如果x,y都是奇数
			{
				y=(x-y)>>1;         // (x-y)即两个奇数的差是偶数,>>表示右移/2
				x-=y;
			}
			else                 //如果x是奇数,y是偶数
			{
				y>>=1;
			}
		}
		else                         
		{
			if(y & 0x1 )           //如果x是偶数,y是奇数
			{
				x>>=1;
				if(x<y)           //始终令x>y
				{
					temp=x;
					x=y;
					y=temp;
				}
			}
			else               //如果x,y是两个偶数
			{
				x>>=1;
				y>>=1;
				++factor;
			}
		}
	}
	return (x<<factor);
}

****4.5完整代码****

在这里插入代码片


#include<stdio.h>
#include<math.h>                           //计算2的i次方的函数pow(2,i)的头文件
#include<time.h>                          //计算程序运行时间和随机产生数的头文件
#include<stdlib.h>
int max1(int a,int b)                     //辗转相除法-------1.函数嵌套调用
{
	int temp;
	if(a<b)                               //若存在a小于b的情况,则交换数值
	{
		temp=a;
		a=b;
		b=temp;
	}
	while(b!=0)                           //直到b为0,通过赋值得到最大公约数为a的值
	{
		temp=a%b;                        //temp为余数                      
		a=b;                             //将b的值赋给a
		b=temp;                          //将余数temp的值赋给b
	}
	return a;
}
int max2(int m,int n)                 //辗转相除法-------2.函数递归调用
{
	if(m%n==0)                        //若m和n取余后余数为0,则返回最大公约数n
		return n;
	else
		return max2(n,m%n);          //返回到max2这个函数中,并给m,n赋值
}
int max3(int x,int y)               //3.穷举法
{
	int temp;
	if(x<y)                        //把较小的值赋给temp
		temp=x;
	else
		temp=y;
	while(temp>0)
	{
		if(x%temp==0&&y%temp==0)   //若大数取余小数余数为0,则结束循环,小数就是两个数的最大公约数
			break;
		else
			temp--;                //两个数中较小的数temp减一,直到找到最大公约数或temp为0为止
	}
	return temp;
}
int max4(int c,int d)                           //4.更相减损法
{
	int i=0;
	int temp,x;
	while(c%2==0&&d%2==0)                      //检测c和d是否偶数,若是,则用2约简
	{
		c=c/2;                           
		d=d/2;
		i++;                                  //计算c和d被2约了几次
	}
	if(c<d)                                   //始终令c<d
	{
		temp=c;
		c=d;
		d=temp;
	}
	while(x)                                 //当x不等于0时
	{
		x=c-d;                               //以较大的数减较小的数                        
		c=(d>x)?d:x;                         //把所得的差与较小的数比较
		d=(d<x)?d:x;
		if(x==d)                            //所得的减数和差相等
			break;
	}
	if(i)                                  //当i不等于0
		return (int)pow(2,i)*d;            //pow(2,i)表示2的i次方,即2的i次方与d的乘积就是最大公约数
}
int max5(unsigned int x,unsigned int y)                    //5.Stein算法
{
	int factor=0;
	int temp;
	if(x<y)                              //赋值令x>y
	{
		temp=x;
		x=y;
		y=temp;
	}
	if(0==y)
	{
		return 0;
	}
	while(x!=y)                        //当x和y不相等
	{
		if(x & 0x1 )                   //0x是十六进制的表示方式,这里表示十六进制的1,即x相与1,也就是判断x是奇数
		{
			if(y & 0x1 )               //如果x,y都是奇数
			{
				y=(x-y)>>1;           // (x-y)即两个奇数的差是偶数,>>表示右移/2
				x-=y;
			}
			else                      //如果x是奇数,y是偶数
			{
				y>>=1;
			}
		}
		else                         
		{
			if(y & 0x1 )             //如果x是偶数,y是奇数
			{
				x>>=1;
				if(x<y)            //始终令x>y
				{
					temp=x;
					x=y;
					y=temp;
				}
			}
			else                   //如果x,y是两个偶数
			{
				x>>=1;
				y>>=1;
				++factor;
			}
		}
	}
	return (x<<factor);
}

void main()
{
	int x,y,p,i,n,N,m[1000];
	int a,b,c,d,e;
	printf("*********************你有两种选择********************\n");
	printf("* 1.输入两个你想计算最大公约数的正整数并计算.       *\n");
	printf("* 2.利用随机数测试最大公约数并计算程序运行时间.     *\n");
	printf("*****************************************************\n");
	printf("请选择你想要进行的操作:\n");
	scanf("%d",&n);
	while(n<1||n>2)
	{
		printf("没有这个选项!请重新输入:\n");
		scanf("%d",&n);
	}
	if(n==1)
	{
	printf("请输入两个正整数:\n");     //用户自行输入两个数
	scanf("%d%d",&x,&y);
	while(x<0||y<0||x==0||y==0)
	{
		printf("请按要求输入正整数:\n");
		scanf("%d%d",&x,&y);
	}
	printf("在这里你有5种方法计算最大公约数:\n");            //选择菜单
	printf(" * * * * * * * * * * * * * * * * * * * * *\n");
	printf(" *   1.辗转相除法----函数嵌套调用.       *\n");                
	printf(" *   2.辗转相除法----函数递归调用.       *\n");              
	printf(" *   3.穷举法.                           *\n"); 
	printf(" *   4.更相减损法.                       *\n");
	printf(" *   5.Stein算法.                        *\n");
	printf(" * * * * * * * * * * * * * * * * * * * * *\n"); 
	while(1)
	{
		int j=0;
	    printf("请输入你的选择(1-5):\n");
		scanf("%d",&p);
		while(p<1||p>5)
		{
			printf("输入错误!请重新输入:\n");
			scanf("%d",&p);
		}
		switch(p)
		{
		case 1:
				a=max1(x,y);
				printf("你选择了辗转相除法中的函数嵌套调用.\n");
				break;
		case 2:
	    		a=max2(x,y);
		    	printf("你选择了辗转相除法中的函数递归调用.\n");
			break;
		case 3:
				a=max3(x,y);
				printf("你选择了穷举法.\n");
				break;
		case 4:
				a=max4(x,y);
				printf("你选择了更相减损法.\n");
				break;
		case 5:
			
	    		a=max5(x,y);
		    	printf("你选择了Stein算法.\n");
				break;
		}
		printf("用这种方法计算的最大公约数为%d:\n",a);
	}
}
else if(n==2)
{
		clock_t start,finish;                  //计算随机函数
		double duration;
		srand((unsigned)time(NULL));
		printf("你想测试多少组数据?\n");
		scanf("%d",&N);
		for(i=0;i<N;i++)                     //随机取20个数(1-100)
		{
			m[i]=rand()%100+1;
			printf("%d\t",m[i]);
		}
		printf("\n");
		printf("在这里你有5种方法计算最大公约数:\n");            //选择菜单
		printf(" * * * * * * * * * * * * * * * * * * * * *\n");
		printf(" *   1.辗转相除法----函数嵌套调用.       *\n");                
		printf(" *   2.辗转相除法----函数递归调用.       *\n");              
		printf(" *   3.穷举法.                           *\n"); 
		printf(" *   4.更相减损法.                       *\n");
		printf(" *   5.Stein算法.                        *\n");
		printf(" * * * * * * * * * * * * * * * * * * * * *\n"); 
		while(1)
		{
			int j=0;
	    	printf("请输入你的选择(1-5):\n");
			scanf("%d",&p);
			while(p<1||p>5)
			{
				printf("输入错误!请重新输入:\n");
				scanf("%d",&p);
			}
			switch(p)
			{
			case 1:
				start=clock();                  //程序运行,开始计时
				while(j<20)
				{
					x=m[j++];
					y=m[j++];
					a=max1(x,y);
					printf("你选择了辗转相除法中的函数嵌套调用.\n");
					printf("用这种方法计算的最大公约数为%d:\n",a);
		
				}
				finish=clock();                                  //程序运行结束,结束计时
				break;
			case 2:
				start=clock();
				while(j<20)
				{
					x=m[j++];
					y=m[j++];
	    			b=max2(x,y);
		    		printf("你选择了辗转相除法中的函数递归调用.\n");
		    		printf("用这种方法计算的最大公约数为%d:\n",b);
				}
				finish=clock();
				break;
			case 3:
				start=clock();
				while(j<20)
				{
					x=m[j++];
					y=m[j++];
					c=max3(x,y);
					printf("你选择了穷举法.\n");
					printf("用这种方法计算的最大公约数为%d:\n",c);
				}
				finish=clock();
				break;
			case 4:
				start=clock();
				while(j<20)
				{	
					x=m[j++];
					y=m[j++];
					d=max4(x,y);
					printf("你选择了更相减损法.\n");
					printf("用这种方法计算的最大公约数为%d:\n",d);
				}
				finish=clock();
				break;
			case 5:
			start=clock();
			while(j<20)
			{
				x=m[j++];
				y=m[j++];
	    		e=max5(x,y);
		    	printf("你选择了Stein算法.\n");
		    	printf("用这种方法计算的最大公约数为%d:\n",e);
			}
			finish=clock();
			break;
		}
			duration=(double)(finish-start)/1000;                  计算时间差,由于计算机计算的是毫秒,转换成秒要除以1000
			printf("这个方法的运行时间是%f秒\n",duration);
	}
		}
	}

5.经验归纳
5.1遇到的问题
1.对随机函数不熟悉;
2.测试环节,对计算程序运行时间不熟悉;
3.在更相减损法的最后,用到了pow()函数,因没加头文件#include<math.h>而出错;
4.因知识储备不足,对最后一种方法(Stein算法)不理解。
PS:以上问题均已解决。

5.2心得体会
我觉得这四种方法里Stein算法我有点看不懂(可能是因为有>>运算符),还有随机数的生成和计算程序运行时间的函数都只是有一些印象,并不熟悉。(但是我可以查资料和问同学啊hhh~~~)
主函数我改了很多次,就是想让界面更加美化一些,改完就发现程序出错了…其实就是赋值这么一个小细节,但就是发现不了(Emmmm),感觉自己还需要学很多东西,动手能力也要加强~~~
总之就是~~~add oil!!!

  • 8
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值