179 lines
4 KiB
Text
179 lines
4 KiB
Text
---
|
|
id: extend-euclid
|
|
title: "Extended Euclidean Algorithm"
|
|
author: Benjamin Qi
|
|
prerequisites:
|
|
- intro-nt
|
|
description: "?"
|
|
frequency: 1
|
|
---
|
|
|
|
import { Problem } from "../models";
|
|
|
|
export const metadata = {
|
|
problems: {
|
|
kat: [
|
|
new Problem("Kattis","Modular Arithmetic", "modulararithmetic"),
|
|
],
|
|
crt: [
|
|
new Problem("Kattis","Chinese Remainder", "chineseremainder"),
|
|
new Problem("Kattis","Chinese Remainder (non-relatively prime moduli)", "generalchineseremainder"),
|
|
],
|
|
}
|
|
};
|
|
|
|
## Euclidean Algorithm
|
|
|
|
<Resources>
|
|
<Resource source="cp-algo" url="algebra/euclid-algorithm.html" title="Euclidean"> </Resource>
|
|
</Resources>
|
|
|
|
The original Euclidean Algorithm computes $\gcd(a,b)$ and looks like this:
|
|
|
|
<LanguageSection>
|
|
|
|
<CPPSection>
|
|
|
|
```cpp
|
|
ll euclid(ll a, ll b) {
|
|
for (;b;swap(a,b)) {
|
|
ll k = a/b;
|
|
a -= k*b;
|
|
}
|
|
return a; // gcd(a,b)
|
|
}
|
|
```
|
|
|
|
|
|
## Extended Euclidean Algorithm
|
|
|
|
<Resources>
|
|
<Resource source="cp-algo" url="algebra/extended-euclid-algorithm.html" title="Extended Euclidean" starred> </Resource>
|
|
<Resource source="Wikipedia" url="https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm" title="Extended Euclidean"> </Resource>
|
|
<Resource source="cp-algo" url="algebra/linear-diophantine-equation.html" title="Linear Diophantine Equation"> </Resource>
|
|
</Resources>
|
|
|
|
The extended Euclidean algorithm computes integers $x$ and $y$ such that
|
|
|
|
$$
|
|
ax+by=\gcd(a,b)
|
|
$$
|
|
|
|
</CPPSection>
|
|
|
|
</LanguageSection>
|
|
|
|
We can slightly modify the version of the Euclidean algorithm given above to return more information!
|
|
|
|
<LanguageSection>
|
|
|
|
<CPPSection>
|
|
|
|
```cpp
|
|
array<ll,3> extendEuclid(ll a, ll b) {
|
|
array<ll,3> x = {1,0,a}, y = {0,1,b};
|
|
// we know that 1*a+0*b=a and 0*a+1*b=b
|
|
for (;y[2];swap(x,y)) { // run extended Euclidean algo
|
|
ll k = x[2]/y[2];
|
|
F0R(i,3) x[i] -= k*y[i];
|
|
// keep subtracting multiple of one equation from the other
|
|
}
|
|
return x; // x[0]*a+x[1]*b=x[2], x[2]=gcd(a,b)
|
|
}
|
|
|
|
int main() {
|
|
FOR(a,1,101) FOR(b,1,101) {
|
|
auto x = extendEuclid(a,b);
|
|
int g = x[2];
|
|
assert(g == __gcd(a,b));
|
|
if (a != b) assert(abs(x[0]) <= b/g/2 && abs(x[1]) <= a/g/2);
|
|
}
|
|
}
|
|
```
|
|
|
|
</CPPSection>
|
|
|
|
</LanguageSection>
|
|
|
|
### Recursive Version
|
|
|
|
<LanguageSection>
|
|
|
|
<CPPSection>
|
|
|
|
```cpp
|
|
ll euclid(ll a, ll b) {
|
|
if (!b) return a;
|
|
return euclid(b,a%b);
|
|
}
|
|
```
|
|
|
|
|
|
</CPPSection>
|
|
|
|
</LanguageSection>
|
|
|
|
becomes
|
|
|
|
<LanguageSection>
|
|
|
|
<CPPSection>
|
|
|
|
```cpp
|
|
pl extendEuclid(ll a, ll b) { // returns {x,y}
|
|
if (!b) return {1,0};
|
|
pl p = extendEuclid(b,a%b); return {p.s,p.f-a/b*p.s};
|
|
}
|
|
```
|
|
|
|
</CPPSection>
|
|
|
|
</LanguageSection>
|
|
|
|
The pair will equal to the first two returned elements of the array in the iterative version. Looking at this version, we can prove by induction that when $a$ and $b$ are distinct positive integers, the returned pair $(x,y)$ will satisfy $|x|\le \frac{b}{2\gcd(a,b)}$ and $|y|\le \frac{a}{2\gcd(a,b)}$. Furthermore, there can only exist one pair that satisfies these conditions!
|
|
|
|
Note that this works when $a,b$ are quite large (say, $\approx 2^{60}$) and we won't wind up with overflow issues.
|
|
|
|
## Application: Modular Inverse
|
|
|
|
<Resources>
|
|
<Resource source="cp-algo" url="algebra/module-inverse.html" title="Modular Inverse"> </Resource>
|
|
</Resources>
|
|
|
|
<Problems problems={metadata.problems.kat} />
|
|
|
|
It seems that when multiplication / division is involved in this problem, $n^2 < \texttt{LLONG\_MAX}$.
|
|
|
|
<LanguageSection>
|
|
|
|
<CPPSection>
|
|
|
|
```cpp
|
|
ll invGeneral(ll a, ll b) {
|
|
array<ll,3> x = extendEuclid(a,b);
|
|
assert(x[2] == 1); // gcd must be 1
|
|
return x[0]+(x[0]<0)*b;
|
|
}
|
|
|
|
int main() {
|
|
FOR(b,1,101) F0R(a,101) if (__gcd(a,b) == 1) {
|
|
ll x = invGeneral(a,b);
|
|
assert((a*x-1)%b == 0);
|
|
assert(0 <= x && x < b);
|
|
}
|
|
}
|
|
|
|
```
|
|
|
|
</CPPSection>
|
|
|
|
</LanguageSection>
|
|
|
|
## Application: Chinese Remainder Theorem
|
|
|
|
<Resources>
|
|
<Resource source="cp-algo" url="algebra/linear_congruence_equation.html" title="Linear Congruence Equation"> </Resource>
|
|
<Resource source="cp-algo" url="algebra/chinese-remainder-theorem.html" title="Chinese Remainder Theorem"></Resource>
|
|
</Resources>
|
|
|
|
<Problems problems={metadata.problems.crt} />
|