GCD/LCM Overflow — RSA Signatures Fail 0.1% of Keys
- GCD(a,b) = GCD(b, a mod b) — reduces to GCD(a,0)=a in O(log min(a,b)) steps.
- LCM(a,b) = a*b / GCD(a,b) — always compute via GCD to avoid large intermediate values.
- Extended GCD finds Bézout coefficients x,y such that ax + by = GCD(a,b).
- Euclidean algorithm finds GCD by repeatedly replacing (a,b) with (b, a mod b) until b=0
- Extended GCD also finds coefficients x,y such that a*x + b*y = GCD(a,b)
- LCM(a,b) = a*b // GCD(a,b) — compute as (a//GCD)*b to avoid overflow
- Time complexity: O(log min(a,b)) — at most 93 steps for 64-bit integers
- Production nightmare: overflow in LCM when using a*b before division in languages with fixed-width integers
- Biggest mistake: assuming GCD gives positive result for negative inputs — behavior varies across languages
Quick Fixes for Common GCD/LCM Bugs
GCD returns negative value
python -c "import math; print(math.gcd(-15, 25))"python -c "def ngcd(a,b): return math.gcd(abs(a),abs(b)); print(ngcd(-15,25))"Modular inverse fails for valid coprime numbers
python -c "from math import gcd; print(gcd(15, 26))"python -c "def modinv(a,m): g,x,_ = extended_gcd(a%m,m); return x%m if g==1 else None; print(modinv(3,7))"LCM overflow in C++/Java
C++: `long long lcm = a / gcd(a,b) * b;`Java: `BigInteger lcm = a.divide(gcd).multiply(b);`Production Incident
e * d ≡ 1 mod φ(n) was always satisfied because they used the extended Euclidean algorithm.lcm = (a b) // gcd using 64-bit integers. For some large primes used in RSA, the intermediate product a b overflowed before the division, producing a truncated LCM. This led to an incorrect modular inverse for the private exponent d.lcm = (a // gcd) * b, ensuring the division happens first. Applied the same fix to all LCM uses in the codebase. Added unit tests with large prime test vectors.Production Debug GuideSymptom → Action for common failures
abs(gcd) or ensure both numerator and denominator are non-negative.a and m are coprime: gcd(a, m) == 1. If not, no inverse exists. Also check that a is reduced modulo m before calling extended GCD.math.lcm as a reference.functools.reduce(lambda a,b: a//gcd(a,b)*b, numbers) and monitor intermediate size. If numbers are large, consider using arbitrary-precision integers or stopping early if LCM exceeds a threshold.The Euclidean algorithm is 2300 years old, appears in Euclid's Elements (Book VII, Proposition 2), and is still one of the fastest algorithms relative to input size that exists. For 64-bit numbers, it terminates in at most 93 steps. The RSA cryptographic system — which secures most HTTPS traffic — depends on extended Euclidean to compute private keys. Every time your browser establishes a TLS connection, a variant of this algorithm runs.
The extended Euclidean algorithm (Bézout's identity: ax + by = GCD(a,b)) is the computational backbone of modular inverses. Modular inverses are the backbone of modular arithmetic. Modular arithmetic is the backbone of RSA, Diffie-Hellman, elliptic curve cryptography, and the Chinese Remainder Theorem. The entire edifice of public-key cryptography rests on an algorithm Euclid described in 300 BC.
Euclidean Algorithm for GCD
Based on the identity: GCD(a, b) = GCD(b, a mod b). Repeat until b = 0 — then GCD = a.
def gcd(a: int, b: int) -> int: while b: a, b = b, a % b return a def gcd_recursive(a: int, b: int) -> int: return a if b == 0 else gcd_recursive(b, a % b) def lcm(a: int, b: int) -> int: return a * b // gcd(a, b) # avoid overflow: a // gcd * b print(gcd(252, 105)) # 21 print(gcd(48, 18)) # 6 print(lcm(12, 18)) # 36 print(lcm(4, 6)) # 12 # Python 3.9+ built-in import math print(math.gcd(252, 105)) # 21 print(math.lcm(12, 18)) # 36
6
36
12
21
36
abs().abs().abs() on inputs; beware of overflow in a % b if b == 0.math.gcd is implemented in C and is faster than pure Python.GCD Implementations Across Languages
The Euclidean algorithm is nearly identical in every language. The key differences are handling of negative numbers and integer overflow. Below we show the iterative implementation in six widely used languages: C++, Java, C#, JavaScript, C, and Python (for completeness). All follow the same logic: while b != 0, set (a, b) = (b, a % b).
// C++ (modulo may return negative for negative inputs) #include <cstdlib> int gcd(int a, int b) { a = std::abs(a); b = std::abs(b); while (b) { int t = b; b = a % b; a = t; } return a; } // Java (similar to C++, use Math.abs) public static int gcd(int a, int b) { a = Math.abs(a); b = Math.abs(b); while (b != 0) { int t = b; b = a % b; a = t; } return a; } // BigInteger version: a.gcd(b) // C# (use Math.Abs, modulo is remainder, can be negative) static int Gcd(int a, int b) { a = Math.Abs(a); b = Math.Abs(b); while (b != 0) { int t = b; b = a % b; a = t; } return a; } // JavaScript (Numbers are 64-bit floats, integer-safe up to 2^53) function gcd(a, b) { a = Math.abs(a); b = Math.abs(b); while (b) { let t = b; b = a % b; a = t; } return a; } // C (unsigned avoids sign issues, but handle negative externally) unsigned int gcd(unsigned int a, unsigned int b) { while (b) { unsigned int t = b; b = a % b; a = t; } return a; } // Python (already shown above, included for completeness) # def gcd(a,b): while b: a,b=b,a%b; return a
BigInteger.gcd(). C# has BigInteger.GreatestCommonDivisor. JavaScript does not have a built-in GCD, but it's easy to write. Python's math.gcd is the fastest option.a % b is not a concern (the modulo operation always produces a result with magnitude less than |b|). However, in LCM calculations, multiplication after division is critical. In JavaScript, all numbers are 64-bit floats, so integers up to 2^53 are exact — beyond that, bitwise operations fail. Use BigInt for large numbers in JS.abs(). Always use abs() to ensure a correct, non-negative GCD.Visualizing the Euclidean Algorithm: Step by Step
Let's trace GCD(252, 105) step by step. The algorithm repeatedly replaces (a, b) with (b, a mod b) until b = 0. The final a is the GCD. We can see how quickly the numbers shrink — Lamé's theorem predicts at most 5×3 = 15 steps for 3-digit numbers, but we finish in just 4 steps.
Euclidean vs Binary GCD: Advantages and Disadvantages
Binary GCD (Stein's algorithm) is an alternative that replaces modulo with bit shifts and subtraction, which can be faster on processors with slow division. However, it is more complex and offers no advantage on modern hardware with fast modulo. Below is a comparison.
| Aspect | Euclidean (modulo) | Binary GCD (Stein) |
|---|---|---|
| Algorithmic steps | O(log min(a,b)) | O(log min(a,b)) |
| Worst-case steps | Fibonacci numbers (~93 for 64-bit) | Slightly higher constant |
| Core operation | a % b | >>, &, subtraction |
| Speed on modern CPUs | Very fast (modulo pipeline) | Comparable, no significant edge |
| Speed on slow division CPUs | Slower | Faster (no division) |
| Implementation complexity | Trivial | More complex (handle even cases, loops) |
| Handling negatives | Requires abs() on inputs | Requires abs() on inputs |
| Use cases | General purpose | Embedded systems, microcontrollers |
In most production systems, the standard Euclidean algorithm is preferred due to its simplicity and excellent performance. Binary GCD is reserved for environments where division is especially expensive.
Practice Problems on GCD and LCM
To solidify your understanding, work through these problems from competitive programming platforms. They cover GCD of arrays, coprime pairs, and Euler's totient, which directly build on the Euclidean algorithm.
- [GCD of an Array](https://cses.fi/problemset/task/1080) (CSES) – Compute GCD of a list of numbers. A straightforward application.
- [Coprime Pairs](https://www.codechef.com/problems/COPRIME) (CodeChef) – Count pairs in an array that are coprime. You'll need GCD(given pair) == 1.
- [Euler's Totient Function](https://cses.fi/problemset/task/1081) (CSES) – Compute φ(n) for each n in a list. Formula: φ(n) = n * ∏(1 - 1/p). Needs prime factors and GCD concepts.
- [LCM Sum](https://www.spoj.com/problems/LCMSUM/) (SPOJ) – Sum of LCMs from 1 to n. Requires efficient GCD and divisor enumeration.
- [GCD Extreme](https://www.spoj.com/problems/GCDEX/) (SPOJ) – Sum of GCD of all pairs for numbers up to n. Uses Euler's totient and prefix sums.
- [Modular Inverse](https://www.hackerrank.com/challenges/modular-inverse/problem) (HackerRank) – Compute x such that a*x ≡ 1 mod m. Direct extended GCD.
- [RSA Key Generation](https://www.cryptool.org/en/cto/highlights/rsa-step-by-step) (CrypTool) – Practice key generation involving GCD and modular inverse.
- [Binomial Coefficients and GCD](https://atcoder.jp/contests/abc156/tasks/abc156_d) (AtCoder) – Uses GCD for fraction simplification.
| Algorithm | Steps (64-bit worst-case) | Operations | Pros | Cons |
|---|---|---|---|---|
| Euclidean (iterative) | ~93 | Modulo, subtraction | Simple, correct, widely understood | Modulo is slower than bit shifts on some hardware |
| Binary GCD (Stein) | ~140 | Bit shifts, subtraction | Faster on processors with slow division (e.g., some ARM) | More complex to implement correctly |
| Python math.gcd | Same as Euclidean | C implementation | Fast, handles negatives, built-in | Not available in all Python versions (3.5+) |
🎯 Key Takeaways
- GCD(a,b) = GCD(b, a mod b) — reduces to GCD(a,0)=a in O(log min(a,b)) steps.
- LCM(a,b) = a*b / GCD(a,b) — always compute via GCD to avoid large intermediate values.
- Extended GCD finds Bézout coefficients x,y such that ax + by = GCD(a,b).
- Modular inverse of a mod m exists iff GCD(a,m) = 1 — compute via extended GCD.
- Fibonacci pairs are worst case for Euclidean algorithm — Lamé's theorem bounds at 5×digits steps.
⚠ Common Mistakes to Avoid
Interview Questions on This Topic
- QWhat is the time complexity of the Euclidean algorithm and why?JuniorReveal
- QHow does the extended Euclidean algorithm compute modular inverses?Mid-levelReveal
- QWhen does the modular inverse of a (mod m) not exist?JuniorReveal
- QHow would you compute LCM of an array of numbers without overflow?Mid-levelReveal
- QExplain the worst-case input for the Euclidean algorithm and why it occurs.SeniorReveal
Frequently Asked Questions
Why use a*b//gcd instead of a//gcd*b for LCM?
a//gcdb computes the division first, reducing intermediate size and avoiding integer overflow. Always divide before multiplying: lcm = (a // gcd(a,b)) b.
What is Bézout's identity?
For any integers a and b, there exist integers x and y such that ax + by = GCD(a,b). The extended Euclidean algorithm finds these coefficients. This identity is the theoretical foundation for modular inverses and the Chinese Remainder Theorem.
How does the Euclidean algorithm perform on negative numbers?
The algorithm's correctness relies on the modulo operation. In Python, a % b always returns a non-negative remainder when b > 0, so GCD works correctly. In C++, the remainder can be negative, leading to incorrect results. Always pass absolute values in C++ or use a wrapper.
Can I use the Euclidean algorithm for polynomials?
Yes, the Euclidean algorithm extends to polynomials over a field. The polynomial GCD is defined similarly using polynomial division. This is used in coding theory (Reed-Solomon codes) and computer algebra systems.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.