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 definitions01Overview
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
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
Code Examples
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 // Compute gcd(a, b) using the iterative Euclidean Algorithm. 5 // Handles negative inputs; returns a nonnegative gcd. 6 long 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. 19 pair<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 29 int 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.
1 #include <bits/stdc++.h> 2 using 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). 6 long 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]}. 22 pair<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 30 int 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.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 long 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}. 11 struct Solution { bool ok; long long x, y, g; }; 12 13 Solution 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). 25 void 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 41 int 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).
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 long 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 10 int 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.