一.欧几里德
欧几里德是用来求最大公约数的算法
其算法的中心思想为:
设a,b的最大公约数为c,则c一定也是a%b的最大公约数
证明方法很多,下面列出最简单的一个:
令 :a = xc; b = yc ; a = bk + r
(1) 证明 c 为 r 的约数
$r = a - b*k = xc - ykc = c(x - ky) $
故 c 为 r 的一个约数,证毕
(2) 证明 c 为 b和r的最大公约数
因
b
=
y
∗
c
b = y*c
b=y∗c
r
=
(
x
−
k
y
)
∗
c
r = (x - ky)*c
r=(x−ky)∗c
故问题转化为 : 证明 y 和 (x - ky) 互质
我们可以使用反证法:
假设y 和 (x - ky)存在公因子 t
令
y
=
p
t
y = pt
y=pt
x
−
k
y
=
q
t
x - ky = qt
x−ky=qt
则 $x = ky + qt = kpt + qt = (kp + q) * t $
则说明 t 也是 x,y的公因子
但是
由初始定义:
a
=
x
∗
c
a = x*c
a=x∗c
b
=
y
∗
c
b = y*c
b=y∗c
若x,y存在公因子t,则 c*t 才为 a,b的最大公因子,则与初始条件 c 为 a,b的最大公因子矛盾。
故假设不成立。
即 y 和 (x - ky) 互质
因为
b = yc
r = (x-ky)*c
故 c 为 b,r的最大公约数,证毕
设gcd(a,b)为a,b,的最大公约数,则
==> gcd(a,b) = gcd(b,r) = gcd(b,a%b)
代码:
int gcd(int a,int b){
if(b == 0) return a;
return gcd(b,a%b);
}
二.拓展欧几里德
已知一个问题:
已知a,b,c均为整数 问对于ax + by = c来说是否存在整数解,如果存在则求出任意一组整数解
此问题我第一次是见于HDU的校赛,不过只是需要求解是否存在(原题大概是问一个三元一次方程ax + by + cz = d,在a,b,c,d确定的情况下存在多少组解)
此题正解是背包,而我是通过移项枚举z的值,然后快速判断二元一次方程是否存在解来判断方案数量。
但当时没学拓欧,就各种乱搞WA了8次,最后YY出了一个快速判断的公式,终于AC。
后来才知道,其实跟拓欧有着很大的联系。
回到开始的问题:
(一)对于判断是否有整数解,其实很简单,就判断一下:
c % gcd(a,b) == 0 是否成立
其实这是很容易想通的,令:
r
=
g
c
d
(
a
,
b
)
r = gcd(a,b)
r=gcd(a,b)
则无论如果改变x,y的大小,对于ax + by 每次改变的值一定是r的倍数,因为
(a+b) ,(a-b), a, b,a+2b,a - 2b…都是r的倍数
如果c 不是 r的倍数,则一定不存在整数解。
(二)至于某一组x,y的值:
则可以用拓展欧几里德来求解:
首先考虑特殊情况:
a
∗
1
+
b
∗
0
=
g
c
d
(
a
,
b
)
=
a
a*1 + b*0 = gcd(a,b) = a
a∗1+b∗0=gcd(a,b)=a
则我们可以考虑从已知的特殊情况推导到未知情况
由欧几里德定理,我们知道:
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)
故我们可以考虑下面这种情况,将 a , b a,b a,b分别用 b b b和 ( a % b ) (a\%b) (a%b)代替,得:
(1) b x + ( a % b ) y = g c d ( a , b ) bx + (a\%b)y = gcd(a,b)\tag1 bx+(a%b)y=gcd(a,b)(1)
这一定是一个满足有解条件的合法等式。
假设上式得
x
,
y
x,y
x,y我们已经知道值是多少了,我们试着将该式转化为这种形式:
a
x
′
+
b
y
′
=
g
c
d
(
a
,
b
)
ax' + by' = gcd(a,b)
ax′+by′=gcd(a,b)
若 x ′ , y ′ x',y' x′,y′能够用x,y进行表示,那我们就达到了从已知情况推导到未知情况的目的。
借用一个显然的条件来化简: a % b = a − ( a / b ) ∗ b a \% b = a - (a/b) * b% a%b=a−(a/b)∗b
代入(1)式化简得:
(2)
a
y
+
b
(
x
−
(
a
/
b
)
y
)
=
g
c
d
(
a
,
b
)
a y + b (x - (a/b)y) = gcd(a,b)\tag2
ay+b(x−(a/b)y)=gcd(a,b)(2)
故
x
′
=
y
x' = y
x′=y
y
′
=
x
−
(
a
/
b
)
∗
b
y' = x - (a/b)*b
y′=x−(a/b)∗b
所以为什么叫拓展欧几里德呢?因为该算法是脱胎于欧几里德算法。
我们要由已知情况推导到未知情况来解决问题
推导方法就是利用欧几里德算法用a,b的变化来带动x,y的变化。
代码1:
int exgcd(int a,int b,int &x,int &y){
int d = a;
if(b != 0){
d = exgcd(b,a%b,y,x);
y -= (a/b)*x;
}
else{
x = 1,y = 0;
}
return d;
}
代码2:(更易理解版)
int exgcd(int a,int b,int &x,int &y){
if(b == 0){ //已知的特殊情况
x = 1,y = 0;
return a;
}
int d = exgcd(b,a%b,x,y); //通过变化a,b一直往下递归,直到达到已知的特殊情况,然后返回
int t = x;
x = y,y = t - (a/b)*y; //代入公式。
return d;
}
(三)求最小整数解or解的间隔:
先上结论,解的间隔:
d
y
=
a
/
g
c
d
(
a
,
b
)
dy = a/gcd(a,b)
dy=a/gcd(a,b)
d
x
=
b
/
g
c
d
(
a
,
b
)
dx = b/gcd(a,b)
dx=b/gcd(a,b)
而
x
x
x的最小整数解为:
(
x
0
%
d
x
+
d
x
)
%
d
x
(x_0 \% dx + dx) \% dx
(x0%dx+dx)%dx (考虑负模的情况)
证明详见:证明链接
int solve(int a,int b,int c){
int x,y;
int d = exgcd(a,b,x,y);
if(c%d) return -1; //无解
x*=c/d; //解出的x满足 ax+by = gcd(a,b)
//而我们要的是 ax'+by' = c
//因c 是 gcd(a,b)的倍数,等式两端同时乘上c/gcd(a,b),x'即为所求。
b/=d;b=abs(b); //dx = b/d,考虑负模的情况。
return (x%b+b)%b; //返回最小正整数解
}
经典题
求
(
A
/
B
)
m
o
d
  
m
(A/B)\mod m
(A/B)modm
最直接的做法可以转化为:
A
m
o
d
  
(
B
∗
m
)
/
B
A \mod (B*m) / B
Amod(B∗m)/B
但当B,m均很大时B*m或许会溢出(当然会Java就不怕啦= =),
或像该题不直接把A告诉你时,我们可以利用拓欧来解决该问题。
推导:
设
(
A
/
B
)
%
9973
=
x
(A/B)\% 9973 = x
(A/B)%9973=x
则
(
A
/
B
)
=
9973
∗
k
+
x
(
k
为
整
数
)
(A/B) = 9973*k + x (k为整数)
(A/B)=9973∗k+x(k为整数)
所以
A
=
9973
∗
k
∗
B
+
x
∗
B
A = 9973*k*B + x*B
A=9973∗k∗B+x∗B
因为
A
%
9973
=
n
A \% 9973 = n
A%9973=n
故
(
x
∗
B
)
%
9973
=
n
(x * B) \% 9973 = n
(x∗B)%9973=n
所以
B
∗
x
=
9973
∗
k
+
n
B*x = 9973*k + n
B∗x=9973∗k+n
B
∗
x
−
9973
k
=
n
B*x - 9973k = n
B∗x−9973k=n
此时$ x,k$为未知数,且
g
c
d
(
B
,
9973
)
=
=
1
gcd(B,9973) == 1
gcd(B,9973)==1(题目条件)故一定有解
这样便转化成了 拓展欧几里德的水题
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int exgcd(int a,int b,int &x,int &y){
if(b == 0){
x = 1,y = 0;
return a;
}
int d = exgcd(b,a%b,x,y);
int t = x;
x = y,y = t - (a/b)*y;
return d;
}
int solve(int a,int b,int c){
int x,y;
exgcd(a,b,x,y);
return ((x*c)%b + b) % b;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,B;
scanf("%d%d",&n,&B);
int ans = solve(B,9973,n);
printf("%d\n",ans);
}
return 0;
}
本文详细介绍了欧几里德算法及其拓展欧几里德算法,用于求解最大公约数和判断、求解整数线性同余方程的解。通过证明和例子阐述了算法原理,并提供了相关代码实现。同时,讨论了如何运用这些算法解决实际的数论问题,如求模运算和大规模数的处理。

1377

被折叠的 条评论
为什么被折叠?



