What is the Euclidean Algorithm?

The Euclidean Algorithm is an ancient, extremely efficient method for computing the Greatest Common Divisor (GCD) of two integers. The Extended Euclidean Algorithm goes further by calculating integers and such that (Bezout’s Identity). This is critical for solving modular multiplicative inverses and linear Diophantine equations. Time Complexity: O(log(min(a, b))).

Mathematical Fundamentals

  • To understand the Euclidean algorithm, we must establish three basic principles from number theory:

1. Divisibility and Common Divisors

  • An integer divides an integer (written as ) if there exists an integer such that .
  • The Greatest Common Divisor, , is the largest positive integer that simultaneously divides both and .
  • If , the integers and are said to be coprime or relatively prime.

2. The Division Algorithm Theorem

  • For any integer and any positive integer , there exist unique integers (quotient) and (remainder) such that:

3. Modulo Reduction Principle

  • The Euclidean algorithm relies on the fact that if is a common divisor of and , it must also divide their remainder.
  • Since , we can rewrite the remainder as .
  • If and , then must divide any linear combination of and , including . Hence, .
  • Therefore:

Standard Euclidean Algorithm

Standard GCD Recurrence

  • The algorithm reduces the problem of finding to finding . Since the remainder strictly decreases (), the second parameter eventually reaches .
  • At that point:

Standard GCD Step-by-Step Trace ()

  • Let’s find :
    1. .

Extended Euclidean Algorithm

Bezout’s Identity

  • Bezout’s Identity states that for any non-zero integers and , there exist integers and such that:
  • Note that and are not unique. The Extended Euclidean algorithm calculates one such pair of coefficients along with the GCD.

Mathematical Derivation of Updating Formulas

  • We want to solve .
  • Suppose we call the algorithm recursively for and , obtaining coefficients and such that:
  • We can express using the division quotient:
  • Substituting this into the recursive equation:
  • Grouping the terms by and :
  • Matching this with the form , we get the update rules:

Base Case

  • When , the equation is .
  • We can choose and as our base coefficients.

Detailed Extended Trace ()

  • We trace the recursive calls of the Extended Euclidean Algorithm:
StepRecurseReturned Computed
1240465,
246104,
31061,
4641,
5422,
6 (Base)20-Base case-
  • Final Result: . Coefficients are .
  • Verification: Wait, . — wait, is the GCD 2 or 4?
  • Let’s re-calculate:
    • .
    • .
    • .
    • .
    • GCD is indeed 2.
    • Let’s check coefficients: . Where is the error?
    • Let’s backtrack carefully:
      • Step 5: , . Wait, from base case , we get: , . Verification: . Correct.
      • Step 4: . Update: , . Verification: . Correct.
      • Step 3: . Update: , . Verification: . Correct.
      • Step 2: . Update: , . Verification: . Correct.
      • Step 1: . Update: , . Verification: . Correct!
      • Ah! The table coefficients had a minor copy error in backtracking. Let’s fix the table values to be exactly correct.
  • Corrected Trace Table:
StepRecurseReturned Computed
1240465,
246104,
31061,
4641,
5422,
6 (Base)20-Base case-

Complexity Analysis & Lamé’s Theorem

Asymptotic Bound

  • The time complexity of the Euclidean algorithm is . This is because, at every two iterations, the remainder is at least cut in half:
  • If , then .

Mathematical Proof of Lamé’s Theorem

  • Lamé's Theorem (1844) and () is at most times the number of decimal digits of .

    The number of division steps in the Euclidean algorithm for two positive integers

  • Proof: Let the Euclidean algorithm take steps to find : Since and all quotients (with because if were 1, the division would have ended earlier): Therefore, the smaller number . From the golden ratio approximation of Fibonacci numbers: Taking the base-10 logarithm on both sides: Since : Thus, the number of steps is at most times the number of digits of . Q.E.D.
    • (where is the -th Fibonacci number)
    • By induction: .

Applications & Diophantine Equations

1. Modular Multiplicative Inverse

  • If , there exists an integer such that:
  • Using the Extended Euclidean Algorithm, we solve .
  • Taking modulo on both sides: .
  • Thus, the coefficient returned by the algorithm is the modular inverse of . (We adjust to be positive by computing (x % m + m) % m).

2. Linear Diophantine Equations

  • A Linear Diophantine Equation is an equation of the form:
  • where , , and are given integers, and we seek only integer solutions for and .

Solvability Condition

  • The equation has an integer solution if and only if is a multiple of .

Finding a Particular Solution

  • We first find coefficients for the equation using the Extended Euclidean Algorithm.
  • If , we scale these coefficients by multiplying by :
  • This is a particular solution to the Diophantine equation.

Finding All General Solutions

  • Once we have one particular solution , we can generate all other integer solutions by shifting parameters:
  • where is any arbitrary integer.

Implementation

  • Below are the implementations for both standard GCD, Extended GCD, modular inverse, and a general Linear Diophantine Equation solver. Python · Cpp · Java Script · Java

    Languages:

# 1. Standard Iterative GCD
def gcd(a, b):
    while b:
        a, b = b, a % b
    return a
 
# 2. Extended GCD
# Returns (gcd, x, y)
def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    g, x1, y1 = extended_gcd(b, a % b)
    x = y1
    y = x1 - (a // b) * y1
    return g, x, y
 
# 3. Modular Multiplicative Inverse
def mod_inverse(a, m):
    g, x, _ = extended_gcd(a, m)
    if g != 1:
        return None  # Inverse does not exist
    return (x % m + m) % m
 
# 4. Linear Diophantine Equation Solver
# Solves ax + by = c. Returns (Success, x0, y0, dx, dy)
# where x = x0 + k*dx, y = y0 + k*dy
def solve_diophantine(a, b, c):
    g, x0_prime, y0_prime = extended_gcd(abs(a), abs(b))
    if c % g != 0:
        return False, 0, 0, 0, 0
    
    x0 = x0_prime * (c // g)
    y0 = y0_prime * (c // g)
    
    # Adjust signs for negative inputs
    if a < 0: x0 = -x0
    if b < 0: y0 = -y0
        
    return True, x0, y0, b // g, -a // g
 
# Example Usage
print("GCD(240, 46) =", gcd(240, 46))
g, x, y = extended_gcd(240, 46)
print(f"Extended: 240*({x}) + 46*({y}) = {g}")
 
success, x0, y0, dx, dy = solve_diophantine(240, 46, 10)
if success:
    print(f"Diophantine particular solution: x0={x0}, y0={y0}")
    print(f"General solution: x = {x0} + k*{dx}, y = {y0} + k*{dy}")
#include <iostream>
#include <tuple>
#include <cmath>
 
// 1. Standard Iterative GCD
long long gcd(long long a, long long b) {
    while (b) {
        a %= b;
        std::swap(a, b);
    }
    return a;
}
 
// 2. Extended GCD
std::tuple<long long, long long, long long> extended_gcd(long long a, long long b) {
    if (b == 0) return {a, 1, 0};
    auto [g, x1, y1] = extended_gcd(b, a % b);
    long long x = y1;
    long long y = x1 - (a / b) * y1;
    return {g, x, y};
}
 
// 3. Modular Multiplicative Inverse
long long mod_inverse(long long a, long long m) {
    auto [g, x, y] = extended_gcd(a, m);
    if (g != 1) return -1;
    return (x % m + m) % m;
}
 
// 4. Linear Diophantine Equation Solver
struct DiophantineResult {
    bool has_solution;
    long long x0, y0;
    long long dx, dy;
};
 
DiophantineResult solve_diophantine(long long a, long long b, long long c) {
    auto [g, x0_prime, y0_prime] = extended_gcd(std::abs(a), std::abs(b));
    if (c % g != 0) {
        return {false, 0, 0, 0, 0};
    }
    long long x0 = x0_prime * (c / g);
    long long y0 = y0_prime * (c / g);
    if (a < 0) x0 = -x0;
    if (b < 0) y0 = -y0;
    return {true, x0, y0, b / g, -a / g};
}
 
int main() {
    std::cout << "GCD(240, 46) = " << gcd(240, 46) << "\n";
    auto [g, x, y] = extended_gcd(240, 46);
    std::cout << "Extended: 240*(" << x << ") + 46*(" << y << ") = " << g << "\n";
    
    auto res = solve_diophantine(240, 46, 10);
    if (res.has_solution) {
        std::cout << "Diophantine solution: x = " << res.x0 << " + k*" << res.dx 
                  << ", y = " << res.y0 << " + k*" << res.dy << "\n";
    }
    return 0;
}
// 1. Standard Iterative GCD
function gcd(a, b) {
    while (b) {
        let temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
 
// 2. Extended GCD
function extendedGcd(a, b) {
    if (b === 0) return [a, 1, 0];
    const [g, x1, y1] = extendedGcd(b, a % b);
    const x = y1;
    const y = x1 - Math.floor(a / b) * y1;
    return [g, x, y];
}
 
// 3. Modular Multiplicative Inverse
function modInverse(a, m) {
    const [g, x] = extendedGcd(a, m);
    if (g !== 1) return -1;
    return (x % m + m) % m;
}
 
// 4. Linear Diophantine Solver
function solveDiophantine(a, b, c) {
    const [g, x0Prime, y0Prime] = extendedGcd(Math.abs(a), Math.abs(b));
    if (c % g !== 0) {
        return { hasSolution: false };
    }
    let x0 = x0Prime * Math.floor(c / g);
    let y0 = y0Prime * Math.floor(c / g);
    if (a < 0) x0 = -x0;
    if (b < 0) y0 = -y0;
    return {
        hasSolution: true,
        x0, y0,
        dx: Math.floor(b / g),
        dy: -Math.floor(a / g)
    };
}
 
console.log("GCD(240, 46) =", gcd(240, 46));
const [g, x, y] = extendedGcd(240, 46);
console.log(`Extended: 240*(${x}) + 46*(${y}) = ${g}`);
const res = solveDiophantine(240, 46, 10);
if (res.hasSolution) {
    console.log(`Diophantine solution: x = ${res.x0} + k*${res.dx}, y = ${res.y0} + k*${res.dy}`);
}
public class ExtendedGCDSolver {
    
    // 1. Standard GCD
    public static long gcd(long a, long b) {
        while (b != 0) {
            long temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
 
    static class GCDResult {
        long gcd, x, y;
        GCDResult(long g, long x, long y) {
            this.gcd = g;
            this.x = x;
            this.y = y;
        }
    }
 
    // 2. Extended GCD
    public static GCDResult extendedGcd(long a, long b) {
        if (b == 0) return new GCDResult(a, 1, 0);
        GCDResult next = extendedGcd(b, a % b);
        long x = next.y;
        long y = next.x - (a / b) * next.y;
        return new Result(next.gcd, x, y);
    }
 
    // Helper to avoid naming conflicts with previous implementations
    static class Result extends GCDResult {
        Result(long g, long x, long y) { super(g, x, y); }
    }
 
    // 3. Modular Multiplicative Inverse
    public static long modInverse(long a, long m) {
        GCDResult res = extendedGcd(a, m);
        if (res.gcd != 1) return -1;
        return (res.x % m + m) % m;
    }
 
    static class DiophantineResult {
        boolean hasSolution;
        long x0, y0, dx, dy;
        DiophantineResult(boolean ok, long x0, long y0, long dx, long dy) {
            this.hasSolution = ok;
            this.x0 = x0;
            this.y0 = y0;
            this.dx = dx;
            this.dy = dy;
        }
    }
 
    // 4. Linear Diophantine Equation Solver
    public static DiophantineResult solveDiophantine(long a, long b, long c) {
        GCDResult res = extendedGcd(Math.abs(a), Math.abs(b));
        if (c % res.gcd != 0) {
            return new DiophantineResult(false, 0, 0, 0, 0);
        }
        long x0 = res.x * (c / res.gcd);
        long y0 = res.y * (c / res.gcd);
        if (a < 0) x0 = -x0;
        if (b < 0) y0 = -y0;
        return new DiophantineResult(true, x0, y0, b / res.gcd, -a / res.gcd);
    }
 
    public static void main(String[] args) {
        System.out.println("GCD(240, 46) = " + gcd(240, 46));
        GCDResult res = extendedGcd(240, 46);
        System.out.println("Extended: 240*(" + res.x + ") + 46*(" + res.y + ") = " + res.gcd);
        
        DiophantineResult dioph = solveDiophantine(240, 46, 10);
        if (dioph.hasSolution) {
            System.out.println("Diophantine: x = " + dioph.x0 + " + k*" + dioph.dx 
                               + ", y = " + dioph.y0 + " + k*" + dioph.dy);
        }
    }
}

Key Takeaways

  • Logarithmic Complexity — Lamé’s Theorem guarantees that the Euclidean algorithm scales linearly with the number of decimal digits, making it extremely fast even for numbers with hundreds of digits.
  • Extended Coefficients — Computing Bezout’s identity coefficients is essential to solving Diophantine equations and computing modular inverses.
  • Diophantine Solvability is only solvable if . General solutions shift around a particular solution by factors of and .

More Learn

GitHub & Webs