2017-12-13
首先是歐幾里得定理,即gcd(a,b)=gcd(b,a%b)
證明一下吧
c=gcd(a,b) d=gcd(b,a
%b)
假設a=b
*k+t;
k是商,t是余數
那么d=gcd(b,a
%b)=gcd(b,t)
因為d|b,d|t,并且a=b
*k+t
所以d|a,即d是a,b的公因數,即d小于等于c
又因為c|a,c|b,并且t=a-b
*k
所以c|t,即c是b,t的公因數,即c小于等于d
所以說c==d的
或許我們大家都知道gcd(
a,b)=gcd(|
a|,|b|),所以我們在求最大公約數
的時候
a和b通常都是取正數的。
對于gcd(
a,b)=gcd(b,
a-b)(
a>b),這里可以用上述方法進行證明的。
我之前在《編程之美》上求解a與b的最大公約數中看到了三種解法,在這里簡單
的介紹一下吧。
(
1)直接利用__gcd(a,b),這是c++里面的庫函數,包含在頭文件<algorithm>
中,我查看了一下他的源碼,是用迭代的方法求解的。
類似于這個吧!
int gcd1(
int x,
int y){
while(
y){
int r=
x%y;
x=
y;
y=r;}
return x;
}
當然,我們也可以用遞歸的方法
int dg1(
int x,
int y){
return y==
0?
x:dg1(
y,
x%y);
}(
2)學過計算機組成原理的人都知道,求余操作耗時是比較長的,那么我們可以用
上面提到的減法操作,并且這種方法對于大整數也是可以求得的,代碼我就不贅述了。(
3)不知道大家還記得這樣一個定理嗎?即gcd(a1
*k,b1
*k)=k
*gcd(a1,b1);
還有一個,如果說我們的b
%k!=
0,那么gcd(a
*k,b)=gcd(a,b)。這里的證明我也
不是很清楚。
接下來我要介紹的方法就是和這個是有關系的,我們大家都知道,在計算機里位操作
是非常快的,左移<<相當于
*2,右移>>相當于/
2,是吧!那么我們可以利用移位操作
來加快我們的執行速度。直接看代碼吧!
int gcd(
int x,
int y){
if (
x<
y)
return gcd(
y,
x);
if (
y==
0)
return x;
if (iseven(
x)&&iseven(
y)){
return gcd(
x>>
1,
y>>
1)<<
1;}
if (iseven(
x)){
return gcd(
x>>
1,
y);}
if (iseven(
y)){
return gcd(
x,
y>>
1);}
else{
return gcd(
y,
x-
y);}
}
對于最后一種情況
x與
y都是奇數的話,它們倆進行減操作,最后的結果一定是
一個偶數吧!還有,我們在判斷奇偶性的時候,只要看二進制表示的最后一位是
0還是
1即可,所以就有了下面的代碼:
bool iseven(
int n){
return !(n&
1);
}
這就是我介紹的三種方法。
接下來就是擴展歐幾里得了 ,說實話,我對這個模板并沒有很好的理解,我僅僅能夠按照他的代碼自己模擬,但是至于代碼怎么得來的以及它的證明過程我并不是很清楚。我參考的是我的一個同學的博客。
http://blog.csdn.net/yoer77/article/details/69568676
在數論中,裴蜀等式(英語:Bézout’s identity)或貝祖定理(Bézout’slemma)是一個關于最大公約數(或最大公約式)的定理。裴蜀定理得名于法國
數x和y的線性丟番圖方程(稱為裴蜀等式): ax + by = m 有整數解時當且僅當m是d的倍數。
(這句話很關鍵,注意這里的
'''當且僅當''')裴蜀等式有解時必然有無窮多個整數解,每組解x、y都稱為裴蜀數,可用擴展
歐幾里得算法(Extended Euclidean algorithm)求得。
例如,
12和
42的最大公因數是
6,則方程
12x+
42y=
6有解。事實上有
(-
3)×
12 +
1×
42 =
6及
4×
12 + (-
1)×
42 =
6。
特別來說,方程 ax+by=
1 有整數解當且僅當整數a和b互素。
裴蜀等式也可以用來給最大公約數定義: d其實就是最小的可以寫成
ax+by形式的正整數。(這個定義的本質是整環中“理想”的概念。因此
對于多項式整環也有相應的裴蜀定理。)
擴展歐幾里得就是在求出gcd(
a,b)的同時求出
a*x+b*y=gcd(
a,b)的一個解。
用類似輾轉相除法,求二元一次不定方程63x+22y=1的整數解。
首先
63=22*2+19
22=19*1+3
19=3*6+1
這里的1就是我們的最大公約數了
然后我們左右換個表示方式
19=63+22*(-2)
3=22+19*(-1)
1=19+3*(-6)
最后我們一步步的回帶進去
1=19+3*(-6)
1=19+[22+19*(-1)]*(-6)
1=19*7+22*(-6)
1=[63+22*(-2)]*7+22*(-6)
1=63*7+22*(-20)
這樣我們就求解出來了
x=7,y=-20
這就是我們所要求得的答案啊!
設:a>b。
顯然當 b=
0,gcd(a,b)=a。此時
x=
1,
y=
0;
ab!=
0 時
對于一般的情況而言
a
*x1+b
*y1=gcd(a,b)
同時我們也可以得出
b
*x2+(a
%b)
*y1=gcd(b,a
%b)
由于
gcd(a,b)=gcd(b,a
%b)
那么我們可以進一步得到
a
*x1+b
*y1=b
*x2+(a
%b)
*y2,a
%b=(a-a/b
*b)
我們要知道,這里的a/b
*b并不一定會等于a,因為我們并不一定會整除
啊!進一步得到
a
*x1+b
*y1=a
*y2+b
*(x2-(a/b)
*y2)
這個等式要保證恒成立,那么
x1=y2,y1=x2-(a/b)
*y2
所以我們就得到了遞推公式
但是我本人并不知道這個所以是如何的來的
遞歸的代碼
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 =
y;
y =
x - (a/b) *
y;
x = t;
return r;
}
如有不理解,可以手動模擬一下,發現的確是這樣,但是還是覺得自己
對這個的理解不是很清晰。
擴展歐幾里得的非遞歸,據說是參考的這兩個鏈接
http://anh
.cs.luc.edu/
331/notes/xgcd
.pdf
http://math
.cmu.edu/~bkell/
21110-
2010s/extended-euclidean
.html
非遞歸的代碼
int exgcd(
int m,
int n,
int &
x,
int &
y) {
if (n ==
0) {
x =
1;
y =
0;
return m;}
int a, a1, b, b1, c, d,
q, r, t;a1 = b =
1;a = b1 =
0;c =
m; d = n;
q = c/d; r = c
%d;
while (r) {c = d;d = r;t = a1;a1 = a;a = t -
q * a;t = b1;b1 = b;b = t -
q * b;
q = c/d;r = c
%d;}
x = a;
y = b;
return d;
}
我自己覺得這個的確很有道理,但是思維怎么就轉變不過來,還是看一個題目吧!
題目鏈接
http:/
/hihocoder.com/problemset/problem/1297
首先我們這道題目用的是擴展歐幾里得的方法求解的
題目輸入s1,s2,v1,v2以及
m,其中
m是石板的總數目,編號是
0到
m-
1,s1是第一個人的起始位置,v1是第一個人的步長,s2是第二個
人的起始位置,v2是第二個人的步長,我們假設第二個人比第一個人
多走了t圈然后和他相遇,假設第一個人走了k步,那么我們可以列出
等式
s1+v1
*k=s2+v2
*k-
m*t,
其中我們只有k與t是變量
(v1-v2)
*k+
m*t=s2-s1
這樣我們是不是就把這個轉換為A
*x+B
*y=C了,我們令
A=v1-v2
B=
m
C=s2-s1
首先我們要明白一點,我們要保證A>
0,那么如果A<
0的話,那么我
們令A+
m,在這里其實就是等同于v1-v2的值加上了
m,其實這就等
同于每次多跳了一圈,對最后的結果是不影響的。
首先我們由上述定理可以得出,如果我們這里的C
%gcd(a,b)不為
0的話,那么這個放方程是沒有解的。直接輸出-
1。
否則的話,我們對A
*x+B
*y=C等式兩邊都除以gcd(a,b),那么
我們就得到了A
'*x+B'*y=C
',其中
A'=A/gcd(a,b),B
'=B/gcd(a,b),C'=C/gcd(a,b)
其中A
'與B'是互質的
我們根據擴展歐幾里得可以算出A
'*x+B'*y=
1的所有解,我們的
目標是A
'*x+B'*y=C
',那么我們可以把得出的結果*C',但是我
們盡管通過歐幾里得求得
x的值,我們也不能保證
x的值是我們想要
的最小的正整數,所以我們構建了
x的解集
A
'*x+B'*y=
1
A
'*x+B'*y+[u+(-u)]
*A'*B'=
1
轉化一下
A
'*(x+u*B')+B
'*(y-u*A')=
1
那么
x=
x+u
*B'
y=y-u*A'
那么我們把
x與
y進行相同的操作同樣也是這個方程的解,這就是
代碼中為什么會對求得的結果進行+B
'的原因
給出代碼
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;ll exgcd(ll m, ll n, ll &x, ll &y) {
if (n ==
0) {x =
1; y =
0;
return m;}
int a, a1, b, b1, c, d, q, r, t;a1 = b =
1;a = b1 =
0;c = m; d = n;q = c/d; r = c%d;
while (r) {c = d;d = r;t = a1;a1 = a;a = t - q * a;t = b1;b1 = b;b = t - q * b;q = c/d;r = c%d;}x = a; y = b;
return d;
}
int main() {ll s1, s2, v1, v2, m;ll A, B, C;ll k, t;
while(
cin >> s1 >> s2 >> v1 >> v2 >> m){A = v1-v2;B = m;C = s2-s1;
if (A <
0) A += B;ll d = __gcd(A, B);
if (C % d)
cout << -
1 << endl;
else {A = A/d;B = B/d;C = C/d;exgcd(A, B, k, t);k = (k * C) % B;
while (k <
0) {k += B;}
cout << k << endl;}}
return 0;
}
總結
以上是生活随笔為你收集整理的(扩展)欧几里得的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。