复习:求最大公约数算法
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
首先介绍扩展欧几里得定理:
对于两个不全为0的整数a,b,必存在一组解x,y,使得ax+by=gcd(a,b)。
换句话说,形如ax+by的最小正整数等于gcd(a,b)。
实现代码如下:(一般题目都要用64位)
(复杂度:O(log max(a,b)))
typedef long long LL;
///求整数x和y,使得ax+by=gcd(a,b),且|x|+|y|最小(|x|<=b,|y|<=a)
///注意:即使a,b在int范围内,中间计算过程可能超出int范围
void exgcd(LL a, LL b, LL& d, LL& x, LL& y)
{
if (!b) d = a, x = 1, y = 0;///此时ax+by=ax,gcd(a,b)=gcd(a,0)=a,故ax=a,所以x=1,y可以取任意整数,这里为计算方便取y=0,你也可以取y=1,同样可以得到一组不同的解!
else exgcd(b, a % b, d, y, x), y -= x * (a / b);///注意后面这一步这么写是由于前面的递归使得x和y交换了位置
/*
上面公式的推导过程:
设x,y表示第一次递归时的值,x',y'表示第二次递归时的值
由于
gcd(a,b)=gcd(b,a%b)
所以
ax+by=gcd(a,b)等价于bx'+(a%b)y'=gcd(b,a%b)=gcd(a,b)
即
bx'+(a-(a/b)*b)y'=gcd(a,b)
变形整理得
ay'+b*(x'-(a/b)*y')=gcd(a,b)
所以
x=y',y=x'-(a/b)*y'
*/
}
至于一般情况:ax+by=c,gcd(a,b)|c 的解,请参考《数论概论》P24-25
这里补充下为何通解是x=x0+kb,y=y0-ka(当然你也可以写成x=x0-kb,y=y0+ka):
首先把a化为a/gcd(a,b),b化为b/gcd(a,b),这样ax+by=1,gcd(a,b)=1(注意这里a,b实际上写作a'和b',为简单起见省掉撇号)
由于
ax+by = ax0+by0
所以
a(x-x0) = -b(y-y0) = P
所以
a|P,b|P
因为a,b互素,所以
ab|P
即
P=kab
所以
x-x0=kb,y-y0=-ka
整理得到
x=x0+kb,y=y0-ka
变形1:同余方程
扩展欧几里得算法除了解决线性不定方程ax+by=c之外,
还可以求解同余方程a≡c(mod m),b≡c(mod n)
将其化为a=c+mx,b=c+ny,作差得
mx-ny=a-b,从而化为线性不定方程解决
变形2:模m乘法逆元
定义:对于整数a,m,如果存在整数b,满足ab ≡ 1(mod m),则说,b是a的模m乘法逆元。
定理:a存在模m的乘法逆元的充要条件是gcd(a,m) = 1
充分性:
因为
gcd(a,m) = 1
根据欧拉定理,有
a^φ(m) ≡ 1(mod m)
因此
a * a^(φ(m)-1) mod m = 1
所以存在a的模m乘法逆元,即a^(φ(m)-1)
必要性:
假设存在a模m的乘法逆元为b,则
ab ≡ 1 (mod m)
所以
ab = km +1
所以
1 = ab - km
由欧几里得定理,有
gcd(a,m) = 1
由定理知:
对于ax + by = 1,可以看出x是a模b的乘法逆元,y是b模a的乘法逆元。
反过来,要计算a模b的乘法逆元,就相当于求ax + by = 1的x的最小正整数解,从而化为线性不定方程解决。
(当然,你也可以求a^(φ(b)-1) mod b,但即使我们用φ函数公式和逐次平方法(复杂度也是O(log b)),由于取模的次数远大于扩展欧几里得算法和代码量略大,所以此法不实用)