1. 程式人生 > 其它 >初級數論1:(擴充套件)歐幾里得演算法

初級數論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$。什麼時候有空就更。