欧几里德
求整数a,b的最小公约数gcd(a,b)的算法。
递归代码:
#include <iostream>
using namespace std;
#define ll long long
ll gcd(ll a,ll b)
{
if(b==0)return a;
else return gcd(b,a%b);
}
int main()
{
ll a,b;
while(cin>>a>>b)
cout<<gcd(a,b)<<endl;
return 0;
}
/*
1 5
1
2 4
2
60 156
12
*/
扩展欧几里德
对于不完全为0的非负整数a,b,gcd(a, b)表示a, b的最大公约数,必定存在整数对x,y,满足a∗x+b∗y==gcd(a,b) ,有多解是一定的。
gcd(a,b)=gcd(b,a mod b) ①
a*x+b*y=gcd(a,b) ②
对①式:当b=0时,ax+by=gcd(a,b)=a,显然(x=1,y=0)是方程的一个解。
对②式:当b不为0时,根据欧几里得定理(1式)gcd(a,b)=gcd(b,a mod b)可得
x和y是gcd(a,b)=ax+by的解,而x′和y′是在对gcd(a,b)按欧几里德算法进行一步后的结果对应的贝祖等式gcd(b,a mod b)=bx'+(a mod b)y'的解.也就是说,gcd(a,b)对应的贝祖等式的解x,y可以由gcd(b,a mod b)对应等式的解x′,y′计算得出.
由于欧几里德算法最后一步为gcd(d,0)=d,此时对应的等式的解为x=1,y=0,因此只要如上述代码,从gcd(d,0)往前处理,在进行欧几里德算法的递归的时候根据相邻两次调用间x,y和x’,y’的关系计算即可求出ax+by=gcd(a,b)的解.
关系是:
x = y’
y = x’ – a/b*y’
更进一步,对于任意不定式ax′+by′=c,只需要在等式ax+by=gcd(a,b)=d两边乘上c/d即可得到解为x′=x∗c/d,y′=y∗c/d
扩展欧几里德代码:
int ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
b=0;
return a;
}
int ans=ex_gcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
扩展欧几里德有什么用处呢?
求解形如 a*x +b*y = c 的通解,但是一般没有谁会无聊到让你写出一串通解出来,都是让你在通解中选出一些特殊的解,比如一个数对于另一个数的乘法逆元.
什么叫乘法逆元?
这里,我们称 x 是 a 关于 m 的乘法逆元。
上式可以等价于这样的表达式: a*x + m*y = 1
如果a、m互质,有解,这个解的x就是a关于m的逆元,y就是b关于a的逆元
why?
两边同时求余m
a*x % m + m*y % m = 1 % m
a*x % m = 1 % m
a*x = 1 (mod m)
得出:x就是 a关于 m的 逆元, 同理两边同时求余 a --->m*y=1(mod a) ---> y就是m关于a的逆元
扩展欧几里得求乘法逆元必须保证 gcd(a, m) = 1
接着乘法逆元讲,一般,我们能够找到无数组解满足条件,但是一般是让你求解出最小的那组解,怎么做?我们求解出来了一个特殊的解 x0 那么,我们用 x0 % m其实就得到了最小的解了。为什么?
可以这样思考:
x 的通解不是 x0 + m*t 吗?
那么,也就是说, a 关于 m 的逆元是一个关于 m 同余的,那么根据最小整数原理,一定存在一个最小的正整数,它是 a 关于m 的逆元,而最小的肯定是在(0 , m)之间的,而且只有一个,这就好解释了。
由于问题的特殊性,有时候我们得到的特解 x0 是一个负数,还有的时候我们的 m 也是一个负数这怎么办?
当 m 是负数的时候,我们取 m 的绝对值就行了,当 x0 是负数的时候,他模上 m 的结果仍然是负数(在计算机计算的结果上是这样的,虽然定义的时候不是这样的),这时候,我们仍然让 x0 对abs(m) 取模,然后结果再加上abs(m) 就行了,于是,我们不难写出下面的代码求解一个数 a 对于另一个数 m 的乘法逆元:
int cal(int a,int m)
{
int x,y;
int gcd=ex_gcd(a,m,x,y);
if(1%gcd!=0) return -1;
x*=1/gcd;
m=abs(m);
int ans=x%m;
if(ans<=0) ans+=m;
return ans;
}