知识储备:
1.“|”整除号
eg:若b可被a整除,或a整除b,则可记作a|b
如2|6,8|16
性质:
①a|b,b|c => a|c
②a|b,a|c => a|(b+c)=>a|(ma+mb) (m,n∈Z)
③a|b(a≠0) => |a|≤|b|
2.“≡”同余号
两个整数a,b,若它们除以整数m所得的余数相等,则称a,b对于模m同余
记作a≡b(mod m)
读作a同余于b模m,或读作a与b关于模m同余。
比如26≡14(mod 12)
设m是大于1的正整数,a,b是整数,如果m|(a-b),则称a与b关于模m同余,记作a≡b(mod m)
3.
一、欧几里得算法
欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数(gcd)。
基本算法:
设a=qb+r,其中a,b,q,r都是整数,则gcd(a,b)=gcd(b,r),即gcd(a,b)=gcd(b,a%b)
性质:
gcd(
am
a
m
-1,
an
a
n
-1) =
agcd(m,n)
a
g
c
d
(
m
,
n
)
-1
推广:
若 gcd(a,b)=1
gcd(
am
a
m
-
bm
b
m
,
an
a
n
-
bn
b
n
) =
agcd(m,n)
a
g
c
d
(
m
,
n
)
-
bgcd(m,n)
b
g
c
d
(
m
,
n
)
若gcd(a,b)=1
则 gcd(a+b,a)=1 gcd(a+b,b)=1 则 gcd(a+b,ab)=1
//代码实现
int gcd(int a,int b)
{
if(a<b)
swap(a,b);//优化加速
return b==0?a:gcd(b,a%b);
}
二、拓展欧几里得
基本算法:
对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数(可为负数)对 x,y ,使得 gcd(a,b)=ax+by。
这是一个不定方程(其实是一种丢番图方程),由于满足方程的解无穷多个,在实际的解题中一般都会去求解x或是y的最小正数的值。
这就用到了拓展欧几里得算法。
证明:设 a>b:
1.显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2.ab!=0 时,设
a
x1
x
1
+b
y1
y
1
=gcd(a,b);
b
x2
x
2
+(a mod b)
y2
y
2
=gcd(b,a mod b);
根据欧几里德原理gcd(a,b)=gcd(b,a mod b)
则:a
x1
x
1
+b
y1
y
1
=b
x2
x
2
+(a mod b)
y2
y
2
; ///a mod b =r
即:a
x1
x
1
+b
y1
y
1
=b
x2
x
2
+(a-
ab
a
b
b )
y2
y
2
=a
y2
y
2
+b
x2
x
2
-
ab
a
b
b
y2
y
2
;
根据恒等定理(对应系数相等)得:
x1
x
1
=
y2
y
2
;
y1
y
1
=
x2
x
2
-
ab
a
b
y2
y
2
;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
//拓展欧几里得递归实现
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int ans=exgcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-a/b*y;
return ans;//返回的是gcd(a,b)
}
//拓展欧几里得非递归实现
int exgcd(int m,int n,int &x,int &y)
{
int x1,int y1,x0,y0;
x0=1;
y0=0;
x1=0;
y1=1;
x=0;
y=1;
int ans=m%n;
int q=(m-r)/n;
while(ans)
{
x=x0-q*x1;
y=y0-q*y1;
x0=x1;
y0=y1;
x1=x;
y1=y;
m=n;
n=ans;
ans=m%n;
q=(m-ans)/n;
}
return n;
}