概念
- 公约数:有 k 个非零整数 ,若 ,使得能被d整除,则称 d 为 的公约数。
- 最大公约数:公约数中的最大值称为最大公约数,记为:。
- 公倍数:有 k 个非零整数 ,若 ,使得d能被整除,则称 d 为 的公倍数。
- 最小公倍数:公倍数中的最小值称为最小公倍数,记为:。
最大公约数一定是存在的,其最小值为 1;当 其值为1 时,则数互质。
公约数一定是最大公约数的约数,公倍数一定是最小公倍数的倍数。
最大公约数
1.辗转相除法/欧几里得算法
原理:,即:。
int gcd(int x, int y)
{
if(x%y == 0)
return y;
else return (y, x%y);
}
int gcd(int a, int b)
{
int r = a%b;
while(r)
{
a = b;
b = r;
r = a%b;
}
return b;
}
2.二进制欧几里得算法
为提高效率,可将欧几里德算法进行优化,通过不断去除因子 2 来降低常数。
- 当 时,
- 当 x、y 均为偶数时,
- 当 x 为偶数,y 为奇数时,
- 当 x 为奇数,x 为偶数时,
int GCD(int x, int y)
{
if(x == 0)
return y;
if(y == 0)
return x;
for(int i=0; !(x&1); i++) //约去2
x >>= 1;
for(int j=0; !(y&1); j++) //约去2
y >>= 1;
if(i>j)
i = j;
while(true)
{
if(x < y) //若x<y,交换两数,保证大数在前
{
x ^= y;
y ^= x;
x ^= y;
}
if((x-=y) == 0)//辗转减
return y<<i;
while(!(x&1))//约去2
x >>= 1;
}
}
3.拓展欧几里得
在已知 x、y 时,求解一组 a、b,使得 .
设
① 当 时,,此时
② 当 x>y>0 时
设 ,
由于
故
即
因此
易得
这样就得到了求解 、 的方法:、 的值基于 、
由于 GCD 不断的递归求解,因此一定会在某时 ,结束递归,从而得出 a、b 的值。
x - [x/y]*y 即为 mod 运算,[x/y] 代表取小于 x/y 的最大整数。
(来源:https://blog.csdn.net/u011815404/article/details/81284222)
int Extended_gcd(int x, int y, int &a, int &b)
{
if(y == 0)
{
a = 1;
b = 0;
return x;
}
int gcd = Extended_gcd(b, a%b, y, x);
b -= a*(x/y);
return gcd;
}
int main()
{
//形如 ax+by=GCD(x,y)
int x, y, a, b;
cin>>x>>y;
int gcd = Extended_gcd(x,y,a,b);
cout<<"GCD="<<gcd<<endl;
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
4.内置
c++内置了最大公约数的计算函数:__gcd(),存在于头文件algorithm中。
以下源码来自stl_algo.h文件:
/**
* This is a helper function for the rotate algorithm specialized on RAIs.
* It returns the greatest common divisor of two integer values.
*/
template<typename _EuclideanRingElement>
_EuclideanRingElement
__gcd(_EuclideanRingElement __m, _EuclideanRingElement __n)
{
while (__n != 0)
{
_EuclideanRingElement __t = __m % __n;
__m = __n;
__n = __t;
}
return __m;
}
调用方法:
5.辗转相减法/更相减损术
以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
对于0和负数要特判,否则死循环。
int gcd(int x, int y)
{
if(x == y) return x;
return x>y ? gcd(x-y, y) : gcd(y-x, x);
}
int gcd(int x, int y)
{
while(x != y)
{
if(x>y)
x -= y;
else y -= x;
}
return x;
}
6.辗转相除法与辗转相减法的比较
更相减损术和辗转相除法的主要区别在于前者所使用的运算是“减”,后者是“除”。从算法思想上看,两者并没有本质上的区别,但是在计算过程中,如果遇到一个数很大,另一个数比较小的情况,可能要进行很多次减法才能达到一次除法的效果,从而使得算法的时间复杂度退化为O(N),其中N是原先的两个数中较大的一个。相比之下,辗转相除法的时间复杂度稳定于O(logN)。
最小公倍数
定理:a、b 两个数的最小公倍数乘以它们的最大公约数等于 a 和 b 本身的乘积。
即:
在求 LCM 时,如果将 LCM 写成 a*b/GCD(a,b),a*b 可能会溢出,可以先除后乘,即:a/GCD(a,b)*b。
int gcd(int x, int y)
{
return y ? gcd(y, x%y) : x;
}
int lcm(int x, int y)
{
return x/gcd(x, y)*y;
}
性质
gcd(a , b) = gcd(b , a-b)
gcd(m*a , m*b) = m*gcd(a , b), m为一个自然数
gcd(a+m*b , b) = gcd(a , b)
m=gcd(a , b) 则gcd(a/m,b/m)=gcd(a,b)/m
gcd(a, lcm(b, c)) = lcm(gcd(a, b), gcd(a, c))
lcm(a, gcd(b, c)) = gcd(lcm(a, b), lcm(a, c))