← Propositiones
Principia P.000003 ·NUMBER_THEORY ·SEED

On the common measure of two magnitudes

De communi mensura duarum magnitudinum

Discharged pending classO(log(min(a,b))) paradigmreduction falsifiable ifThe Euclidean algorithm, by repeatedly replacing the pair (a, b) with (b, a mod b), terminates after finitely many steps and returns the greatest common divisor of a and b.

Enuntiatio

Propositio III. Theorema. Datis duobus numeris non utrisque nullis, eorum maxima communis mensura per divisiones successivas invenitur.

Let two non-negative integers aa and bb be given, not both zero. I say that their greatest common divisor — the greatest integer dividing both without remainder — is found by the following reduction: while bb is not zero, replace the pair (a,b)(a, b) by the pair (b,amodb)(b,\, a \bmod b); when bb becomes zero, the remaining first element aa is the sought divisor. The reduction always terminates, for the second magnitude strictly diminishes within the well-ordered naturals and cannot descend below zero. The number of reductions does not exceed a constant multiple of logmin(a,b)\log\min(a,b), whence the labour is logarithmic in the magnitudes.

Expressio

def gcd(a: int, b: int) -> int:
    """Greatest common divisor of a and b by Euclid's reduction.

    Operates on the absolute values, so signs are immaterial.
    By convention gcd(0, 0) == 0.

    >>> gcd(48, 18)
    6
    >>> gcd(18, 48)   # order is immaterial
    6
    >>> gcd(1071, 462)
    21
    >>> gcd(17, 13)   # coprime magnitudes
    1
    >>> gcd(0, 5)     # zero carries no constraint
    5
    >>> gcd(-48, 18)  # signs are immaterial
    6
    """
    a, b = abs(a), abs(b)
    while b != 0:
        a, b = b, a % b   # the reduction (a, b) -> (b, a mod b)
    return a


# --- Demonstratio per exempla: concrete inline assertions ---
assert gcd(48, 18) == 6        # 48 = 2*18 + 12; 18 = 1*12 + 6; 12 = 2*6 + 0
assert gcd(18, 48) == 6        # symmetry under exchange
assert gcd(0, 0) == 0          # the conventional boundary case
assert gcd(0, 7) == 7          # gcd(0, n) = n
assert gcd(13, 17) == 1        # distinct primes are coprime
assert gcd(100, 100) == 100    # gcd(n, n) = n
assert gcd(1071, 462) == 21    # the classical worked example

if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose=False)
    # Verification against the standard library over a wide range:
    from math import gcd as _ref
    for x in range(0, 200):
        for y in range(0, 200):
            assert gcd(x, y) == _ref(x, y), (x, y)
    print("All assertions hold.")

Demonstratio

We treat a,b0a, b \ge 0, not both zero; signs are discharged at the outset by taking absolute values, since dn    dnd \mid n \iff d \mid |n|, and the case a=b=0a=b=0 returns 00 by convention. Two parts are required: that the reduction preserves the divisor sought (correctness), and that it must halt (termination). A third part bounds the labour.

Lemma (Preservation of common divisors). For b>0b > 0, the pairs (a,b)(a, b) and (b,amodb)(b,\, a \bmod b) have exactly the same set of common divisors; hence the same greatest common divisor.

Write the division with remainder a=qb+ra = qb + r, where q=a/bq = \lfloor a/b \rfloor and r=amodbr = a \bmod b with 0r<b0 \le r < b. Let dd be any integer.

  • Suppose dad \mid a and dbd \mid b. Then dd divides any integer combination of aa and bb; in particular d(aqb)=rd \mid (a - qb) = r. Thus dd divides both bb and rr.
  • Conversely, suppose dbd \mid b and drd \mid r. Then d(qb+r)=ad \mid (qb + r) = a. Thus dd divides both aa and bb.

Therefore the common divisors of {a,b}\{a, b\} and of {b,r}\{b, r\} coincide as sets. Two equal sets of positive integers have the same greatest element, so gcd(a,b)=gcd(b,amodb).\gcd(a, b) = \gcd(b,\, a \bmod b). \qquad \square

Loop invariant (Correctness). Let (a0,b0)(a_0, b_0) be the input pair (already made non-negative), and let (ak,bk)(a_k, b_k) denote the pair held after kk reductions. I claim the invariant gcd(ak,bk)=gcd(a0,b0)holds for every k0.\gcd(a_k, b_k) = \gcd(a_0, b_0) \quad\text{holds for every } k \ge 0.

Base. At k=0k = 0 the pair is (a0,b0)(a_0, b_0) and the invariant is the identity gcd(a0,b0)=gcd(a0,b0)\gcd(a_0,b_0)=\gcd(a_0,b_0).

Step. Suppose it holds after kk reductions and the loop performs one more, which requires bk>0b_k > 0. The new pair is (ak+1,bk+1)=(bk,akmodbk)(a_{k+1}, b_{k+1}) = (b_k,\, a_k \bmod b_k). By the Lemma, gcd(bk,akmodbk)=gcd(ak,bk)\gcd(b_k,\, a_k \bmod b_k) = \gcd(a_k, b_k), which by hypothesis equals gcd(a0,b0)\gcd(a_0, b_0). Hence the invariant survives the step.

When the loop halts it is because b=0b = 0; say this occurs at step KK, so bK=0b_K = 0. Then by the invariant gcd(a0,b0)=gcd(aK,bK)=gcd(aK,0)=aK,\gcd(a_0, b_0) = \gcd(a_K, b_K) = \gcd(a_K, 0) = a_K, the last equality because every integer divides 00, so the common divisors of {aK,0}\{a_K, 0\} are exactly the divisors of aKa_K, the greatest of which is aKa_K itself (and is 00 only in the degenerate all-zero case). The returned value aKa_K is therefore precisely gcd(a0,b0)\gcd(a_0, b_0).

Termination. Consider the second component bb as a measure μ=bk\mu = b_k. After the first reduction the second component is a remainder, and remainders satisfy 0akmodbk<bk0 \le a_k \bmod b_k < b_k. Hence for every step actually taken, 0bk+1<bk.0 \le b_{k+1} < b_k. The sequence b1>b2>b3>b_1 > b_2 > b_3 > \cdots is a strictly decreasing sequence of non-negative integers. By the well-ordering of the naturals, no such sequence is infinite: a strictly descending chain bounded below by 00 has at most b1+1b_1 + 1 terms. Therefore bb reaches 00 after finitely many reductions and the loop halts. There is no infinite regress; the construction is well-founded.

Scholium (the labour is logarithmic). A sharper bound shows the descent is fast. After two reductions the second component is at least halved: if bk+1=akmodbkb_{k+1} = a_k \bmod b_k, then either bk+1bk/2b_{k+1} \le b_k/2 already, or bk+1>bk/2b_{k+1} > b_k/2, in which case the next quotient is exactly 11 and bk+2=bkmodbk+1=bkbk+1<bk/2b_{k+2} = b_k \bmod b_{k+1} = b_k - b_{k+1} < b_k/2. Thus bk+2<bk/2b_{k+2} < b_k/2 in every case, so the second component loses at least one bit every two steps. The number of reductions is therefore O(logmin(a,b))O(\log \min(a,b)), and (the tightest classical result) is largest precisely for consecutive Fibonacci numbers, where the quotients are all 11 — which is why gcd(1071,462)\gcd(1071, 462), lying near a Fibonacci ratio, takes its full complement of steps. The arithmetic operations being taken as unit cost, this is the complexity recorded above.

Both halves established and the bound exhibited, the proposition stands.

Q.E.D.