关于求最大公约数的几种常见算法及其运算效率的比较

一 题目分析
求两数的最大公约数有多种,以下介绍4种常用算法。
1.辗转相除法
辗转相除法(又名欧几里德法)C语言中用于计算两个正整数a,b的最大公约数和最小公倍数,实质它依赖于下面的定理:
gcd(a,b) =

a ( b=0)

gcd(b,a mod b) (b!=0)

根据这一定理可以采用函数嵌套调用和递归调用形式进行求两个数的最大公约数和最小公倍数,现分别叙述如下:
①函数嵌套调用
其算法过程为: 前提:设两数为a,b设其中a 做被除数,b做除数,temp为余数
1、大数放a中、小数放b中;
2、求a/b的余数;
3、若temp=0则b为最大公约数;
4、如果temp!=0则把b的值给a、temp的值给a;
5、返回第二步;
启示:请注意算法中变量数值之间的相互交换方法、如何取模、怎样进行自定义函数及主调函数与被调函数间的相互关系,函数参数的定义及对应关系特点,利用控制语句如何实现。
②函数递归调用
int gcd (int a,int b)
{ if(a%b==0)
return b;
else
return gcd(b,a%b);
}
#include “stdio.h”
main()
{
int m,n,t1;
printf(“please input two integer number:”);
scanf("%d%d",&m,&n);
t1=gcd(m,n);
printf(“The highest common divisor is %d\n”,t1);/最大公约数/
printf(“The least common multiple is %d\n”,mn/t1);/最小公倍数/
getch();
}
启示:采用递归调用法要注意递归终止条件的描述,只有找到递归变化的规律,才能有效地解决问题。
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,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种算法的程序流程图及N-S盒图
1辗转相除法
程序流程图:
在这里插入图片描述

2定义法
程序流程图
在这里插入图片描述

3更相减损法
程序流程图
在这里插入图片描述

4 Stein算法
程序流程图
在这里插入图片描述

三 源代码
#include
#include
#include
using namespace std;
class solution1//辗转相除法
{
public:
int divisor(int,int);
int multiple(int,int);//嵌套调用
int gcd(int,int);//递归调用
int gcd_mul(int,int);
};
class solution2//穷举法
{
public:
int divisor(int,int);
int multiple(int,int);
};
class solution3 //更相减损法
{
public:
int gcd(int,int);

};
class solution4 //Stein算法
{
public:
int stein(unsigned int, unsigned int);
int gcd(int,int);
};
void compare(int);
int elem1[60] = { 12,13,15,20,25, 32,33,35,36,38, 48,49,50,52,54, 61,62,63,65,68, 5594,4656,3086,2031,7960, 5684,1284,5674,9498,5843, 3456,2375,7486,4342,5647, 4484,2456,8164,2897,7780, 345767,543623,651372,794762,836443, 573463,768445,347615,677389,433322, 542863,876319,235341,577363,427635, 413624,657545,342152,778486,691280 };
int elem2[60] = { 39,40,41,42,46, 48,49,50,52,54, 55,56,58,59,60, 89,42,99,54,66, 1125,1233,1445,6594,5523, 5692,1383,5464,8372,5338, 3371,7645,4183,7583,9878, 4432,5624,7777,6543,8278, 112221,224361,4454274,555566,337274, 453662,553255,337145,677854,919790, 667644,672854,243589,653283,727780, 334316,516674,571189,432521,995306 };
void main()
{
int x;
cout << “输入运算个数(20,40,60)”<<endl;
cin >> x;
compare(x);
system(“pause”);
}
void compare(int x)
{
solution1 way1;
solution2 way2;
solution3 way3;
solution4 way4;
clock_t start, finish, t1, t2, t3, t4, t5, t6;
//测试辗转相除法求最大公约数嵌套调用时间1
start = clock();//插入要测试的程序段
for (int i = 0;i < x;i++)
{
way1.divisor(elem1[i], elem2[i]);
cout << elem1[i] << " " << elem2[i] << “最大公约数” << way1.divisor(elem1[i], elem2[i]) << endl;
}
finish = clock();
t1 = finish - start;
cout << “求两位数以内的最大公约数20次用时:\n辗转相除法(嵌套调用):” << finish - start << “/” << CLOCKS_PER_SEC << " (s) " << endl;
//测试辗转相除法递归调用时间1
start = clock();//插入要测试的程序段
for (int i = 0;i < x;i++)
{
cout << elem1[i] << " " << elem2[i] << “最大公约数为:” << way1.gcd(elem1[i], elem2[i]) << endl;
}
finish = clock();
t2 = finish - start;
cout << “辗转相除法(递归调用):” << finish - start << “/” << CLOCKS_PER_SEC << " (s) " << endl;
//测试穷举法20次运行时间
start = clock();//插入要测试的程序段
for (int i = 0;i < x;i++)
{
cout << elem1[i] << " " << elem2[i] << “最大公约数为:” << way2.divisor(elem1[i], elem2[i]) << endl;
}
finish = clock();
t3 = finish - start;
cout << “穷举法:” << finish - start << “/” << CLOCKS_PER_SEC << " (s) " << endl;
//更相减损法
start = clock();//插入要测试的程序段
for (int i = 0;i < x;i++)
{
cout << elem1[i] << " " << elem2[i] << “最大公约数为:” << way3.gcd(elem1[i], elem2[i]) << endl;;
}
finish = clock();
t4 = finish - start;
cout << “更相减损法:” << finish - start << “/” << CLOCKS_PER_SEC << " (s) " << endl;
//Stein算法
start = clock();//插入要测试的程序段
for (int i = 0;i < x;i++)
{
cout << elem1[i] << " " << elem2[i] << “最大公约数为:” << way4.stein(elem1[i], elem2[i]) << endl;;
}
finish = clock();
t5 = finish - start;
cout << “Stein算法:” << finish - start << “/” << CLOCKS_PER_SEC << " (s) " << endl;
//Stein算法(递归调用)
for (int i = 0;i < x;i++)
{
cout << elem1[i] << " " << elem2[i] << “最大公约数为:” << way4.gcd(elem1[i], elem2[i]) << endl;;
}
finish = clock();
t6 = finish - start;
cout << “Stein算法(递归调用):” << finish - start << “/” << CLOCKS_PER_SEC << " (s) " << endl;
if (x == 20)
{
cout << “两位数以内各算法运行20次时间如下:” << endl;
cout << “辗转相除法(嵌套调用):” << t1 << endl;
cout << “辗转相除法(递归调用):” << t2 << endl;
cout << " 穷举法:" << t3 << endl;
cout << “更相减损法:” << t4 << endl;
cout << “Stein算法:” << t5 << endl;
cout<<“Stein算法(递归调用):” << t6 << " " << endl;
}
else if (x == 40)
{
cout << “四位数以内各算法运行40次时间如下:” << endl;
cout << “辗转相除法(嵌套调用):” << t1 << endl;
cout << “辗转相除法(递归调用):” << t2 << endl;
cout << " 穷举法:" << t3 << endl;
cout << “更相减损法:” << t4 << endl;
cout << “Stein算法:” << t5 << endl;
cout << “Stein算法(递归调用):” << t6 << " " << endl;
}
else
{
cout << “六位数以内各算法运行60次时间如下:” << endl;
cout << “辗转相除法(嵌套调用):” << t1 << endl;
cout << “辗转相除法(递归调用):” << t2 << endl;
cout << " 穷举法:" << t3 << endl;
cout << “更相减损法:” << t4 << endl;
cout << “Stein算法:” << t5 << endl;
cout << “Stein算法(递归调用):” << t6 << " " << endl;
}
}
int solution1::divisor(int a,int b)
{
int temp;
if (a < b)
{
temp = a;
a = b;
b = temp;
}
while (b != 0)
{
temp = a % b;
a = b;
b = temp;
}
return a;
}
int solution1::multiple(int a, int b)
{
int temp;
temp = divisor(a,b);
return (a * b / temp);
}
int solution1::gcd(int a, int b)
{
if (a%b == 0)
return b;
else
return gcd(b,a%b);
}
int solution1::gcd_mul(int a, int b)
{
int temp = gcd(a,b);
return a * b / temp;
}
int solution2::divisor(int a, int b)
{
int temp;
temp = (a < b) ? a : b;
while (temp>0)
{
if (a%temp == 0 &&b%temp == 0)
break;
temp–;
}
return temp;
}
int solution2::multiple(int a, int b)
{
int p,q,temp;
p = (a > b) ? a : b;
q = (a < b) ? a : b;
temp = p;
while(1)
{
if (p%q==0)
break;
else
p += temp;
}
return p;
}
int solution3::gcd(int m, int n)
{
int i = 0, temp, x=1
;
while (m % 2 == 0 && n % 2 == 0)
{
m /= 2;
n /= 2;
i+=1;
}
if (m < n)
{
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 solution4::stein(unsigned int x,unsigned int y)
{
int factor = 0,temp;
if (x < y)
{
temp = x;
x = y;
y = temp;
}
if (y == 0) return 0;
while (x!=y)
{
if (x & 0x1)
{
if (y & 0x1)
{
y = (x - y) >> 1;
x -= y;
}
else
y >>= 1;
}
else
{
if (y & 0x1)
{
x >>= 1;
if (x < y)
{
temp = x;
x = y;
y = temp;
}
}
else
{
x >>= 1;
y >>= 1;
++factor;
}
}
}
return x << factor;
}
int solution4::gcd(int u,int v)
{
if (u == 0) return v;
if (v == 0) return u;
if (~u & 1)
{
if (v & 1)
return gcd(u >> 1, v);
else
return gcd(u>>1,v>>1)<<1;
}
if (~v & 1)
return gcd(u, v >> 1);
if (u > v)
return gcd((u-v)>>1,v);
return gcd((v-u)>>1,u);
}

四 调试、测试及运行结果
在main()函数中分别测试了个函数块功能,调试代码如下
solution1 way1;
solution2 way2;
solution3 way3;
solution4 way4;
clock_t start, finish, t1, t2, t3, t4, t5, t6;
cout << “辗转相除法(嵌套):” << way1.divisor(45, 65) << endl;
cout << “辗转相除法(递归):” << way1.gcd(45, 65) << endl;
cout << “定义法:” << way2.divisor(45, 65) << endl;
cout << “更相减损法:” << way3.gcd(45, 65) << endl;
cout << “Stein:” << way4.stein(45, 65) << endl;
cout << “Stein算法(递归):” << way4.gcd(45, 65) << endl;
cout << “最小公倍数:” << way2.multiple(45, 65) << endl;
int x;
compare(x);
运行结果如下:在这里插入图片描述
compare()函数运行结果:
在这里插入图片描述
程序运行结果如下:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
五 经验归纳
本次试验对最大公约数算法进行了一次详细的了解与运算速度的测试,收获之一是对c++结构化程序设计有了进一步理解,一开始我是在主函数里进行时间测试的,由于分了3批数据进行测试,相同代码段很多,整个main()函数看起来就很长且冗杂,封装进一个compare()函数后整个主函数看起来就很精炼简洁且结构明了;收获之二是对c++类有了更深的理解,程序要测试多组数据但多次输入未免有点麻烦于是我想不如吧数据封装在类里边让函数自己去调用,但后来发现一个问题就是类里边的函数是直接需要参数而不是自己去调用的,而给定函数和类里边数据名称的形参是会报错的,后来我又想吧数据定义成静态成员变量再定义一个成员变量去取出数据但又感觉程序有点复杂化了,最终走了把数据定义成全局变量这么一条路,至此我对类的私有成员变量有了多一层理接。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值