# 擴展歐基里德算法 (Extended Euclidean algorithm)
## 歐基里德算法
歐基里德算法又稱輾轉相除法,是計算兩個整數的最大公因數(Greatest Common Divisor, GCD)的方法。
有兩個數字 $a, b$,令 $p = gcd(a, b)$,那麼一定存在兩個正整數 $n, m \in \mathbb{R}$ 使得
$$
a = np,\quad b = mp
$$
那麼如果要求 $p$,我們可以不斷讓 $a, b$ 互減,最後就會剩下 $p$
下圖是從維基百科上抓來的,兩線段分別表示 $252$ 及 $105$,每個單位為其最大公因數 $21$
<center>
<img src="https://i.imgur.com/XYw4kc5.gif" height=300>
</center>
可以發現到,當我們重複將較大的一方扣除較小的一方,直到其中一方為 $0$ 時,剩下的就會剛好是最大公因數 $p$
從這裡我們可以發現到,最大公因數實際上有這樣的性質:
$$
gcd(a, b) = \begin{cases}
gcd(a-b, b) & a > b \\
gcd(b-a, a) & a < b
\end{cases}
$$
不過實際上我們也不需要做那麼多次減法,因為我們總是會減到直到不能再減
- 假設 $a > b$,那麼 $a - b$ 的次數就是 $\lfloor a/b \rfloor$,而這最後的 $a$ 其實就是 $a\%b$
- 假設 $a < b$,那麼 $b - a$ 的次數就是 $\lfloor b/a \rfloor$,而這最後的 $b$ 其實就是 $b\%a$
> 附註: $\lfloor \rfloor$ 為下高斯符號,也就是向下取整
> 舉例來說: $\lfloor 3.14 \rfloor = 3, \lfloor 4.9 \rfloor = 4$
> 附註: 雖然沒有提到 $a=b$ 的情況,不過在 $a=b$ 時無論放在 $a > b$ 的作法或是 $a < b$ 的做法也都會是正確的
也就是說:
$$
gcd(a, b) = \begin{cases}
gcd(a\%b, b) & a > b \\
gcd(b\%a, a) & a < b
\end{cases}
$$
換個方法敘述
已知 $a/b = q \dots r$
$$
a = qb + r\\
gcd(a, b) = gcd(qb + r, b) = gcd(r, b) = gcd(a\%b, b)
$$
已知 $b/a = q \dots r$
$$
b = qa + r\\
gcd(a, b) = gcd(qa + r, a) = gcd(r, a) = gcd(b\%a, a)
$$
在實作上,可以透過 $gcd(a, b) = gcd(b, a\%b)$ 來簡寫
如果 $a < b$ 的話,因為 $gcd(b, a\%b)$ 會將兩者交換,因此下一次計算 $gcd$ 時就會計算到 $gcd(a, b\%a)$ 了
```python=
def gcd(a, b):
if(b == 0): return a
return gcd(b, a%b)
```
## 擴展歐基里德算法
擴展歐基里德算法的命題如下:
已知 $\forall a, b \in \mathbb{Z}$,必定存在 $x, y$ 使得貝祖定理成立
$$
ax + by = gcd(a, b)
$$
求 $x, y$ 分別為何
根據命題可得
$$
\begin{align*}
ax_1 + by_1 &= gcd(a, b) \\
bx_2 + (a\%b)y_2 &= gcd(b, a\%b)
\end{align*}
$$
又因為
$$
gcd(a, b) = gcd(b, a\%b)
$$
因此
$$
\begin{align*}
ax_1 + by_1 &= bx_2 + (a\%b)y_2 \\
&= bx_2 + (a-\lfloor a/b \rfloor b)y_2
\end{align*}
$$
整理式子後可以得到
$$
ax_1 + by_1 = ay_2 + b(x_2 - \lfloor a/b \rfloor y_2)
$$
因此
$$
\begin{cases}
x_1 = y_2 \\
y_1 = x_2 - \lfloor a/b \rfloor y_2
\end{cases}
$$
因此,由於 $gcd(a, b) = gcd(b, a\%b)$,我們可以將原問題轉換成等價的新問題,並且新問題得出來的 $x, y$ 可以幫助我們得到原問題的 $x, y$
如此一來就可以實作出擴展歐基里德算法囉!
> 補充: 在 $b = 0$ 的情況下我們知道 $a = 1 \times a + 0 \times b$,因此回傳 $(x, y) = (1, 0)$
```python=
def exd_gcd(a, b):
if(b == 0): return 1, 0
else:
x, y = exd_gcd(b, a%b)
return y, x-(a//b)*y
```
###### tags: `Crypto`