初級數論1:(擴充套件)歐幾里得演算法
初級數論第一節:歐幾里得演算法,擴充套件歐幾里得演算法,例題。
這是你也能看懂的數論。
歐幾里得演算法
首先講一下歐幾里得演算法
歐幾里得演算法是可以在 $O(\log_n)$ 時間內求解兩數最大公約數的演算法,簡稱 $gcd$。
程式碼如下:
int gcd (int a, int b) { if (b == 0) return a; return gcd (b, a % b); }
首先來證明一下這個演算法的正確性。
$b=0$ 的時候 $a,b$ 的最大公約數就是 $a$,這個是對的。
現在需要證明 $gcd(a,b)=gcd(b,a\%b)$,可以再簡化一點,證明 $gcd(a,b)=gcd(b,a-b)$。
因為$a$ 一直減 $b$ 直到 $a<b$ 最後得到的結果就是 $a\% b$
證明
設 $gcd(a,b)=x$,那麼 $a|x,b|x$。
設 $a=cx,b=dx$,$a>b$。
那麼 $a-b=(c-d)\times x$,$a-b$ 也是 $x$ 的倍數。
所以 $gcd(a,b)=gcd(b,a-b)$
時間複雜度證明
設 $a>b$。
如果 $a>2b$,那麼迭代一次之後 $a$ 會縮小到 $b$ 以內,減半。
如果 $b<a<2b$,那麼第一次迭代之後變成 $b,a\%b$,此時有三種情況:
$a\%b<\frac{b}{2}$,那麼下一輪 $b\%(a\%b)$ 小於 $a\%b$,即小於 $\frac{b}{2}$。
$a\%b>\frac{b}{2}$,那麼下一輪 $b\%(a\%b)$ 就會等於 $b-(a\%b)$,因為 $a\%b>\frac{b}{2}$所以 $b-(a\%b)<\frac{b}{2}$。
$a\%b=\frac{b}{2}$,直接成 $0$ 了。
每一輪都減半, $log_n$ 次就成 $0$ 了。
拓展知識:
$1.gcd(a,b)$ 一般跑不滿 $log_n$,只有相鄰的斐波那契數列會跑滿。
證明:
假設斐波那契數列的相鄰兩項為 $a,b$,那麼$a < b < 2a$,取餘相當於減。
而相減就會變成斐波那契數列的上一項,
所以用歐幾里得演算法求斐波那契數列的第 $x$ 項和第 $x+1$ 項的最大公約數。
時間複雜度就是 $O(x)$,而斐波那契數列的增長是 $2^n$ 不到一點,基本跑滿 $O(log_n)$
$2.gcd(F[n],F[m])=gcd(F[n,m])$,這個在演算法競賽中很有用。
其中,$F[n]$ 指的是斐波那契數列的第 $n$ 項
證明過程太長,此處省略 $500$ 字。
例題:$Luogu P1306$ 斐波那契公約數
擴充套件歐幾里得演算法
擴充套件歐幾里得演算法是可以在 $O(\log_n)$ 的時間內求方程 $ax+by=gcd(a,b)$ 的整數解。
簡稱擴歐,別名 $exgcd$。
其中,$a,b$ 已知,$gcd(a,b)$ 可以在算的時候求。
擴充套件歐幾里得演算法是用小的解算出大的解然後回退。
終止答案
歐幾里得演算法的終結條件是 $b=0$,$b$ 最後一定會等於 $0$。
所以我們從 $b=0$ 的情況入手。
即 $ax+0\times y=gcd(a,0)$,$x$ 取 $1$,$y$ 按理來說取任何數都行。
但是取太大最後會爆 $longlong$,所以取 $0$。
回退
先來看一下歐幾里得演算法,$gcd(a,b)$可以怎麼求?
我們只要知道了 $gcd(b,a\% b)$ 就可以求得 $gcd(a,b)$,因為 $gcd(a,b) = gcd(b,a\% b)$。
擴充套件歐幾里得演算法也是這樣,要想求 $ax+by=gcd(a,b)$ 的整數解,
可以先求 $bx'+(a\%b)\times y'=gcd(b,a\% b)$ 的整數解,然後回退得到 $ax+by=gcd(a,b)$ 的解。
我們先假設已經求得了 $bx'+(a\%b)\times y'=gcd(b,a\% b)$ 的解,這是必然的。
因為最後 $b$ 會等於 $0$,就會得到一組解,然後慢慢回退。
解的變形
首先可以發現 $gcd(b,a\% b)=gcd(a,b)$。
我們現在就知道了 $bx'+(a\%b)\times y=gcd(a,b)$。嘗試拆開拼出 $ax+by$。
其次,$a\% b$ 可以寫成 $a-\lfloor\frac{a}{b}\rfloor\times b$。
代入進去變成 $bx'+(a-\lfloor\frac{a}{b}\rfloor\times b)\times y'=gcd(a,b)$。
拆開變成 $bx'+a\times y' - \lfloor\frac{a}{b}\rfloor \times b\times y'=gcd(a,b)$。
然後把和 $a$ 有關的,還有和 $b$ 有關的提出來。
得到 $ay'+b\times (x' + \lfloor\frac{a}{b}\rfloor \times y') = gcd(a, b)$
然後我們發現這個方程不就是 $ax + by$ 的形式嗎?
$x$ 就等於 $y'$,$y$ 稍麻煩一點,等於 $x' +\lfloor\frac{a}{b}\rfloor \times y'$
然後就可以回退到上一層求解了。
時間複雜度證明
不難發現,$exgcd$ 的時間複雜度只和 $a,b$ 有關,
並且 $a,b$ 每一次還是變成 $b,a\% b$,所以時間複雜度和 $gcd$ 一樣。
程式碼:
void exgcd (int a, int b) { if (b == 0)//b=0 的情況答案可以直接得到 { x = 1; y = 0; return; } exgcd (b, a%b)//否則先算 exgcd(b, a % b) 的解。 int t = x;//現在的 x, y 是 bx + (a % b)y = gcd(a, b) 的解。 x = y;//剛剛證明了 ax + by = gcd(a, b) 的解 x 就是上一個方程的解 y y = t + a / b * y;//x 已被更改,所以用臨時變數 t 儲存上一個方程的解 x }
拓展:一般形式
題目裡很少有要求 $ax+by=gcd(a,b)$ 的整數解,一般都是求 $ax+by=c$ 的整數解。
這時該怎麼辦呢?
如果 $gcd(a,b)|c$,那麼我們在等式兩邊同乘以 $\frac{c}{gcd(a,b)}$ 即可得到整數解。
否則,可以證明,該方程無整數解。
例題:
$1.iai$ 六星題 兩個鬧鐘
$2.iai$ 五星級挑戰 無盡的迴圈
兩個鬧鐘
題意已經很明顯了,就是一個擴歐。
設兩個整數 $x,y$,使得 $ax+n=by+m$。
整理得 $ax-by=m-n$。
另 $y'=-y$,代入得 $ax+by=m-n$
其中,$m-n$ 和 $a,b$ 都是已知的,這不就是擴歐嗎?
解出來之後,$ax+n$ 或者 $by+m$ 都是題目要求的答案。
但是這個解有可能是負的,所以得嘗試把他變成正的。
可以發現兩個鬧鐘第一次同時響如果在 $t_1$ 時刻,那麼下一次響肯定是在 $t_1+lcm(a,b)$ 時刻啦!
那麼就一直加 $lcm(a,b)$ 直到都變成正的就好了。
但是會超時,用一個除法就可以了。
無盡的迴圈
這一道題是需要變形一下的,大家可以自己先嚐試一下。
另外,注意開個 $longlong$。
簡化題意
給定 $a,b,c,k$,求一個整數 $d$,使得 $a+dc\equiv b(mod 2^k)$。
然後把同餘去了,變成 $a+dc = b + e\times 2^k$。
把 $e$ 移到左邊,$b$ 移到右邊,得 $dc - e\times 2^k = b - a$。
其中, $c,2^k$ 已知,右邊的 $b-a$ 也是已知的。
令 $a=c,x=d,b=2^k,y=e,c=b-a$,這就是一個典型的擴歐式子。$ax+by=c$
最後 $x$ 就是要求的答案,然後還需要化簡一下,和兩個鬧鐘差不多,大家可以自己去研究研究。
下節預告:中國剩餘定理 $CRT$ 和擴充套件中國剩餘定理 $EXCRT$。什麼時候有空就更。