序言:
当博主第一次见到欧几里德算法时,我是不屑一顾的,由于模板比较好背,所以也没有仔细研究过其中的数学原理.这段时间突然喜欢上了数学,碰巧同学讲了一下基础数论,就去听了一听. 由于博主数学基础和学习能力都比较差,没有立即消化其中的知识,于是研究了好几天,直到今天才有所进展,通过这篇博客希望大家能够认识到数学的精妙之处.
正文:
欧几里德算法的推导与证明:
众所周知,欧几里德算法的定理可以表示为:
gcd(a,b)=gcd(b,a%b)
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
但是这个定理是如何推导出来的呢?
首先我们可以假设 a=k∗b+r a = k ∗ b + r (其中的r代表着a/b的余数,也就是a%b的结果)
1.首先我们假设d是a和b的最大公约数,则我们可以知道 a可以整除d,表示为a|b,b也可以整除d,表示为b|d,由于 r=a−k∗b r = a − k ∗ b 其中a是d的倍数,而且b也是d的倍数,那我们可以得到其实r也是d的倍数,换句话说r也是可以整除d.
2.接下来我们假设d是b和r的最大公约数,则我们可以知道b可以整除d,表示为b|d,r也可以整除d,表示为r|d,由于 a=k∗b+r a = k ∗ b + r 其中b是d的倍数,而且r也是d的倍数,那我们可以得知其实a也是d的倍数,换句话说a也是可以整除d.
通过以上的充分必要式的证明方法,我们可以得到: gcd(a,b)=gcd(b,a%b) g c d ( a , b ) = g c d ( b , a % b )
接下来我们来看代码实现(递归) :
int gcd(int a, int b)
{
if(b==0)
return a;
else
return gcd(b,a%b);
}
看起来十分简单,实则暗藏玄机…
首先我们需要考虑当
b=0
b
=
0
时,为何要返回a呢.. 在解释之前,我要说明一点,只有当a > b时 算法才算正式开始进行运算,这是为什么呢?我们举个简单的例子,当
a=12,b=16
a
=
12
,
b
=
16
时,我们在第二轮的时候惊奇的发现他递归的却是
gcd(16,12)
g
c
d
(
16
,
12
)
,这其实算是一种维护的过程,感叹第一写出这份代码人心思的缜密….回归正题,当我们发现
b=0
b
=
0
时,也就是说上一轮的
a%b=0
a
%
b
=
0
也就是说上一轮的a可以完全整除b,此轮的a则是上一轮的b提供的,所以将b返回即可。
我遇到的问题
在学习欧几里得的时候,我有一个疑惑,在我们刚才的推导过程:假设d是a和b的最大公约数,我们可以推导出 r ( a%b a % b )也可以整除d , 既然a,b,r都可以整除d,为什么我们的等式只能写作 gcd(a,b)=gcd(b,a%b) g c d ( a , b ) = g c d ( b , a % b ) 而不能写作 gcd(a,b)=gcd(a,a%b) g c d ( a , b ) = g c d ( a , a % b ) 呢? 在这个问题上,我思考了很久,终于得到了一定的理解… 首先我刚才说过 只有当 a>b a > b 的时候,算法才开始进行运算,所以说a在这里面是相对是最大的,b其次,而r是最小的…. 我说的可能太过绝对,以下为他们的具体关系:
a≥b≥r a ≥ b ≥ r
在我们每轮递归的过程中,希望值是越来越小的…其中a确永远是最大的…而且递归a也是没有意义的…因为我们最终要求的是最大公约数解.
代数形式的推导过程:
扩展欧几里德算法的解释说明:
在学习完上面所讲的欧几里德算法之后,本节我们来讲一下 扩展欧几里得算法,欧几里德算法是用来做什么的呢?
扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式:
ax+by=gcd(a,b)
a
x
+
b
y
=
g
c
d
(
a
,
b
)
,然后再求出现实所所需要的通解。由于证明这个等式成立的过程涉及到其他的数论知识,在学习基础的我们可以暂时把证明过程放在一边,权当这是成立的。
我们已知 a∗x1+b∗y1=gcd(a,b) a ∗ x 1 + b ∗ y 1 = g c d ( a , b ) 根据欧几里德,我们可以得到:
gcd(a,b)=gcd(b,a%b) g c d ( a , b ) = g c d ( b , a % b )
gcd(b,a%b)=b∗x2+(a%b)∗y2=b∗x2+(a−floor(a/b)∗b)∗y2
g
c
d
(
b
,
a
%
b
)
=
b
∗
x
2
+
(
a
%
b
)
∗
y
2
=
b
∗
x
2
+
(
a
−
f
l
o
o
r
(
a
/
b
)
∗
b
)
∗
y
2
我们最终可以得到:
y1=x2−floor(a/b)∗y2,x1=y2
y
1
=
x
2
−
f
l
o
o
r
(
a
/
b
)
∗
y
2
,
x
1
=
y
2
接下来我们来看代码实现(递归) :
int exGcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int r=exGcd(b,a%b,x,y);
int t=x;
y=t-a/b*y;
return r;
}
现在我们考虑一下递归的终点…..当
b=0
b
=
0
我们为何要
x=1
x
=
1
,
y=0
y
=
0
呢…
首先我们知道贝祖等式
a∗x+b∗y=gcd(a,b)
a
∗
x
+
b
∗
y
=
g
c
d
(
a
,
b
)
其中
a≥gcd(a,b)
a
≥
g
c
d
(
a
,
b
)
当
b=0
b
=
0
时
x=1
x
=
1
是必然的,满足最终
a=gcd(a,b)
a
=
g
c
d
(
a
,
b
)
,而y的取值..由于
b=0
b
=
0
所以y可以取任何值…但是我们最终要求的最小解..所以将其赋值为零…
通过以上的运算过程….我们把
a∗x+b∗y=gcd(a,b)
a
∗
x
+
b
∗
y
=
g
c
d
(
a
,
b
)
的其中一组解解出来了, 仅仅是其中一组解。对于已经得到的解x1, y1,我们便可以求出通解。
通解计算公式:
我们假设 x=x0+k∗t x = x 0 + k ∗ t ,将其带入到 a∗x+b∗y=gcd(a,b) a ∗ x + b ∗ y = g c d ( a , b ) ,我们可以得到 y=y0−a∗k/b∗t; y = y 0 − a ∗ k / b ∗ t ;
而我们要保证y也为整数的话必须保证a * k /b也为整数,我们不妨令 k=b/gcd(a,b) k = b / g c d ( a , b )
所以通解为:
x=x0+b/gcd(a,b)∗t x = x 0 + b / g c d ( a , b ) ∗ t
y=y0−a/gcd(a,b)∗t; y = y 0 − a / g c d ( a , b ) ∗ t ;
其中的t就是x,y满足贝祖等式时的周期。
我需要讲的最后一个问题:
求任何数字n在一定周期t中最小正整数:
int ans = ((n % t)+t) % t;
有关题目:POJ 1601