∑MathIntermediate

GCD and Euclidean Algorithm

Key Points

  • ‱
    The greatest common divisor (gcd) of two integers is the largest integer that divides both without a remainder.
  • ‱
    The Euclidean Algorithm computes gcd(a, b) by repeatedly replacing (a, b) with (b, a mod b) until the remainder is zero.
  • ‱
    It finishes very fast: the number of iterations is O(log min(a, b)), meaning it grows slowly even for large numbers.
  • ‱
    The extended Euclidean Algorithm also finds integers x and y such that ax + by = gcd(a, b) (BĂ©zout’s identity).
  • ‱
    If gcd(a, b) = 1, then a has a modular inverse modulo b, which we can compute using the extended Euclidean Algorithm.
  • ‱
    GCD and LCM are linked by gcd(a, b) × lcm(a, b) = a × b for nonzero a, b.
  • ‱
    You can use gcd to reduce fractions, solve linear Diophantine equations ax + by = c, and compute modular inverses for number theory problems.
  • ‱
    In practice, always guard LCM against overflow and normalize remainders/inverses to be within [0, m-1].

Prerequisites

  • →Integer division and modulo — Understanding a = bq + r and how remainders work is essential to follow the Euclidean step a → a mod b.
  • →Basic properties of divisibility — GCD reasoning relies on what it means for one number to divide another and common divisors.
  • →Absolute value and sign — GCD is defined as nonnegative; extended coefficients can be negative and must be normalized.
  • →Prime factorization and LCM — Helps justify why gcd·lcm = |a·b| and builds intuition for shared factors.
  • →Modular arithmetic basics — Required for modular inverses, congruences, and normalization to [0, m-1].
  • →Asymptotic complexity (Big-O) — To appreciate why the Euclidean Algorithm is fast and suitable for large inputs.

Detailed Explanation

Tap terms for definitions

01Overview

The greatest common divisor (gcd) of two integers a and b is the largest integer that divides both without leaving a remainder. It is a cornerstone of number theory and appears in many algorithmic problems, from reducing fractions to cryptography. The Euclidean Algorithm is a simple, fast method to compute the gcd: repeatedly replace the pair (a, b) with (b, a mod b) until the second number becomes zero; the first number then holds the gcd. Its efficiency is remarkable: the number of iterations is proportional to the number of digits of the smaller input, i.e., O(log min(a, b)).

Beyond just the gcd value, the extended Euclidean Algorithm computes integers x and y such that ax + by equals the gcd. This is BĂ©zout’s identity and is crucial for tasks like finding modular inverses (when gcd(a, m) = 1) and solving linear Diophantine equations ax + by = c. Another core relation ties gcd to the least common multiple (lcm): for nonzero a and b, gcd(a, b) × lcm(a, b) = a × b. This identity allows efficient LCM computation once the gcd is known.

In competitive programming (Codeforces 1000–1400), gcd-based techniques regularly show up in problems involving array normalization, counting coprime pairs, period synchronization via LCM, and constructing or verifying modular inverses. Mastering the intuition, implementation, and pitfalls (like overflow in LCM or invalid inverses) is key for writing correct and fast solutions.

02Intuition & Analogies

Imagine you and a friend have stacks of identical tiles. You want to arrange your tiles into the largest possible square grids such that both of your stacks make perfectly filled squares with no leftovers. The side length of those squares is analogous to the gcd: it’s the largest size that fits both collections evenly.

How do we find this size efficiently? Think of repeatedly trimming. Suppose you try to tile with your friend’s grid size b. You lay down as many b-sized groups as possible from your a tiles and see what’s left over—this remainder is a mod b. The leftover tells you what still doesn’t fit into b-sized groups. Now swap roles: try to fit b into the leftover. Keep repeating. Each trim makes the leftover strictly smaller, so eventually the leftover becomes zero. At that point, the last nonzero piece is exactly the largest size that fits both—your gcd.

For the extended version, picture blending two paint colors in buckets sized a and b. You want to measure out exactly one unit of the gcd using integer scoops of size a and b, possibly adding and removing. The extended Euclidean Algorithm tells you how many scoops of each (x and y, possibly negative) will exactly produce the gcd. If the gcd is 1, then the recipe x scoops of a and y scoops of b gets you exactly one unit—meaning a has a multiplicative inverse modulo b. If you need ax + by = c, you just scale that base recipe by c/gcd.

The gcd–lcm link is like scheduling buses arriving every a and b minutes. The gcd is the largest time unit that evenly partitions both schedules; the lcm is when they next meet together. Knowing one helps compute the other quickly and safely.

03Formal Definition

For integers a and b not both zero, the greatest common divisor gcd(a, b) is the unique nonnegative integer d satisfying: (1) d divides a and d divides b; (2) for any integer c that divides both a and b, . By convention, gcd(0, 0) = 0 and gcd(a, 0) = . Euclidean Algorithm: For integers a, b with , let mod b. Then gcd(a, b) = gcd(b, r). Iterating this replacement yields a sequence of strictly decreasing nonnegative remainders, guaranteeing termination. The last nonzero remainder is gcd(a, b). Formally, if with 0 ≀ r < , then the set of common divisors of (a, b) equals that of (b, r), which proves correctness. BĂ©zout’s identity: For integers a and b, there exist integers x, y such that ax + , b). The extended Euclidean Algorithm computes such (x, y) along with the gcd. If , b), then the linear Diophantine equation ax + has an integer solution if and only if g divides c; one particular solution can be obtained by scaling the BĂ©zout coefficients by c/g. LCM relation: For nonzero a and b, lcm(a, b) is the least positive integer divisible by both, and it satisfies gcd(a, b) × lcm(a, b) = . This follows from unique prime factorization or divisor lattice properties.

04When to Use

  • Reducing fractions: Divide numerator and denominator by gcd(n, d) to obtain a simplified fraction.
  • Period alignment and scheduling: Use lcm(a, b) = a/gcd(a, b) × b to find when events with periods a and b coincide.
  • Modular inverses: When working modulo m (especially in CRT or modular equations), compute inv = a^{-1} mod m using extended Euclid if gcd(a, m) = 1.
  • Solving ax + by = c: Check solvability with gcd(a, b) | c, then construct integer solutions via extended Euclid and parameterization.
  • Array tasks: Compute gcd of an entire array, or all-elements-except-one using prefix/suffix gcds. Useful for problems like “maximum GCD by removing one element” or “normalize vector directions.”
  • Coprimality checks and counting: Quickly test if two numbers are coprime (gcd = 1) or compute gcds across ranges to detect shared structure.
  • Geometry/number theory hybrids: Lattice point counting on line segments uses gcd(|dx|, |dy|). Slopes can be stored in reduced form using gcd.
  • Preprocessing for CRT or modular linear equations: gcd exposes incompatibilities early, saving time and preventing incorrect inverses.

⚠Common Mistakes

  • Overflow in LCM: Computing lcm(a, b) as a * b / gcd(a, b) can overflow 64-bit even if the final result fits. Compute as (a / gcd(a, b)) * b using 128-bit temporaries, and watch for zero cases.
  • Ignoring signs: gcd is defined as nonnegative. Normalize inputs with absolute values; remember that extended coefficients x, y can be negative, but the gcd itself should be ≄ 0.
  • Assuming a modular inverse always exists: inv(a mod m) exists only if gcd(a, m) = 1. Don’t call inverse on non-coprime pairs; check gcd first.
  • Wrong normalization of modular results: After computing an inverse or BĂ©zout coefficient, reduce it into [0, m-1] using (x % m + m) % m to avoid negative residues.
  • Mishandling edge cases: gcd(0, 0) = 0; gcd(0, b) = |b|. Extended Euclid must handle b = 0 correctly. Also, guard division by zero in LCM when one argument is zero.
  • Using recursion without understanding base: In extended Euclid, the base case is b = 0; set x = sign(a), y = 0, gcd = |a|. Forgetting the sign may lead to incorrect coefficients when a < 0.
  • Complexity misconceptions: Although gcd is very fast, repeatedly calling it in tight loops on large inputs can still be O(n log V). Precompute or batch operations when possible.

Key Formulas

Euclidean Step

Explanation: Replacing the pair (a, b) with (b, a mod b) preserves the set of common divisors. Repeating this until the remainder is zero yields the gcd.

LCM–GCD Relation

Explanation: For nonzero a and b, the least common multiple times the greatest common divisor equals the absolute product. This allows computing LCM via GCD safely.

BĂ©zout’s Identity

Explanation: There are always integers x and y representing the gcd as a linear combination of a and b. Extended Euclid computes such x and y.

Diophantine Solvability

Explanation: The linear equation has a solution exactly when c is a multiple of the gcd. If g = gcd(a, b), then scaling Bézout coefficients by c/g gives one solution.

General Diophantine Solution

Explanation: If (x0, y0) solves ax + by = c with g = gcd(a, b), then all solutions are obtained by shifting along this one-parameter family.

Euclidean Complexity

Explanation: The number of Euclidean iterations is proportional to the number of digits of the smaller input. This remains small even for very large numbers.

Scaling Property

Explanation: Multiplying both inputs by k scales the gcd by |k|. Useful for factoring out common multipliers.

Associativity of gcd

Explanation: You can fold gcd over a list by repeatedly applying it pairwise. This underlies prefix/suffix gcd techniques.

Inverse Existence

Explanation: An inverse modulo m exists exactly when a and m are coprime. Extended Euclid provides the inverse when it exists.

Fraction Reduction

Explanation: Divide numerator and denominator by their gcd to get the fraction in lowest terms.

Complexity Analysis

The classic Euclidean Algorithm runs in O(log min(a, b)) iterations, where each iteration performs one modulo operation and a couple of assignments. Intuitively, the remainder strictly decreases and, in the worst case, follows a sequence related to Fibonacci numbers; consecutive Fibonacci pairs maximize the number of steps, which is still logarithmic in the input magnitude. On modern hardware, 64-bit modulo operations are extremely fast, so gcd computations are effectively constant-time for most competitive programming inputs. The extended Euclidean Algorithm has the same asymptotic complexity, O(log min(a, b)), because it performs a constant amount of arithmetic per Euclidean step while back-substituting coefficients. Space usage can be O(1) when implemented iteratively, or O(log min(a, b)) stack space in the recursive version (though the depth is small in practice). Computing the LCM via gcd takes O(log min(a, b)) time to get the gcd, plus O(1) additional arithmetic. Care must be taken to avoid overflow when multiplying large 64-bit integers; using 128-bit temporaries (__int128) or division before multiplication (a / gcd * b) mitigates this. For arrays, folding gcd over n elements costs O(n log V), where V bounds the values. Prefix/suffix gcd preprocessing is O(n log V) to build, and queries like “gcd of all except index i” are answered in O(1). If you need arbitrary range gcd queries, a sparse table achieves O(n log n) preprocessing and O(1) per query. Binary GCD (Stein’s algorithm) offers O(log max(a, b)) bit operations using shifts and subtraction, which can be beneficial when modulus is expensive (e.g., in constrained environments), but for typical 64-bit integers, the classical algorithm is sufficiently fast.

Code Examples

Iterative Euclidean GCD and overflow-safe LCM
1#include <bits/stdc++.h>
2using namespace std;
3
4// Compute gcd(a, b) using the iterative Euclidean Algorithm.
5// Handles negative inputs; returns a nonnegative gcd.
6long long gcd_ll(long long a, long long b) {
7 a = llabs(a);
8 b = llabs(b);
9 while (b != 0) {
10 long long r = a % b; // remainder
11 a = b;
12 b = r;
13 }
14 return a; // gcd(a, 0) = |a|
15}
16
17// Overflow-safe LCM. Returns {ok, value}.
18// If a==0 or b==0, lcm is 0 by convention (for nonzero a,b). If overflow would occur, ok=false.
19pair<bool, long long> lcm_ll(long long a, long long b) {
20 if (a == 0 || b == 0) return {true, 0};
21 long long g = gcd_ll(a, b);
22 // Compute (a/g) * b in 128-bit to avoid overflow, then check bounds.
23 __int128 t = (__int128)(a / g) * (__int128)b;
24 if (t > LLONG_MAX || t < LLONG_MIN) return {false, 0};
25 long long ans = (long long) (t >= 0 ? t : -t); // LCM is nonnegative
26 return {true, ans};
27}
28
29int main() {
30 ios::sync_with_stdio(false);
31 cin.tie(nullptr);
32
33 // Demo: read pairs and print gcd and lcm
34 int q;
35 cout << "Enter number of test cases: ";
36 if (!(cin >> q)) return 0;
37 while (q--) {
38 long long a, b;
39 cout << "Enter a and b: ";
40 cin >> a >> b;
41 long long g = gcd_ll(a, b);
42 auto [ok, l] = lcm_ll(a, b);
43 cout << "gcd(" << a << ", " << b << ") = " << g << '\n';
44 if (ok) cout << "lcm(" << a << ", " << b << ") = " << l << '\n';
45 else cout << "lcm(" << a << ", " << b << ") overflows 64-bit range\n";
46 }
47 return 0;
48}
49

This program implements the Euclidean Algorithm iteratively to compute gcd and uses it to compute lcm cautiously. We divide by gcd before multiplying, and we use 128-bit temporaries to detect overflow safely. The gcd function normalizes inputs with absolute values and returns a nonnegative result.

Time: O(log min(a, b)) per querySpace: O(1)
Extended Euclidean Algorithm and modular inverse
1#include <bits/stdc++.h>
2using namespace std;
3
4// Recursive extended Euclidean Algorithm.
5// Returns gcd(a,b) and finds x,y such that a*x + b*y = gcd(a,b).
6long long ext_gcd(long long a, long long b, long long &x, long long &y) {
7 if (b == 0) {
8 x = (a >= 0 ? 1 : -1); // ensure gcd nonnegative
9 y = 0;
10 return llabs(a);
11 }
12 long long x1, y1;
13 long long g = ext_gcd(b, a % b, x1, y1);
14 // Back-substitute: x = y1, y = x1 - floor(a/b) * y1
15 x = y1;
16 y = x1 - (a / b) * y1;
17 return g;
18}
19
20// Computes modular inverse of a mod m if it exists.
21// Returns pair {exists, inverse_in_[0,m-1]}.
22pair<bool, long long> mod_inverse(long long a, long long m) {
23 long long x, y;
24 long long g = ext_gcd(a, m, x, y);
25 if (g != 1) return {false, 0}; // inverse exists iff gcd(a,m)=1
26 long long inv = (x % m + m) % m; // normalize to [0, m-1]
27 return {true, inv};
28}
29
30int main() {
31 ios::sync_with_stdio(false);
32 cin.tie(nullptr);
33
34 cout << "Enter a and m to compute modular inverse of a mod m (q lines, end with EOF):\n";
35 long long a, m;
36 while (cin >> a >> m) {
37 auto [ok, inv] = mod_inverse(a, m);
38 if (!ok) {
39 cout << "Inverse does not exist since gcd(" << a << ", " << m << ") != 1\n";
40 } else {
41 cout << "Inverse of " << a << " mod " << m << " is " << inv
42 << " (check: " << ( (__int128)a * inv % m ) << ")\n";
43 }
44 }
45 return 0;
46}
47

The extended Euclidean Algorithm computes coefficients x and y such that a·x + m·y = gcd(a, m). When gcd = 1, x is the modular inverse of a modulo m. We normalize x into the range [0, m−1] to get a standard representative. The code reports when no inverse exists.

Time: O(log min(a, m))Space: O(1) additional (O(log min(a, m)) stack due to recursion)
Solving ax + by = c (Linear Diophantine) and shifting to nonnegative x
1#include <bits/stdc++.h>
2using namespace std;
3
4long long ext_gcd(long long a, long long b, long long &x, long long &y) {
5 if (b == 0) { x = (a >= 0 ? 1 : -1); y = 0; return llabs(a); }
6 long long x1, y1; long long g = ext_gcd(b, a % b, x1, y1);
7 x = y1; y = x1 - (a / b) * y1; return g;
8}
9
10// Finds one solution to a*x + b*y = c. Returns {exists, x, y, g}.
11struct Solution { bool ok; long long x, y, g; };
12
13Solution solve_diophantine(long long a, long long b, long long c) {
14 long long x0, y0; long long g = ext_gcd(a, b, x0, y0);
15 if (c % g != 0) return {false, 0, 0, g};
16 long long k = c / g;
17 __int128 xx = (__int128)x0 * k;
18 __int128 yy = (__int128)y0 * k;
19 // Clamp back to 64-bit assuming results fit for given inputs.
20 return {true, (long long)xx, (long long)yy, g};
21}
22
23// Shift solution (x, y) to make x >= 0 if possible.
24// General solution: x = x0 + t*(b/g), y = y0 - t*(a/g).
25void shift_to_nonneg_x(long long a, long long b, long long g, long long &x, long long &y) {
26 long long dx = b / g;
27 long long dy = a / g;
28 if (dx == 0) return; // then a!=0,b==0; x fixed
29 // We need x + t*dx >= 0 -> t >= (-x + dx - 1) // dx (ceiling division)
30 auto ceil_div = [](long long p, long long q) {
31 // ceil(p/q) for integers with q>0
32 if (q < 0) p = -p, q = -q;
33 if (p >= 0) return (p + q - 1) / q;
34 else return p / q; // trunc toward 0 works as ceil when p<0
35 };
36 long long t = ceil_div(-x, dx);
37 x += t * dx;
38 y -= t * dy;
39}
40
41int main() {
42 ios::sync_with_stdio(false);
43 cin.tie(nullptr);
44
45 cout << "Solve ax + by = c (enter a b c, one per line, EOF to stop):\n";
46 long long a, b, c;
47 while (cin >> a >> b >> c) {
48 Solution sol = solve_diophantine(a, b, c);
49 if (!sol.ok) {
50 cout << "No integer solution because gcd(" << a << ", " << b << ") = " << sol.g
51 << " does not divide " << c << "\n";
52 continue;
53 }
54 long long x = sol.x, y = sol.y;
55 cout << "One solution: x = " << x << ", y = " << y << " (check: "
56 << ( (__int128)a * x + (__int128)b * y ) << ")\n";
57 // Optionally find a solution with x >= 0
58 shift_to_nonneg_x(a, b, sol.g, x, y);
59 cout << "Shifted solution with x >= 0: x = " << x << ", y = " << y << "\n";
60 }
61 return 0;
62}
63

We first compute one particular solution (x0, y0) to ax + by = c by scaling the BĂ©zout coefficients by c/g, where g = gcd(a, b). Then we use the general solution family to shift x to be nonnegative: x = x0 + t·(b/g), y = y0 − t·(a/g). This is useful when constraints require x ≄ 0 (e.g., counting solutions in ranges).

Time: O(log min(|a|, |b|)) to find a base solution; shifting is O(1)Space: O(1)
Prefix/Suffix GCD to answer “remove one element” queries on arrays
1#include <bits/stdc++.h>
2using namespace std;
3
4long long gcd_ll(long long a, long long b) {
5 a = llabs(a); b = llabs(b);
6 while (b) { long long r = a % b; a = b; b = r; }
7 return a;
8}
9
10int main() {
11 ios::sync_with_stdio(false);
12 cin.tie(nullptr);
13
14 cout << "Enter n: ";
15 int n; if (!(cin >> n)) return 0;
16 vector<long long> a(n+1);
17 cout << "Enter array of n integers: ";
18 for (int i = 1; i <= n; ++i) cin >> a[i];
19
20 // Build prefix and suffix gcd arrays in O(n)
21 vector<long long> pre(n+2, 0), suf(n+2, 0);
22 for (int i = 1; i <= n; ++i) pre[i] = gcd_ll(pre[i-1], a[i]);
23 for (int i = n; i >= 1; --i) suf[i] = gcd_ll(suf[i+1], a[i]);
24
25 cout << "Enter number of queries q: ";
26 int q; cin >> q;
27 cout << "Each query gives an index i (1-based); output gcd of all elements except a[i].\n";
28 while (q--) {
29 int i; cin >> i;
30 long long ans = gcd_ll(pre[i-1], suf[i+1]);
31 cout << ans << '\n';
32 }
33
34 // Example: gcd of entire array is pre[n] (or suf[1])
35 // Example: gcd of subarray [L..R] could be answered with a sparse table (not shown here).
36
37 return 0;
38}
39

This program preprocesses prefix and suffix gcd arrays so that the gcd of all elements except a[i] can be answered in O(1): gcd(prefix[i−1], suffix[i+1]). This technique is common in problems asking for the best gcd after removing one element or computing contributions excluding a position.

Time: Building O(n log V); each query O(1)Space: O(n)