PrevNext
Rare
 0/2

Extended Euclidean Algorithm

Author: Benjamin Qi

?

Edit This Page

Prerequisites

Euclidean Algorithm

Resources
cp-algo

The original Euclidean Algorithm computes gcd(a,b)\gcd(a,b) and looks like this:

C++

ll euclid(ll a, ll b) {
while (b > 0) {
ll k = a / b;
a -= k * b;
swap(a, b);
}
return a;
}

Java

static long euclid(long a, long b) {
while (b > 0) {
long k = a / b;
a -= k * b;
long temp = b;
b = a;
a = temp;
}
return a;
}

Python

def euclid(a: int, b: int) -> int:
assert (
int(a) > 0 and int(b) > 0
), "Arguments must be positive, non-zero numeric values."
while b > 0:
k = a // b
# subtract multiples of one equation from the other.
a -= b * k
a, b = b, a
return a

Extended Euclidean Algorithm

The extended Euclidean algorithm computes integers xx and yy such that

ax+by=gcd(a,b)ax+by=\gcd(a,b)

We can slightly modify the version of the Euclidean algorithm given above to return more information!

C++

array<ll, 3> extend_euclid(ll a, ll b) {
// we know that (1 * a) + (0 * b) = a and (0 * a) + (1 * b) = b
array<ll, 3> x = {1, 0, a};
array<ll, 3> y = {0, 1, b};
// run extended Euclidean algo
while (y[2] > 0) {
// keep subtracting multiple of one equation from the other
ll k = x[2] / y[2];
for (int i = 0; i < 3; i++) { x[i] -= k * y[i]; }
swap(x, y);
}
return x; // x[0] * a + x[1] * b = x[2], x[2] = gcd(a, b)
}

Java

static long[] extendEuclid(long a, long b) {
// we know that 1 * a + 0 * b = a and 0 * a + 1 * b = b
long[] x = {1, 0, a};
long[] y = {0, 1, b};
// run extended Euclidean algo
while (y[2] > 0) {
// keep subtracting multiple of one equation from the other
long k = x[2] / y[2];
for (int i = 0; i < 3; i++) { x[i] -= k * y[i]; }

Python

def extend_euclid(a: int, b: int) -> list[int]:
assert (
int(a) > 0 and int(b) > 0
), "Arguments must be positive, non-zero numeric values."
# we know that 1 * a + 0 * b = a and 0 * a + 1 * b = b.
x_arr = [1, 0, int(a)]
y_arr = [0, 1, int(b)]
q = -1

Recursive Version

C++

ll euclid(ll a, ll b) {
if (b == 0) { return a; }
return euclid(b, a % b);
}

Java

static long euclid(long a, long b) {
if (b == 0) { return a; }
return euclid(b, a % b);
}

Python

def euclid(a: int, b: int) -> int:
"""Recursive Euclidean GCD."""
return a if b == 0 else euclid(b, a % b)

becomes

C++

pl extend_euclid(ll a, ll b) { // returns {x,y}
if (b == 0) { return {1, 0}; }
pl p = extend_euclid(b, a % b);
return {p.s, p.f - a / b * p.s};
}

Java

static long[] extendEuclid(long a, long b) { // returns {x,y}
if (b == 0) { return new long[] {1, 0}; }
long[] p = extendEuclid(b, a % b);
return new long[] {p[1], p[0] - a / b * p[1]};
}

Python

def extend_euclid(a: int, b: int) -> list[int]:
if not b:
return [1, 0]
p = extend_euclid(b, a % b)
return [p[1], p[0] - (a // b) * p[1]]

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 aa and bb are distinct positive integers, the returned pair (x,y)(x,y) will satisfy xb2gcd(a,b)|x|\le \frac{b}{2\gcd(a,b)} and ya2gcd(a,b)|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,ba,b are quite large (say, 260\approx 2^{60}) and we won't wind up with overflow issues.

Application - Modular Inverse

Resources
cp-algo
StatusSourceProblem NameDifficultyTags
KattisN/A

It seems that when multiplication / division is involved in this problem, n2<LLONG_MAXn^2 < \texttt{LLONG\_MAX}.

C++

ll inv_general(ll a, ll b) {
array<ll, 3> x = extend_euclid(a, b);
assert(x[2] == 1); // gcd must be 1
return x[0] + (x[0] < 0) * b;
}

Java

static long invGeneral(long a, long b) {
long[] x = extendEuclid(a, b);
assert (x[2] == 1); // gcd must be 1
return x[0] + (x[0] < 0 ? 1 : 0) * b;
}

Python

def inv_general(a: int, b: int) -> int:
"""Returns the modular inverse of two positive, non-zero integer values."""
arr = extend_euclid(a, b)
assert arr[2] == 1, "GCD must be 1."
return arr[0] + (arr[0] < 0) * b

Application - Chinese Remainder Theorem

Focus Problem – try your best to solve this problem before continuing!

For Two Congruences

Explanation

The Chinese Remainder Theorem - also referred to as CRT - yields a unique solution to a system of simultaneous modular congruences with pairwise coprime moduli. More specifically, CRT determines a number xx that when divided by the given divisors leaves the given remainders. Mathematically, it solves the following system of modular congruences:

xa1(modm1)xa2(modm2)xa3(modm3)xak(modmk)(1)x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ x \equiv a_3 \pmod{m_3} \\ \vdots\\ x \equiv a_k \pmod{m_k} \\ \tag{1}
M=m1m2m3mkM=m_1\cdot m_2 \cdot m_3 \cdots m_k

The above system has exactly one solution modulo MM.

We'll focus on the particular case for k=2k=2; for two coprime moduli:

xa1(modm1)xa2(modm2)(2)x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \tag{2}
M=m1m2M=m_1 \cdot m_2
  • Let n1n_1 and n2n_2 be the modular inverse of m1m_1 modulo m2m_2 and the modular inverse of m2m_2

modulo m1m_1, respecively, thus the properties hold: n1m11(modm2)n_1 \cdot m_1 \equiv 1 \pmod{m_2} and n2m21(modm1)n_2 \cdot m_2 \equiv 1 \pmod{m_1}. It's worth mentioning that n1n_1 and n2n_2 always exists because gcd(m1,m2)=1\gcd(m_1,m_2)=1. We can define a solution xx as:

x=a1m2n2+a2m1n1(modm1m2)x = a_1 \cdot m_2 \cdot n_2 + a_2 \cdot m_1 \cdot n_1 \pmod{m_1\cdot m_2}

It satisfies both equations:

  • Modulo m1m_1 we have xa1m2n2(modm1)    xa1(modm1)x \equiv a_1 \cdot m_2 \cdot n_2 \pmod{m_1} \iff x \equiv a_1 \pmod{m_1} since m2n21(modm1)m_2 \cdot n_2 \equiv 1 \pmod{m_1}
  • Modulo m2m_2 we have xa2m1n1(modm2)    xa2(modm2)x \equiv a_2 \cdot m_1 \cdot n_1 \pmod{m_2} \iff x \equiv a_2 \pmod{m_2} since m1n11(modm2)m_1 \cdot n_1 \equiv 1 \pmod{m_2}

Now that we have a solution x(modM)x \pmod{M} it only remains to show that it is indeed the only valid solution modulo m1m2m_1 \cdot m_2. Assuming that there is a different valid solution yy we have: m1yxm_1 | y-x, m2yxm_2 | y-x, m3yxm_3 | y-x, \ldots, mkyxm_k | y-x, then it follows that m1m2mkm_1 \cdot m_2 \cdots m_k divides yxy-x, since m1,m2,,mkm_1, m_2, \ldots, m_k are relatively prime. This means that: yx(modm1m2mk)y \equiv x \pmod{m_1 \cdot m_2 \cdots m_k}.

Implementation

Time Complexity: O(TlogMN)\mathcal{O}(T \cdot \log{MN})

C++

#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
typedef long long ll;

For Several Congruences

Using the same notations as in (1)(1), consider ni=Mmin_i = \frac{M}{m_i} - the product of all moduli excluding mim_i - and nini1(modmi)n_i' \equiv n_i^{-1} \pmod{m_i}. By similar arguments as before, the unique solution mod MM is:

x=i=1nainini(modM)x = \sum_{i=1}^{n}{a_i \cdot n_i \cdot n_i'} \pmod{M}

Module Progress:

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!

PrevNext