以下提到的数都是整数。
欧几里德算法
欧几里德算法用于求解最大公倍数,也就是辗转相除法。其结论非常简洁,对任意整数
a
a
a、
b
b
b,有:
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
gcd(a,b)=gcd(b,a\%b)
gcd(a,b)=gcd(b,a%b)
其中,%代表取模运算,也就是C++语言中的运算符。因此,欧几里德算法实现起来也非常简单。
typedef long long int llt;
llt gcd(llt a,llt b){
while( b ){
llt r = b;
b = a % b;
a = r;
}
return a;
}
递归实现更加简洁。
typedef long long int llt;
llt gcd(llt a,llt b){
return b ? gcd(b,a%b) : a;
}
实际使用时,只需注意 a a a、 b b b取负值的情况,此时gcd有可能计算出一个负数。
扩展的欧几里德算法
扩展的欧几里德算法是指对任意整数
a
a
a、
b
b
b,必然存在整数对
x
x
x、
y
y
y,使得:
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)
这里注意一条,
x
x
x、
y
y
y不是唯一的,存在无穷多对数满足上述等式。考虑
a
=
8
,
b
=
12
a=8, b=12
a=8,b=12的情况,则有:
8
⋅
(
−
1
)
+
12
⋅
1
=
4
8\cdot(-1)+12\cdot1=4
8⋅(−1)+12⋅1=4
8
⋅
(
−
4
)
+
12
⋅
3
=
4
8\cdot(-4)+12\cdot3=4
8⋅(−4)+12⋅3=4
⋯
\cdots
⋯
扩展的欧几里德算法使用递归实现也非常简单:
llt exEuclid(llt a,llt b,llt&x,llt&y){
if ( 0 == b ){
return x=1,y=0,a;
}
llt r = exEuclid(b,a%b,x,y);
llt t = x;
x = y, y = t - a/b*y;
return r;
}
迭代实现稍微复杂一点点。
typedef long long int llt;
llt exEuclid(llt a,llt b,llt&x,llt&y){
llt x0 = 1, y0 = 0;
llt x1 = 0, y1 = 1;
x = 0; y = 1;
llt r = a % b;
llt q = ( a - r ) / b;
while( r ){
x = x0 - q * x1;
y = y0 - q * y1;
x0 = x1; y0 = y1;
x1 = x; y1 = y;
a = b; b = r; r = a % b;
q = ( a - r ) / b;
}
return b;
}
乘法逆元
使用扩展的欧几里德算法可以很方便的求出乘法逆元。如果
a
a
a、
b
b
b的乘积对
p
p
p的余数为1,则称
b
b
b在模
p
p
p的意义下是
a
a
a的逆元。显然逆元是相互的。对任意非零
a
a
a当且仅当
a
,
p
a,p
a,p互质,
a
a
a的逆元存在。
已知
a
,
p
a,\,p
a,p,求
x
x
x,使得
a
⋅
x
≡
1
(
m
o
d
p
)
a\cdot{x}\equiv1({\rm{mod}}\,p)
a⋅x≡1(modp)成立。
原方程等价于
a
x
+
p
y
=
1
ax+py=1
ax+py=1,如果
a
,
p
a,p
a,p互质,则可以使用扩展的欧几里德算法求出
x
x
x。
//returns the inverse of a mod p satisfied with 1 == ax%p
//it will be success only when a and p are co-prime
inline llt inv(llt a,llt p){
llt x,y;
llt r = exEuclid(a,p,x,y);
if ( r != 1 ) return 0;
x = x % p;
if ( x < 0 ) x += p;
return x;
}