Euclid's greatest common divisor
Find the largest number that divides two numbers by repeatedly replacing the pair with the smaller number and the remainder.
O(log(min(a, b)))Space O(1)4 practice problemsStep-through pictureThe idea
The greatest common divisor (GCD) of two whole numbers is the largest number that divides both with nothing left over. The GCD of 48 and 18 is 6: both are multiples of 6, and no larger number divides both.
Euclid's method finds it without listing divisors. It uses one fact: any number that divides both a and b also divides the remainder a % b. So the pair (a, b) has the same GCD as the pair (b, a % b). The remainder is always smaller than b, so the numbers shrink fast. When the remainder reaches 0, the other number is the GCD.
The GCD unlocks a second tool, the least common multiple (LCM): the smallest number that both divide. lcm(a, b) = a / gcd(a, b) * b. Simplifying fractions, aspect ratios, and finding when two repeating cycles line up all use these two.
An everyday example
Tiling a floor with the largest square tile
You want to cover a 48 by 18 floor with square tiles, all the same size, with no cutting. First, lay the biggest squares you can: 18 by 18 squares fit twice, leaving a 12 by 18 strip. The tile that works for the whole floor must also work for that strip. In the strip, 12 by 12 squares fit once, leaving 12 by 6. Then 6 by 6 squares fill 12 by 6 exactly. So 6 by 6 is the largest tile that covers the whole floor.
| In the example | In the code |
|---|---|
| The two sides of the floor | a and b |
| The strip left after laying the big squares | remainder = a % b |
| Moving on to tile the leftover strip | a = b; b = remainder |
| The squares fill the strip exactly | b === 0, so the answer is a |
From the slow way to euclid's greatest common divisor
- Start with the obvious way: try every candidate from the smaller number down to 1, and stop at the first one that divides both. For
630and2002that takes 617 tries before it reaches14. For two numbers near a billion it can take a billion tries, so we need something that doesn't walk through every candidate. - Here is the useful fact. If
ddivides bothaandb, it also divides their differencea - b, becausea - bis a multiple ofdminus another multiple ofd. So the pair(2002, 630)has the same common divisors as(1372, 630). Subtracting the smaller number from the bigger one shrinks the pair without changing the answer. - Subtracting can still be slow. From
2002we subtract630three times (1372,742,112) before the number drops below630. Subtractingbfromauntil the result is smaller thanbis exactly what the remainder does in one step:2002 % 630is112. So replace the pair(a, b)with(b, a % b). - Each new remainder is smaller than the number it was divided by, so the pair keeps shrinking until the second number is
0. Every number divides0, so the common divisors of(a, 0)are just the divisors ofa, and the greatest one isaitself. TakeMath.absfirst so signs don't matter, and repeat the remainder step whileb !== 0. That loop is Euclid's algorithm.
How it works
- Make both numbers non-negative with
Math.abs, because the GCD doesn't depend on the sign. - While
bis not0, computeremainder = a % b. - Move the pair along:
abecomesb, andbbecomesremainder. - When
bis0,ais the GCD. Return it. - For the LCM, divide one number by the GCD first, then multiply:
a / gcd(a, b) * b.
Watch it run
This picture runs the code below on a real input. Press Step to move one step at a time, or Play to watch. The highlighted line is the one that just ran. Change the input to see other cases.
See every step at onceThe same run, laid out side by side
The code
Point at a note to light up the lines it explains.
function gcd(a, b) {a = Math.abs(a);b = Math.abs(b);while (b !== 0) {const remainder = a % b;a = b;b = remainder;}return a;}function lcm(a, b) {if (a === 0 || b === 0) return 0;return Math.abs((a / gcd(a, b)) * b);}
How fast it is
| Time | O(log(min(a, b))) |
|---|---|
| Extra memory | O(1) |
The larger number at least halves every two steps, so the loop runs a number of times that grows with the digits of the numbers. Only two numbers are stored.
- Best case
O(1)bis0, so the loop never runs, orbdividesaat once, likegcd(2002, 14), which needs one step. - Average case
O(log n)Random pairs of numbers below a million take about 12 steps, and below a billion about 18. The count grows with the number of digits.
- Worst case
O(log n)Two neighboring Fibonacci numbers, like
987and610. Every quotient is 1, so the pair shrinks as slowly as possible: 14 steps here, and at most 76 steps for any numbers JavaScript stores exactly.
Common mistakes
- Computing the LCM as
a * b / gcd(a, b). The producta * bcan pass2^53and lose precision even when the LCM itself is small. Divide first:a / gcd(a, b) * b. - Looping from
min(a, b)down to1to find the GCD. It works, but takes up to a billion steps for large numbers. - Forgetting
Math.abs. With negative numbers the result can come out negative. - Stopping when
a % b === 0and returningainstead ofb. The divisor is the smaller number of the pair.
When to use it
Look for these clues in a problem:
- The problem asks for the largest size, step, or unit that divides several numbers evenly.
- You need to simplify a fraction or a ratio, such as
1920:1080to16:9. - Two or more things repeat on different cycles and you need when they line up again (that's the LCM).
- A grid or layout must split evenly into equal tiles.
Check yourself
Answer each one in your head first, then open it.
What happens if a is smaller than b at the start?
Nothing special: a % b is just a, so the first step swaps the pair. The code needs no extra check for it.
Why do the numbers 987 and 610 need more steps than most pairs their size?
They are neighboring Fibonacci numbers, so every quotient is 1 and each remainder is as large as it can be. The pair shrinks as slowly as possible: 14 steps.
Why does lcm divide by the GCD before multiplying?
Because a / gcd(a, b) is a whole number and keeps the result small. Multiplying first can make a product above 2^53, which JavaScript can't store exactly.
Why must the loop stop at b === 0 rather than when a % b === 0?
Stopping when a % b === 0 also works, but then the answer is b, not a, and returning a is a common bug. Running one more step moves that value into a, so the code can always return a.
Practice problems
Stuck on one? Tuto is in the playground too, and gives you hints, never the answer.
0 of 4 solved. Solve one to earn the bronze medal, half for silver, and all of them for gold.
Start practicingGo further
You don't need these to start practicing. Open one when you want more.
Why it works
The reasoning that makes it always right
The loop keeps one promise, true at the top of every loop: the pair (a, b) has exactly the same common divisors as the two original numbers. At the start it's true, because Math.abs doesn't change which numbers divide a value.
Each step keeps the promise. Write a = q × b + remainder. In the trace, 14 divides 2002 and 630, so it divides 2002 - 3 × 630 = 112. Going the other way, anything that divides 630 and 112 also divides 3 × 630 + 112 = 2002. So swapping (2002, 630) for (630, 112) neither adds nor loses a common divisor.
The loop always ends, because remainder is always smaller than b, and a non-negative whole number can't get smaller forever. It also ends fast: within two steps the first number at least halves. If b <= a / 2, the remainder is below b, so below a / 2. If b > a / 2, then a % b = a - b, which is also below a / 2.
When the loop stops, the pair is (a, 0). The common divisors of a and 0 are all the divisors of a, and the greatest of those is a. Since the promise says these are the same common divisors as the original numbers, returning a gives the right answer.
Going deeper
3 more ideas
Why the remainder keeps the same GCD
Write a = q × b + r, where r = a % b. If a number d divides both a and b, it divides q × b, so it also divides a - q × b, which is r. And if d divides b and r, it divides q × b + r = a. So (a, b) and (b, r) have exactly the same common divisors, and the same greatest one.
Why it's so fast
Every two steps, the larger number at least halves. So the number of steps grows with the number of digits, not with the size of the numbers: about O(log(min(a, b))). Even two numbers near a billion need only a few dozen steps.
Edge cases: zero, negatives, and more than two numbers
gcd(a, 0) is a, because every number divides 0. gcd(0, 0) is 0 by convention. Negative inputs give the same GCD as their absolute values. For a list, fold it: gcd(gcd(gcd(x1, x2), x3), x4), since the GCD of many numbers is built one pair at a time.
Work it by hand
Every step in a table, with why
Find gcd(-630, 2002). The first number is negative and smaller than the second, so the trace shows both of those being handled.
a = -630, b = 2002
| Step | a | b | remainder | What happens | Why |
|---|---|---|---|---|---|
| 1 | 630 | 2002 | - | Math.abs on both | The sign is dropped, because -630 and 630 have the same divisors. |
| 2 | 630 | 2002 | 630 | pair becomes (2002, 630) | 630 % 2002 is 630, because 2002 doesn't fit into 630 even once. So the first step just swaps the two numbers. |
| 3 | 2002 | 630 | 112 | pair becomes (630, 112) | 2002 = 3 × 630 + 112. One remainder does the work of three subtractions. |
| 4 | 630 | 112 | 70 | pair becomes (112, 70) | 630 = 5 × 112 + 70. The pair is now far smaller than where it started. |
| 5 | 112 | 70 | 42 | pair becomes (70, 42) | 112 = 1 × 70 + 42. A quotient of 1 is the slowest kind of step: the numbers shrink only a little. |
| 6 | 70 | 42 | 28 | pair becomes (42, 28) | 70 = 1 × 42 + 28. Another slow step. |
| 7 | 42 | 28 | 14 | pair becomes (28, 14) | 42 = 1 × 28 + 14. |
| 8 | 28 | 14 | 0 | pair becomes (14, 0) | 28 = 2 × 14 + 0. The remainder is 0, so 14 divides 28 exactly. |
| 9 | 14 | 0 | - | b === 0: return 14 | The loop condition fails. Every number divides 0, so the answer is a. |
The GCD is 14, found in 7 loop steps instead of 617 tries. Then lcm(-630, 2002) divides first: 630 / 14 = 45, and 45 × 2002 = 90090.
Compared with other ways
When to pick something else
| Approach | Time | Extra memory | Choose it when |
|---|---|---|---|
| Euclid's greatest common divisor | O(log n) | O(1) | Almost always: it's short, exact, and fast for any size of whole number. |
| Count down from the smaller number | O(n) | O(1) | Only for tiny numbers in throwaway code. It takes up to min(a, b) tries. |
| Compare prime factorizations | O(√n) | O(log n) | When you need the factorizations anyway, for example to count divisors. Factoring is much slower than Euclid for large numbers. |
Extra memory: O(1): two numbers and a remainder. A recursive version, gcd(b, a % b), uses O(log n) space for its call stack.
How the steps grow
- Euclid's greatest common divisor
O(log n) - Count down from the smaller number
O(n) - Compare prime factorizations
O(√n)
| Approach | n = 10 | n = 1,000 | n = 1,000,000 |
|---|---|---|---|
| Euclid's greatest common divisor | 3 | 10 | 20 |
| Count down from the smaller number | 10 | 1,000 | 1 million |
| Compare prime factorizations | 3 | 32 | 1,000 |
Inputs that need care
6 tricky inputs
- Both zero:
gcd(0, 0) - The loop never runs and it returns
0. Every number divides 0, so there is no greatest one; returning 0 is the usual rule.lcmreturns0before callinggcd. - One zero:
gcd(0, 9) - The first step computes
0 % 9 = 0, so the pair becomes(9, 0)and it returns9.gcd(9, 0)returns9without looping. - Negative numbers:
gcd(-630, 2002) Math.absmakes both positive first, so the answer is14, never-14.- The smaller number first:
gcd(630, 2002) - No swap is needed. The first remainder is
630 % 2002 = 630, which moves the pair to(2002, 630)by itself. - Decimals:
gcd(0.3, 0.1) - The code expects whole numbers. Decimal remainders aren't exact, so it returns about
2.8e-17instead of0.1. Scale decimals to whole numbers first, such as cents instead of dollars. - A big LCM:
lcm(6451993423, 4212802097) - The product
a * bis far past2^53, soa * b / gcdcomes out as3949000642487957.5, a fraction. Dividing first gives the exact3949000642487957.
Variations and follow-ups
Twists interviewers add
Extended Euclid
Keep track of how each remainder is built from the original a and b, and you also get whole numbers x and y with a × x + b × y = gcd(a, b). When the GCD is 1, x is the modular inverse of a modulo b. This works even when the modulus isn't prime, where Fermat's shortcut doesn't.
GCD of a whole list
Fold the list one pair at a time: values.reduce((g, v) => gcd(g, v), 0). Starting at 0 works because gcd(0, v) is v. You can stop early once the running GCD is 1, since nothing can make it smaller.
Binary GCD
Stein's method replaces % with halving and subtraction: pull out shared factors of 2, then subtract the smaller odd number from the larger. It uses only shifts and subtraction, which can be faster for very large BigInt values where division is costly.
Recursive form
gcd(a, b) is a when b is 0, and gcd(b, a % b) otherwise. It's a one-line definition that matches the proof exactly. The loop version does the same steps without growing the call stack.
In an interview
Spot it, explain it, answer follow-ups
- Spot it from words like "simplify the fraction", "aspect ratio", "largest square tile that fits", "largest group size that divides both", or "when do two repeating cycles line up again" (that last one is the LCM).
- Explain it in two sentences: "
(a, b)and(b, a % b)have the same common divisors, so I can keep replacing the pair. The numbers at least halve every two steps, so it'sO(log n)." - Test by hand:
gcd(0, 5),gcd(5, 0), a negative input, and the smaller number first. Each one takes a line of thought, and each one shows a different part of the code doing its job. - The usual follow-up is the LCM. Say "divide before multiplying" and write
a / gcd(a, b) * b, explaining thata * bcan pass2^53even when the LCM fits.
Where frontend code uses it
The same idea in real UI code
- Showing an aspect ratio:
1920 × 1080divided by their GCD120gives16:9, which an image editor or a video settings panel can display. - CSS animations with different durations, such as
400msand600ms, line up again after their LCM,1200ms. Knowing that tells you when a combined loop repeats. - Design tools that snap elements to a shared spacing unit can use the GCD of the spacings in a layout.