求两个数的最大公约数与最小公倍数
对于求两个数的最小公倍数与最大公约数,最大公约数等于两数相乘再除以最大公约数,所以问题的核心就是怎么求最大公约数。我所知道的有四种方法:穷举法(不推荐使用),辗转相除法(使用最多),更相减损术,Stein算法(密码领域用的多)
穷举法(暴力搜索)
穷举法就不多解释了直接给代码
int GCD1(int a,int b)//暴力搜索
{
int gcd=(a>b?b:a);
for(int i=gcd; i>0; i--)
{
if(a%i==0&&b%i==0)
{
gcd=i;
break;
}
}
return gcd;
}
辗转相除法(欧几里得算法)
辗转相除法分如下两部步:
- 较大数a对教小数b进行取余运算得到余数c;
- 若余数为零,则较小数为最大公约数;否则令a=b,b=c重新执行第一步
递归代码如下(要求a,b按大小输入):
int GCD2(int a,int b)//辗转相除
{
return b==0?a:GCD2(b,a%b);
}
非递归代码如下:
int GCD2(int a,int b)//辗转相除
{
int big,small;
if(a>b)
{
big=a;
small=b;
}
else
{
big=b;
small=a;
}
for(int i=big%small; i>0; i=big%small)
{
big=small;
small=i;
}
return small;
}
更相减损术
更相减损术用的是减法而不是除法,具体步骤如下:
- 两数若都为偶数则两数一直都除以2到不都为偶数,并记录除以了几个2;
- 较大数a减较小数b得到差值c;
- 若c与b相等,则c为最大公约数,否则令a=max(b,c),b=min(b,c)再执行第二步;
代码如下:
int GCD3(int a,int b)//更相减损术
{
int mul=1;
int big,small;
if(a>b)
{
big=a;
small=b;
}
else
{
big=b;
small=a;
}
while(1)
{
if(big%2==0&&small%2==0)
{
mul*=2;
big/=2;
small/=2;
}
else break;
}
for(int i=big; i!=small; i=big-small)
{
if(i>small)
{
big=i;
}
else
{
big=small;
small=i;
}
}
return small*mul;
}
Stein算法
欧几里得算法与更相减损术的缺陷
欧几里德算法是计算两个数最大公约数的传统算法,无论从理论还是从实际效率上都是很好的。但是却有一个致命的缺陷,这个缺陷在素数比较小的时候一般是感觉不到的,只有在大素数时才会显现出来。
更相减损术在两数相差较大时,会进行多次无意义运算,两数相差越大,无意义运算越多,效率过低。
一般实际应用中的整数很少会超过64位(当然已经允许128位了),对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。于是便有了Stein算法。
算法的思想
由J. Stein 1961年提出的Stein算法很好的解决了欧几里德算法中的这个缺陷,Stein算法只有整数的移位和加减法,为了说明Stein算法的正确性,首先必须注意到以下结论:
- gcd(a,a)=a,也就是一个数和其自身的公约数仍是其自身。
- gcd(ka,kb)=k gcd(a,b),也就是最大公约数运算和倍乘运算可以交换。特殊地,当k=2时,说明两个偶数的最大公约数必然能被2整除。
- 当k与b互为质数,gcd(ka,b)=gcd(a,b),也就是约掉两个数中只有其中一个含有的因子不影响最大公约数。特殊地,当k=2时,说明计算一个偶数和一个奇数的最大公约数时,可以先将偶数除以2。
算法的步骤
- 两数若都为偶数则两数一直都除以2到不都为偶数,并记录除以了几个2;
- 若两数中还有非偶数,将其除以2至奇数
- c=|数a-数b|,d=min(数a,数b),
- 若c为0则d为最大公约数,否则令a=c,b=d重复第二步
代码如下:
int GCD4(int a,int b)//Stein
{
int mul=1;
while(1)
{
if(a%2==0&&b%2==0)
{
a>>=1;
b>>=1;
mul<<=1;
}
else if(a%2==0)
{
a>>=1;
}
else if(b%2==0)
{
b>>=1;
}
else break;
}
while(a>0)
{
while(a%2==0)
{
a>>=1;
}
int abs=fabs(a-b);
int small=a>b?b:a;
a=abs;
b=small;
}
return b*mul;
}
c++代码
具体实现代码及效果图:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int GCD1(int a,int b);//暴力搜索
int GCD2(int a,int b);//辗转相除
int GCD3(int a,int b);//更相减损术
int GCD4(int a,int b);//Stein算法
int main()
{
int a=0,b=0;
int gcd[4]= {1,1,1,1}; //最大公约数:greatest common divisor(gcd)
int lcm[4]= {1,1,1,1}; //最小公倍数:least common multiple(lcm)
while(cin>>a>>b)
{
gcd[0]=GCD1(a,b);
gcd[1]=GCD2(a,b);
gcd[2]=GCD3(a,b);
gcd[3]=GCD4(a,b);
for(int i=0; i<4; i++)
{
lcm[i]=a*(b/gcd[i]);//先算除法防止越界
cout<<gcd[i]<<" "<<lcm[i]<<endl;
}
}
return 0;
}
萌新一个,若代码有错误或算法思想有问题还望多多斧正;