C语言求两数最大公约数的四种算法

实验目的

1.明确算法的概念和特点;
2.通过对问题的分析,设计合理的算法解决问题。

实验内容

运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。

题目分析

求两数的最大公约数,常用的算法有辗转相除法、穷举法、更相减损法、Stein算法等。将每一种算法用一个函数实现,再在主函数中用switch()语句调用任意一种算法,并且在主函数中利用rand()、srand()函数来产生随机函数进行每种算法平均运行时间的测试。

算法构造

辗转相除法

算法过程: 前提设两数为a,b设其中a 做被除数,b做除数,temp为余数
1、大数放a中、小数放b中;
2、求a/b的余数;
3、若temp=0则b为最大公约数;
4、如果temp!=0则把b的值给a、temp的值给a;
5、返回第二步。
流程图
图1.辗转相除法算法流程图
代码

 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);         //返回最大公约数到调用函数处
}
//辗转相除法(2.函数递归调用)
int divisor2(int a,int b)
{
	if(a%b==0)     //若a,b取余后余数为0,则返回最大公约数b
		return b;
	else
		return divisor2(b,a%b); //否则递归调用,继续取余
}
穷举法

算法过程:对两个正整数a,b如果能在区间[a,0]或[b,0]内能找到一个整数temp能同时被a和b所整除,则temp即为最大公约数。
流程图
在这里插图2.穷举法算法流程图入图片描述
代码

int divisor3(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);
}
更相减损法

算法过程:第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
流程图
在这里插入图片描述
代码

 int divisor4(int m,int n)
{	int i=0,temp,x;
	while(m%2==0&&n%2==0)  //判断a,b是否都是偶数,若是则用2约简
	{
		m/=2;
		n/=2;
		i+=1;  //约简2的个数
	}
	if(m<n)  //a保存最大值
	{
		temp=m;
		m=n;
		n=temp;
	}
	while(x)       //当x不为0时
	{
		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;
}
Stein算法

算法过程:Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。最大公约数的性质有 gcd( kx,ky ) = k*gcd( x,y ) 这么一个非常好的性质。试取 k=2,则有 gcd( 2x,2y ) = 2 * gcd( x,y )。很快联想到将两个偶数化小的方法,对两个正整数 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 ,就用这个。

流程图
在这里插入图片描述
代码

int Stein(int x,int y)
{
	int factor=0;
	int temp;
	if(x<y)
	{
		temp=x;
		x=y;
		y=temp;
	}
	if(y==0)
		return 0;   //0能被任何非0数整除
	while(x!=y)
	{
		if(x&0x1)    //0x表示16进制
		{
			if(y&0x1)   //x,y同为奇数
			{
				y=(x-y)>>1;
				x=x-y;
			}
			else
			{
				y>>=1;   //x为奇数,y为偶数
			}
		}
		else
		{
			if(y&0x1)   //x为偶数,y奇数
			{
				x>>=1;
				if(x<y)
				{
					temp=x;
					x=y;
					y=temp;
				}
			}
			else     //x,y同为偶数
			{
				x>>=1;
				y>>=1;
				++factor;
			}
		}
	}
	return (x<<factor);
}

源代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>     //pow(2,i)的头文件
#include<time.h>     //计算程序运行时间及随机产生数的头文件
//辗转相除法(1.函数嵌套调用)
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);         //返回最大公约数到调用函数处
}
//辗转相除法(2.函数递归调用)
int divisor2(int a,int b)
{
	if(a%b==0)     //若a,b取余后余数为0,则返回最大公约数b
		return b;
	else
		return divisor2(b,a%b); //否则递归调用,继续取余
}
//穷举法
int divisor3(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 divisor4(int m,int n)
{
	int i=0,temp,x;
	while(m%2==0&&n%2==0)  //判断a,b是否都是偶数,若是则用2约简
	{
		m/=2;
		n/=2;
		i+=1;  //约简2的个数
	}
	if(m<n)  //a保存最大值
	{
		temp=m;
		m=n;
		n=temp;
	}
	while(x)       //当x不为0时
	{
		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;
}
//Stein算法
int Stein(int x,int y)
{
	int factor=0;
	int temp;
	if(x<y)
	{
		temp=x;
		x=y;
		y=temp;
	}
	if(y==0)
		return 0;//0能被任何非0数整除
	while(x!=y)
	{
		if(x&0x1)//0x表示16进制
		{
			if(y&0x1)//x,y同为奇数
			{
				y=(x-y)>>1;
				x=x-y;
			}
			else
			{
				y>>=1;//x为奇数,y为偶数
			}
		}
		else
		{
			if(y&0x1)//x为偶数,y奇数
			{
				x>>=1;
				if(x<y)
				{
					temp=x;
					x=y;
					y=temp;
				}
			}
			else//x,y同为偶数
			{
				x>>=1;
				y>>=1;
				++factor;
			}
		}
	}
	return (x<<factor);
}
void main()
{
	int x,y,temp;
	int m,n,p,i,N,s[100];
	printf("--------------You have two options-------------\n");
	printf("       1.求两数最大公约数的操作\n");
	printf("       2.计算程序运行时间及随机数测试操作\n");
	printf("-----------------------------------------------\n");
	printf("Please input a number you want:\n ");
	scanf("%d",&n);
	if(n<1||n>2)
	{
		printf("error! Please input again!\n");
		scanf("%d",&n);
	}
	if(n==1)
	{  
		printf("请输入两个正整数(用逗号隔开):\n");
		scanf("%d,%d",&x,&y);
		printf("There are five ways to find the greatest common divisor,you can choose one:\n");
		printf("-----------------选择方法-------------------\n");
		printf("      1.辗转相除法(函数嵌套调用)     \n");
		printf("      2.辗转相除法(函数递归调用)     \n");
		printf("      3.穷举法                         \n");
		printf("      4.更相减损法                     \n");
		printf("      5.Stein算法                      \n");
		printf("--------------------------------------------\n");
        while(1)
		{
			printf("Please input the way of you choice:\n");
			scanf("%d",&m);
				switch(m)
				{
				case 1:
					printf("The way you choice is first(辗转相除法-函数嵌套调用)\n");
					temp=divisor1(x,y);
					printf("The greatest common divisor is %d\n",temp);
					break;
				case 2:
					printf("The way you choice is second(辗转相除法-函数递归调用)\n");
                    temp=divisor2(x,y);
                    printf("The greatest common divisor is %d\n",temp);
					break;
				case 3:
                    printf("The way you choice is third(穷举法)\n");
					temp=divisor3(x,y);
					printf("The greatest common divisor is %d\n",temp);
					break;
				case 4:
					printf("The way you choice is fourth(更相减损法 )\n");
					temp=divisor4(x,y);
					printf("The greatest common divisor is %d\n",temp);
					break;
				case 5:
					printf("The way you choice is fifth(Stein算法 )\n");
					temp=Stein(x,y);
					printf("The greatest common divisor is %d\n",temp);
					break;
				}
			}
	}
   else	if(n==2)
   {
		clock_t start,finish;               
		double T;
		srand((unsigned)time(NULL));
		printf("你想测试多少组数据:\n");
		scanf("%d",&N);
		for(i=0;i<N;i++)                     
		{
			s[i]=rand()%500+1;
			printf("%d  ",s[i]);
		}
		printf("\n");
		printf("There are five ways to find the greatest common divisor,you can choose one:\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("Please input the way of you choice:\n");
			scanf("%d",&p);
			while(p<1||p>5)
			{
				printf("输入错误!请重新输入:\n");
				scanf("%d",&p);
			}
			switch(p)
			{
			case 1:
				start=clock();                  //程序运行,开始计时
				while(j<N)
				{
					x=s[j++];
					y=s[j++];
					temp=divisor1(x,y);
					printf("The greatest common divisor is %d\n",temp);
		
				}
				finish=clock();                //程序运行结束,结束计时
				break;
			case 2:
				start=clock();
				while(j<N)
				{
					x=s[j++];
					y=s[j++];
	    			temp=divisor2(x,y);
                    printf("The greatest common divisor is %d\n",temp);
				}
				finish=clock();
				break;
			case 3:
				start=clock();
				while(j<N)
				{
					x=s[j++];
					y=s[j++];
					temp=divisor3(x,y);
					printf("The greatest common divisor is %d\n",temp);
				}
				finish=clock();
				break;
			case 4:
				start=clock();
				while(j<N)
				{	
					x=s[j++];
					y=s[j++];
			    	temp=divisor4(x,y);
					printf("The greatest common divisor is %d\n",temp);
				}
				finish=clock();
				break;
			case 5:
			start=clock();
			while(j<N)
			{
				x=s[j++];
				y=s[j++];
	    		temp=Stein(x,y);
				printf("The greatest common divisor is %d\n",temp);
			}
			finish=clock();
			break;
		}
			T=(double)(finish-start)/1000;    //计算时间差,由于计算机计算的是毫秒,转换成秒要除以1000
			printf("这个方法的运行时间是%f秒\n",T);
	}
}
}

调试、测试及运行结果

运行结果
运行结果
调试结果
在这里插入图片描述
测试运行时间
在这里插入图片描述

经验总结

遇到的问题
1.对于Stein算法自己没有理解清楚,看了程序也是似懂非懂,但想了一下觉得还是要弄明白,所以又在csdn上查了很多资料,也请教了身边的同学,对于这个算法是最后弄明白的。
2.对于在不同规模测试数据的情况下比较四种算法的运行时间,刚开始并没有想到用随机函数来产生数据,而是想手动输入,但这样的做法显然有局限性,对于随机函数也是参考了很多资料。
总结
四种算法相对来说,前两种方法比较简单,也容易理解,后两种算法确实需要思考、查资料,其中对于第三种更相减损法,我想的是:可不可以不用判断两数是否是偶数,直接在循环中大数减小数,直到两数相等。自己动手试了一下,这样确实也可以求出最大公约数,而且比较简单、容易理解。所以,通过这个例子,我觉得每一次的问题的答案都不应该是唯一的,我们应该勇于尝试新的方法。

  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值