初心 shoshin

Common Ground: Part One

What the greatest common divisor and least common multiple have in common

You probably learned about greatest common divisors (GCD) and least common multiples (LCM) in school. GCD and LCM are some of the oldest ideas in number theory, and they go way deeper than you might expect. They sit beneath modern cryptography, networking, compilers, elliptic curves, planetary orbits, etc!

This is the first part in a 3-part series I'm doing on GCD and LCM.

In this post, Part 1, we will

In Part 2, we will go over Euclid's Extended Algorithm and its many applications to modern computing. In Part 3, we will extend the definitions of $\mathrm{gcd}$ and $\mathrm{lcm}$ to the rationals.

A quick note on style—I'll use "GCD" and "LCM" to refer to actual numbers, the results of the computations. I'll use $\mathrm{gcd}$ and $\mathrm{lcm}$ to refer to the functions, the computations themselves.

Greatest Common Divisor

The greatest common divisor (GCD) between two non-negative integers is the largest number that divides both evenly, usually written as $\mathrm{gcd}(x, y)$ for two non-negative integers $x$ and $y$.

Examples

GCD Expression Answer Notes
$\mathrm{gcd}(4, 2)$ 2 $4 \div 2 = 2$, $2 \div 2 = 1$
$\mathrm{gcd}(150, 100)$ 50 $150 \div 50 = 3$, $100 \div 50 = 2$
$\mathrm{gcd}(-10, 2)$ 2 We take the absolute value of the arguments 1
$\mathrm{gcd}(8, 3)$ 1 No non-trivial divisors 2
$\mathrm{gcd}(8, 0)$ 8 Both are divisible by 8
$\mathrm{gcd}(0, 0)$ 0 By definition

Prime Factorization

The (arguably) simplest way to calculate GCD is to take the prime factors of each number and multiply together the prime factors they have in common.

Example

This is really only useful for small numbers. There's not even an algorithm for efficiently 3 computing prime factors!

Euclid's Algorithm

There's no simple formula to calculate $\mathrm{gcd}$ for arbitrary numbers, you have to use an algorithm. 4

Luckily, Euclid has us covered. He described his titular algorithm in The Elements, where he used it to compute the GCD of line segments.

It goes like this: subtract the smaller number from the larger. Repeat until both numbers are the same.

Example

Code

def gcd(a: int, b: int) -> int:
  a, b = abs(a), abs(b)

  while a != b:
    if a > b:
      a = a - b
    else:
      b = b - a

  return a

Or recursively:

def gcd(a: int, b: int) -> int:
  a, b = abs(a), abs(b)

  if a == b: return a
  if a > b: return gcd(a - b, b)
  return gcd(a, b - a)

Euclid's Algorithm via Remainders

Iterated subtraction is slow—worst-case, you subtract one number from the other again and again. That gives us an algorithm that runs in roughly proportional time to the size of the smaller number, $O(\min(a,b))$.

Instead, we can take remainders. Each remainder shrinks the numbers dramatically, giving us an $O(\log \min(a, b))$ algorithm—exponentially faster in practice.

Example

Code

def gcd(a: int, b: int) -> int:
  a, b = abs(a), abs(b)

  while a != 0 and b != 0:
    if a > b:
      a = a % b
    else:
      b = b % a

  return a + b # One of a or b must be zero

I wrote like this, because it's easier to see what's happening. However, this algorithm has a shorter and more canonical form:

def gcd(a: int, b: int) -> int:
    a, b = abs(a), abs(b)
    while b:
        a, b = b, a % b
    return a

The greater than check was unnecessary. Do you see why?

Say we're on the first iteration of the loop and b > a. In that case, a % b == a, so the assignment a, b = b, a % b just swaps the two numbers: (a, b) becomes (b, a). Now a is the larger one, and on the next iteration we take the remainder of the larger divided by the smaller. No if statements needed!

In other words, the algorithm naturally orients the numbers so the remainder step is always reducing the larger number—no branch needed. The math enforces the flow.

Each iteration stores the smaller remainder in b. Eventually, the remainder hits zero, which means division was exact. At that moment, a holds the last non-zero value, the GCD.

Least Common Multiple

The LCM of two non-negative integers, $a$ and $b$, is the smallest number that is a multiple of both.

Examples

LCM Expression Answer Notes
$\mathrm{lcm}(4, 2)$ $4$ $2 \cdot 2 = 4$, $4 \cdot 1 = 4$
$\mathrm{lcm}(150, 100)$ $300$ $150 \cdot 2 = 300$, $100 \cdot 3 = 300$
$\mathrm{lcm}(-10, 2)$ $10$ We take the absolute value of the arguments 1
$\mathrm{lcm}(8, 3)$ $24$ $8$ and $3$ are coprime, so just multiply them together
$\mathrm{lcm}(8, 0)$ $0$ Any number times $0$ has LCM $0$, by convention. This also makes sense if you think about it—LCM can't be negative, so 0 is as low as you can go 5
$\mathrm{lcm}(0, 0)$ $0$ By definition

Prime Factorization

LCM can be expressed as prime factors of its arguments just like GCD. But where GCD is equal to the intersection of the prime factors, LCM is equal to the union of the prime factors divided by the intersection (i.e., the GCD).

Also, note that we're talking about multisets here, not normal mathematical sets.

Example

Again, not particularly useful, but it's an interesting number theory fact to be aware of.

The Formula in Terms of GCD

The cool thing about $\mathrm{lcm}$ is that it can be expressed as a simple formula based on $\mathrm{gcd}$:

$$ \mathrm{lcm}(a, b) = \frac{|a \cdot b|}{\mathrm{gcd}(a, b)} $$

The Relationship

The largest that the LCM of two integers, $x$ and $y$, can ever be is $x \cdot y$. This is the case when $x$ and $y$ are coprime.

What if they're not coprime? Take $lcm(20, 30)$. $20 \cdot 30 = 600$. But $600$ is not the LCM of $20$ and $30$. Since $20$ and $30$ are both divisible by $5$, we could divide $600$ by $5$ to get $120$. $120$ is indeed a common multiple of $20$ and $30$, but it's not the least common multiple, because $5$ is not the largest number that divides both $20$ and $30$.

What is the largest number that divides both $20$ and $30$? The GCD! In this case, it's $10$. Take the maximum, $600$ and divide it by $10$ to get $60$. $60$ is the LCM.

Thus, our formula:

$$ \mathrm{lcm}(a, b) = \frac{|a \cdot b|}{\gcd(a, b)} $$

Code

Given gcd:

def lcm(a: int, b: int) -> int:
    a, b = abs(a), abs(b)
    g = gcd(a, b)
    return 0 if g == 0 else (a * b) // g

n-ary LCM/GCD

$\mathrm{gcd}$ and $\mathrm{lcm}$ over the integers are commutative: $\mathrm{gcd}(a, b) = \mathrm{gcd}(b, a)$ (we used this property in the code for Euclid's Algorithm Via Remainders). They are also associative: $\mathrm{gcd}(\mathrm{gcd}(a, b), c) = \mathrm{gcd}(a, \mathrm{gcd}(b, c))$. We can use these properties to apply both $\mathrm{gcd}$ and $\mathrm{lcm}$ to lists of integers.

Code

def gcdn(*args: int) -> int:
  if not args: raise ValueError('Must provide at least one arg')

  res = args[0]
  for i in args[1:]:
    res = gcd(res, i)

  return res

If functional is your thing:

from functools import reduce
def gcdn(*args: int) -> int:
    if not args: raise ValueError('Must provide at least one arg')
    return reduce(gcd, args)

Note that folding (reducing) works here because $\mathrm{gcd}$ and $\mathrm{lcm}$ are associative, while idempotence ensures repeated values don't explode the computation.

That is,

The code for $\mathrm{lcm}$ is nearly identical. Just substitute the call to gcd with lcm.

Conclusion

In this post, we defined $\mathrm{gcd}$ and $\mathrm{lcm}$, showed how they are related to one another, and defined an n-ary version of both.

Coming Soon

Athena Math

If you're weird like me, and you wanna practice calculating GCD and LCM mentally, there's only one app that I know that offers that: Athena Math.

Comments

The spirit of this blog is the "beginner's mind." As such, I would love to hear your thoughts!


Will be set to "Anonymous" if not provided

Markdown supported—use ">" to reply

All comments must be approved first. Blame the bots. 🤖

Replies

Footnotes


  1. $\mathrm{gcd}(-x, y) = \mathrm{gcd}(x, y)$, so we just stick to non-negative (i.e., positive plus zero) integers. ↩︎ ↩︎

  2. If $\mathrm{gcd}(x, y) = 1$, $x$ and $y$ are said to be coprime, also referred to as relatively prime↩︎

  3. By "efficient", we mean "polynomial time or faster." ↩︎

  4. Though, there exists a closed form, $\gcd(a,b)= \prod_{\substack{p \ \text{prime}}} p^{\min\left(v_p(a),\, v_p(b)\right)}$, it's a bit unwieldy. ↩︎

  5. Formally, the classical definition uses least positive common multiple, but since $0$ has no positive multiples, most modern texts define $\mathrm{lcm}(0, b) = 0$ explicitly to preserve the GCD-LCM identity. ↩︎