Numerical algorithms¶
Part 1 · Basic algorithms · Chapter 5 · Lecture notes, volume 1 (PDF) · Exercises: Numerical algorithms · App: Euclid and the sieve of Eratosthenes
Introduction¶
In this chapter we study four classical algorithms that work on natural numbers:
-
the change of base, which writes a natural number given in base ten as a sequence of digits in another base \(b\);
-
the primality test, which decides whether a natural number is prime;
-
the Sieve of Eratosthenes, which finds all the prime numbers up to a given natural number;
-
the Euclidean algorithm, which computes the greatest common divisor of two natural numbers.
All four algorithms are built on one elementary operation, the division with remainder: the change of base reads the digits as remainders, the primality test and the sieve look for remainders equal to zero, and the Euclidean algorithm replaces a pair of numbers with a smaller pair through a remainder. For each algorithm we give the pseudo-code (with arrays indexed from \(1\)) and an execution step by step; the implementations in Python, C++ and Java (with arrays indexed from \(0\)), run on the same inputs, are in the Colab notebooks of the chapter.
5.1 Change of base¶
In the chapter «Representation of numbers» we saw that a sequence of \(k\) digits \((d_{k-1} \dots d_1 d_0)_b\) in base \(b\) represents the natural number \(\sum_{i=0}^{k-1} d_i \, b^i\), and that \(\lfloor \log_b n \rfloor + 1\) digits are needed to write a natural number \(n \ge 1\) in base \(b\). In this section we solve the inverse problem: given \(n\) in base ten, we compute its digits in base \(b\).
5.1.1 Division with remainder¶
Definition 5.1: division with remainder
Given a natural number \(n\) and a natural number \(b \ge 1\), the division with remainder (or Euclidean division) of \(n\) by \(b\) computes the two natural numbers \(q\) and \(r\) such that
The number \(n\) is called the dividend, \(b\) the divisor, \(q\) the quotient and \(r\) the remainder. The two numbers \(q\) and \(r\) are unique, and we write
The key fact behind the change of base is that one division by \(b\) extracts the least significant digit of \(n\) in base \(b\), and leaves as quotient the number formed by the remaining digits.
Observation 5.2
Let \(n \ge 1\) be a natural number whose representation in base \(b \ge 2\) is \((d_{k-1} \dots d_1 d_0)_b\). Then
where the quotient is \(0\) if \(k = 1\).
Proof
Collecting the factor \(b\) from all the terms with \(i \ge 1\), we obtain
Since \(d_0\) is a digit in base \(b\), we have \(0 \le d_0 < b\). By the uniqueness of the quotient and of the remainder of the division with remainder, the quotient of \(n\) divided by \(b\) is \(\sum_{i=1}^{k-1} d_i \, b^{i-1}\) and the remainder is \(d_0\). □
Dividing \(n\) by \(b\), the remainder is the least significant digit \(d_0\), and the quotient is the number written with the other digits \((d_{k-1} \dots d_1)_b\). Dividing the quotient again by \(b\) gives \(d_1\), and so on: repeating the division until the quotient is \(0\) produces all the digits of \(n\) in base \(b\), from the least significant to the most significant one, in exactly \(\lfloor \log_b n \rfloor + 1\) divisions.
5.1.2 The algorithm¶
Given a natural number \(n>0\) represented in base 10 (decimal) and a target base \(b \geq 2\), we consider the problem of converting \(n\) to base \(b\).
- Pseudo-code of the algorithm to convert sequences of digits from base ten into base \(b\):
Algorithm: ChangeOfBase
-
Explanation:
-
The algorithm calculates the number of digits \(nd\) needed to represent \(n\) in base \(b\) using the formula \(\lfloor \log_b n \rfloor + 1\).
-
It initializes an array \(d[1..nd]\) to store the digits and a counter \(k\) to track the current position.
-
The main loop repeatedly divides \(n\) by \(b\) to extract the digits from right to left (from the least significant to the most significant one).
-
At each iteration, the remainder \(r = n \bmod b\) gives the next digit, which is stored in \(d[k]\).
-
The quotient \(q = \lfloor n / b \rfloor\) becomes the new value of \(n\) for the next iteration.
-
The algorithm terminates when \(n = 0\), returning the array \(d\) and the total number of digits \(nd\).
-
The digits in \(d\) are stored in reverse order: the entry \(d[k]\) is the digit \(d_{k-1}\) of weight \(b^{k-1}\), so \(d[1] = d_0\) is the least significant digit (rightmost) and \(d[nd] = d_{nd-1}\) is the most significant digit (leftmost).
-
Example 5.1: conversion from base ten into base \(b\)
-
Translation of \(7\) into base \(b=2\):
iteration \(k\) \(n\) \(n/b\) \(q\) \(r\) digit \(1\) \(7\) \(7/2\) \(3\) \(1\) \(d[1] = d_{0}\) (LSD) \(2\) \(3\) \(3/2\) \(1\) \(1\) \(d[2] = d_{1}\) \(3\) \(1\) \(1/2\) \(0\) \(1\) \(d[3] = d_{2}\) (MSD) Accordingly, \(7\) in base \(2\) is \((111)_{2}\), with \(nd = \lfloor \log_{2} 7 \rfloor + 1 = 3\) digits.
-
Translation of \(8\) into base \(b=2\):
iteration \(k\) \(n\) \(n/b\) \(q\) \(r\) digit \(1\) \(8\) \(8/2\) \(4\) \(0\) \(d[1] = d_{0}\) (LSD) \(2\) \(4\) \(4/2\) \(2\) \(0\) \(d[2] = d_{1}\) \(3\) \(2\) \(2/2\) \(1\) \(0\) \(d[3] = d_{2}\) \(4\) \(1\) \(1/2\) \(0\) \(1\) \(d[4] = d_{3}\) (MSD) Accordingly, \(8\) in base \(2\) is \((1000)_{2}\), with \(nd = \lfloor \log_{2} 8 \rfloor + 1 = 4\) digits.
-
Translation of \(25\) into base \(b=3\):
iteration \(k\) \(n\) \(n/b\) \(q\) \(r\) digit \(1\) \(25\) \(25/3\) \(8\) \(1\) \(d[1] = d_{0}\) (LSD) \(2\) \(8\) \(8/3\) \(2\) \(2\) \(d[2] = d_{1}\) \(3\) \(2\) \(2/3\) \(0\) \(2\) \(d[3] = d_{2}\) (MSD) Accordingly, \(25\) in base \(3\) is \((221)_{3}\), with \(nd = \lfloor \log_{3} 25 \rfloor + 1 = 3\) digits.
-
Translation of \(100\) into base \(b=8\):
iteration \(k\) \(n\) \(n/b\) \(q\) \(r\) digit \(1\) \(100\) \(100/8\) \(12\) \(4\) \(d[1] = d_{0}\) (LSD) \(2\) \(12\) \(12/8\) \(1\) \(4\) \(d[2] = d_{1}\) \(3\) \(1\) \(1/8\) \(0\) \(1\) \(d[3] = d_{2}\) (MSD) Accordingly, \(100\) in base \(8\) is \((144)_{8}\), with \(nd = \lfloor \log_{8} 100 \rfloor + 1 = 3\) digits.
Key points.
-
The remainder of the division of \(n\) by \(b\) is the least significant digit of \(n\) in base \(b\); the quotient is the number written with the other digits.
-
The algorithm performs one division for each digit: \(\lfloor \log_b n \rfloor + 1\) iterations.
-
The digits are produced from the least significant to the most significant one, so the result is read backwards: from \(d[nd]\) to \(d[1]\).
5.2 Primality test¶
The change of base uses the remainder \(n \bmod b\) to read a digit. A remainder equal to zero has a meaning of its own: it says that the division is exact, that is, that \(b\) is a divisor of \(n\). Counting divisors leads to the prime numbers.
Definition 5.3: divisor
A natural number \(d \ge 1\) is a divisor of a natural number \(n\) if there exists a natural number \(k\) such that \(n = k \; d\):
or equivalently, if \(n \bmod d = 0\).
Definition 5.4: prime number
A natural number \(n \ge 2\) is prime if its only divisors are \(1\) and \(n\) itself. A natural number \(n \ge 2\) which is not prime is called composite: it has a divisor \(d\) with \(2 \le d \le n-1\). The numbers \(0\) and \(1\) are not prime.
Given a natural number \(n\), we consider the problem of determining whether \(n\) is a prime number.
5.2.1 A first algorithm¶
The definition suggests a direct algorithm: test, one by one, the candidate divisors of \(n\) different from \(1\) and \(n\). The following observation shows that the candidates larger than \(n/2\) can be skipped.
Observation 5.5
For every natural number \(n \ge 2\), if \(d\) is a divisor of \(n\) with \(d < n\), then \(d \le n/2\).
Proof
Since \(d\) is a divisor of \(n\), there exists a natural number \(k\) such that \(n = k \; d\). Since \(d < n\), we have \(k \ge 2\), hence
□
-
It follows that it suffices to check whether any natural number \(d\) in \(\{2, 3, \dots, \lfloor n/2 \rfloor\}\) is a divisor of \(n\). If no such divisor exists, \(n\) is prime.
-
Pseudo-code of the Basic Primality Test algorithm:
Algorithm: BasicPrimalityTest
true if \(n\) is prime, false otherwisefalse// 0 and 1 are not primefalse// \(d\) divides \(n\): not primetrue// No divisor found: \(n\) is prime-
Explanation of the algorithm:
-
Numbers less than \(2\) are not prime by definition.
-
The loop tests every candidate divisor \(d\) from \(2\) up to \(\lfloor n/2 \rfloor\).
-
If \(d\) is a divisor of \(n\) (i.e., \(n \bmod d = 0\)), the function returns
falseimmediately. -
If the loop completes without finding a divisor, \(n\) is prime and the function returns
true.
-
-
For a prime number the loop is executed completely: for \(n = 29\) the algorithm tests the \(13\) candidates \(d = 2, 3, \dots, 14\). We now show that far fewer candidates are enough.
5.2.2 Testing the divisors up to \(\sqrt{n}\)¶
The divisors of a natural number come in pairs: \(d\) and \(n/d\). In each pair, one of the two numbers is at most \(\sqrt{n}\).
Observation 5.6
For every natural number \(n \ge 2\), if \(d\) is a divisor of \(n\) then \(n/d\) is a divisor of \(n\).
Proof
If \(d\) is a divisor of \(n\), then there exists a natural number \(k\) such that
Since \(k = n/d\), it follows that \(n/d\) is a natural number. Accordingly,
so \(n/d\) is also a divisor of \(n\). □
Observation 5.7
For every natural number \(n \ge 2\), if \(d > \sqrt{n}\) then \(n/d < \sqrt{n}\).
Proof
If \(d > \sqrt{n}\), dividing \(n\) by both sides of this inequality, we obtain
Therefore, \(n/d\) is smaller than \(\sqrt{n}\). □
Corollary 5.8
Every composite natural number \(n \ge 2\) has a divisor \(d\) with \(2 \le d \le \lfloor \sqrt{n} \rfloor\). Equivalently, a natural number \(n \ge 2\) is prime if and only if no natural number in \(\{2, 3, \dots, \lfloor \sqrt{n} \rfloor\}\) is a divisor of \(n\).
Proof
Since \(n\) is composite, it has a divisor \(d\) with \(2 \le d \le n-1\). If \(d \le \sqrt{n}\), then \(d \le \lfloor \sqrt{n} \rfloor\), because \(d\) is a natural number, and we are done. Otherwise \(d > \sqrt{n}\): by the two previous observations, \(n/d\) is a divisor of \(n\) with \(n/d < \sqrt{n}\), and \(n/d \ge 2\) because \(d < n\). Hence \(n/d\) is a divisor of \(n\) with \(2 \le n/d \le \lfloor \sqrt{n} \rfloor\). □
From the previous observations, we know that for every natural number \(n \ge 2\), if \(d > \sqrt{n}\) is a divisor of \(n\), then \(n/d\) is also a divisor of \(n\) and \(n/d < \sqrt{n}\). It follows that every divisor of \(n\) greater than \(\sqrt{n}\) is paired with a divisor of \(n\) smaller than \(\sqrt{n}\). Therefore, if \(n\) has any divisor at all (other than \(1\) and \(n\)), at least one of them must be \(\le \sqrt{n}\).
Example 5.3: illustration of the property
Let us consider \(n = 36\): \(\sqrt{36} = 6\). The divisors of \(36\) other than \(1\) and \(36\) are \(2, 3, 4, 6, 9, 12, 18\).
| divisor \(d > \sqrt{36}\) | paired divisor \(36/d < \sqrt{36}\) |
|---|---|
| \(9\) | \(36/9 = 4\) |
| \(12\) | \(36/12 = 3\) |
| \(18\) | \(36/18 = 2\) |
Every divisor \(> 6\) is paired with a divisor \(< 6\); the divisor \(6 = \sqrt{36}\) is paired with itself.
-
By the corollary, the loop of the basic algorithm can stop at \(\lfloor \sqrt{n} \rfloor\) instead of \(\lfloor n/2 \rfloor\).
-
Pseudo-code of the Primality Test algorithm:
Algorithm: PrimalityTest
true if \(n\) is prime, false otherwisefalse// 0 and 1 are not primefalse// \(d\) divides \(n\): not primetrue// No divisor found: \(n\) is prime-
Explanation of the algorithm:
-
Numbers less than \(2\) are not prime by definition.
-
The loop tests every candidate divisor \(d\) from \(2\) up to \(\lfloor\sqrt{n}\rfloor\).
-
If \(d\) divides \(n\) (i.e., \(n \bmod d = 0\)), the function returns
falseimmediately. -
If the loop completes without finding a divisor, then, by the corollary, \(n\) is prime and the function returns
true. -
For \(n = 2\) and \(n = 3\) we have \(\lfloor \sqrt{n} \rfloor = 1 < 2\): the loop is not executed and the function returns
true.
-
Example 5.4: primality test
-
Check \(n = 36\): \(\lfloor\sqrt{36}\rfloor = 6\)
\(d\) \(36 \bmod d\) action \(2\) \(0\) return false36 is not prime (\(36 = 2 \cdot 18\)). -
Check \(n = 29\): \(\lfloor\sqrt{29}\rfloor = 5\)
\(d\) \(29 \bmod d\) action \(2\) \(1 \ne 0\) continue \(3\) \(2 \ne 0\) continue \(4\) \(1 \ne 0\) continue \(5\) \(4 \ne 0\) continue Loop ends. Return true: 29 is prime. -
Check \(n = 25\): \(\lfloor\sqrt{25}\rfloor = 5\)
\(d\) \(25 \bmod d\) action \(2\) \(1 \ne 0\) continue \(3\) \(1 \ne 0\) continue \(4\) \(1 \ne 0\) continue \(5\) \(0\) return false25 is not prime (\(25 = 5 \cdot 5\)).
For the prime number \(29\) the algorithm tests \(4\) candidates, while the basic algorithm tests \(13\).
Key points.
-
The divisors of \(n\) come in pairs \(d\) and \(n/d\), and in each pair one of the two numbers is at most \(\sqrt{n}\).
-
Hence \(n \ge 2\) is prime if and only if no natural number in \(\{2, 3, \dots, \lfloor \sqrt{n} \rfloor\}\) divides it.
-
The Primality Test executes at most \(\lfloor \sqrt{n} \rfloor - 1\) iterations, instead of the \(\lfloor n/2 \rfloor - 1\) iterations of the basic algorithm.
5.3 Sieve of Eratosthenes¶
The Primality Test answers the question for one number. When we need all the prime numbers up to \(n\), calling it on \(2, 3, \dots, n\) repeats many divisions; the sieve works the other way around: instead of searching for the divisors of each number, it crosses out the multiples of each prime.
Given a natural number \(n\), we consider the problem of determining all prime numbers smaller than or equal to \(n\).
-
The Sieve of Eratosthenes is an ancient algorithm (attributed to the Greek mathematician Eratosthenes of Cyrene, c. 276–194 BC) that computes all prime numbers up to a natural number \(n\).
-
The key idea is to start with all natural numbers from \(2\) to \(n\) marked as potentially prime, and then iteratively mark the multiples of each prime as composite.
Definition 5.9: multiple
A natural number \(m\) is a multiple of a natural number \(p \ge 1\) if there exists a natural number \(k\) such that \(m = k \; p\):
or equivalently, if \(m \bmod p = 0\).
The first observation tells where the marking of the multiples of \(p\) can start.
Observation 5.10
For every natural number \(p \ge 2\), if a natural number \(m\) is a multiple of \(p\) with \(p < m < p^2\), then \(m\) has a divisor \(d\) with \(2 \le d < p\).
Proof
Since \(m\) is a multiple of \(p\), there exists a natural number \(d\) such that \(m = d \; p\). Then \(d\) is a divisor of \(m\).
Moreover, since \(m > p\) and \(m = d \; p\), we have \(d \ge 2\). Since \(m < p^2\) and \(m = d \; p\), we have \(d < p\). Hence \(2 \le d < p\). □
From the previous observation, for every prime \(p\) and every multiple \(m\) of \(p\) with \(p < m < p^2\), the number \(m\) has a divisor \(d\) with \(2 \le d < p\). Hence \(m\) also has a prime divisor \(q\) with \(2 \le q < p\), and was therefore already marked as composite when \(q\) was processed. Thus, when marking the multiples of \(p\), one can start with \(p^2\).
Example 5.6: illustration of the property
Let \(p = 5\). Its multiples between \(p\) and \(p^2 = 25\) are \(m = 10, 15, 20\).
| multiple \(m\) | \(m = d \cdot p\) | divisor \(d < p\) | already marked when |
|---|---|---|---|
| \(10\) | \(10 = 2 \cdot 5\) | \(d = 2\) | \(p = 2\) (from \(j = 4\), step \(2\)) |
| \(15\) | \(15 = 3 \cdot 5\) | \(d = 3\) | \(p = 3\) (from \(j = 9\), step \(3\)) |
| \(20\) | \(20 = 4 \cdot 5\) | \(d = 4\) | \(p = 2\) (from \(j = 4\), step \(2\)) |
The number \(20\) has the divisor \(d = 4\), which is not prime, and also the prime divisor \(2\), which marked it. The next multiple is \(25 = 5 \cdot 5 = p^2\), which has not been marked by any previous prime number. Therefore, for \(p = 5\), one can start marking numbers from \(25\).
The second observation tells where the outer loop over \(p\) can stop.
Observation 5.11
For every natural number \(n \ge 2\), every composite natural number \(m \le n\) has a prime divisor \(q\) with \(q \le \lfloor \sqrt{n} \rfloor\).
Proof
By the corollary of the section «Primality test», \(m\) has a divisor \(d\) with \(2 \le d \le \lfloor \sqrt{m} \rfloor \le \lfloor \sqrt{n} \rfloor\). Let \(q\) be the smallest divisor of \(m\) with \(q \ge 2\); then \(q \le d \le \lfloor \sqrt{n} \rfloor\). The number \(q\) is prime: if \(q\) had a divisor \(c\) with \(2 \le c < q\), then \(c\) would also be a divisor of \(m\) (from \(q = k_1 c\) and \(m = k_2 q\) we get \(m = k_2 k_1 c\)), against the choice of \(q\) as the smallest one. □
-
Hence, after the multiples of all the primes \(p \le \lfloor \sqrt{n} \rfloor\) have been marked, every composite number up to \(n\) is marked, and the outer loop can stop.
-
Pseudo-code of the Sieve of Eratosthenes:
Algorithm: SieveOfEratosthenes
-
Explanation of the algorithm:
-
The array \(prime\) is declared and all entries initialized to \(1\); the entry \(prime[1]\) is immediately set to \(0\), since \(1\) is not prime.
-
The outer loop iterates over each candidate prime \(p\) from \(2\) up to \(\lfloor\sqrt{n}\rfloor\).
-
If \(prime[p] = 1\), then \(p\) is prime (a composite \(p\) would have been marked by its smallest prime divisor), and all its multiples starting from \(p^2\) are set to \(0\).
-
The inner loop starts at \(p^2\) because all smaller multiples \(2p, 3p, \ldots, (p{-}1)p\) have already been set to \(0\) when processing the smaller primes \(2, 3, 5, 7, \ldots\).
-
After the execution of the algorithm, \(prime[i] = 1\) if and only if \(i\) is prime.
-
Example 5.7: Sieve of Eratosthenes (\(n=10\))
With \(n=10\) (\(\lfloor\sqrt{10}\rfloor = 3\)), each row shows the state of the array \(prime[1..10]\) after the assignment \(prime[j] \leftarrow 0\). Blue cells indicate entries just set to \(0\); red cells indicate the current value of \(p\).
| \(prime[1..10]\) | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| \(p\) | \(j\) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | action |
| \(-\) | \(-\) | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | initialization |
| 2 | 4 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[4] \leftarrow 0\) |
| 6 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | \(prime[6] \leftarrow 0\) | |
| 8 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | \(prime[8] \leftarrow 0\) | |
| 10 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | \(prime[10] \leftarrow 0\) | |
| 12 | \(j = 12 > 10\): stop | |||||||||||
| 3 | 9 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | \(prime[9] \leftarrow 0\) |
| 12 | \(j = 12 > 10\): stop | |||||||||||
| Outer loop ends: \(p = 4 > \lfloor\sqrt{10}\rfloor = 3\). | ||||||||||||
Result: the prime numbers \(\le 10\) are \(2,\ 3,\ 5,\ 7\).
Example 5.8: Sieve of Eratosthenes (\(n=20\))
With \(n=20\) (\(\lfloor\sqrt{20}\rfloor = 4\)), each row shows the state of the array \(prime[1..20]\) after the assignment \(prime[j] \leftarrow 0\). Blue cells: entry just set to \(0\); red cells: current value of \(p\). When the entry is already \(0\), the assignment does not change the array.
| \(prime[1..20]\) | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| \(p\) | \(j\) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | action |
| \(-\) | \(-\) | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | initialization |
| 2 | 4 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[4] \leftarrow 0\) |
| 6 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[6] \leftarrow 0\) | |
| 8 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[8] \leftarrow 0\) | |
| 10 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[10] \leftarrow 0\) | |
| 12 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[12] \leftarrow 0\) | |
| 14 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | \(prime[14] \leftarrow 0\) | |
| 16 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | \(prime[16] \leftarrow 0\) | |
| 18 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | \(prime[18] \leftarrow 0\) | |
| 20 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | \(prime[20] \leftarrow 0\) | |
| 22 | \(j = 22 > 20\): stop | |||||||||||||||||||||
| 3 | 9 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | \(prime[9] \leftarrow 0\) |
| 12 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | \(prime[12] \leftarrow 0\) (already \(0\)) | |
| 15 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | \(prime[15] \leftarrow 0\) | |
| 18 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | \(prime[18] \leftarrow 0\) (already \(0\)) | |
| 21 | \(j = 21 > 20\): stop | |||||||||||||||||||||
| 4 | \(prime[4] = 0\): skip | |||||||||||||||||||||
| Outer loop ends: \(p = 5 > \lfloor\sqrt{20}\rfloor = 4\). | ||||||||||||||||||||||
Result: the prime numbers \(\le 20\) are \(2,\ 3,\ 5,\ 7,\ 11,\ 13,\ 17,\ 19\).
Key points.
-
The sieve does not divide: it marks as composite the multiples of each prime \(p\).
-
The marking of the multiples of \(p\) starts at \(p^2\): the smaller multiples have a smaller prime divisor and are already marked.
-
The outer loop stops at \(\lfloor \sqrt{n} \rfloor\): every composite number up to \(n\) has a prime divisor not larger than \(\lfloor \sqrt{n} \rfloor\).
5.4 Greatest common divisor¶
The last algorithm of the chapter deals with two numbers at once: we look for the divisors they have in common.
Definition 5.12: common divisor and greatest common divisor
Let \(n, m\) be two natural numbers with \(n, m \ge 1\).
-
A natural number \(d \ge 1\) is a common divisor of \(n\) and \(m\) if \(d\) is a divisor of both \(n\) and \(m\), that is, if there exist natural numbers \(k_1, k_2\) such that
\[ n = k_1 \; d \qquad \text{and} \qquad m = k_2 \; d. \]Equivalently, \(n \bmod d = 0\) and \(m \bmod d = 0\).
-
The greatest common divisor of \(n\) and \(m\), denoted \(\gcd(n, m)\), is the largest common divisor of \(n\) and \(m\).
-
The greatest common divisor always exists: \(1\) is a common divisor, and every common divisor is at most \(\min\{n, m\}\).
-
For instance, the common divisors of \(48\) and \(18\) are \(1, 2, 3, 6\), hence \(\gcd(48, 18) = 6\).
Given two natural numbers \(n, m\) with \(n, m \ge 1\), we consider the problem of computing their greatest common divisor.
The following observation replaces the pair \((n, m)\) with a pair of smaller numbers which has the same greatest common divisor.
Observation 5.13
For every pair of natural numbers \(n \ge 1\) and \(m \ge 1\),
Proof
Let \(q = \lfloor n/m \rfloor\), so that \(n = q m + (n \bmod m)\).
Let \(d_1\) be a common divisor of \(n\) and \(m\). Then there exist natural numbers \(k_1, k_2\) such that
Since \(n \bmod m = n - qm\), we obtain
where \(k_1 - q k_2 \ge 0\) because \(n \bmod m \ge 0\). So \(d_1\) is also a divisor of \(n \bmod m\). Hence \(d_1\) is a common divisor of \(m\) and \(n \bmod m\).
Conversely, let \(d_2\) be a common divisor of \(m\) and \(n \bmod m\). Then there exist natural numbers \(k_1, k_2\) such that
Since \(n = qm + (n \bmod m)\), we obtain
so \(d_2\) is also a divisor of \(n\). Hence \(d_2\) is a common divisor of \(n\) and \(m\).
Therefore, \(n\) and \(m\) have exactly the same common divisors as \(m\) and \(n \bmod m\), and hence
In the right-hand side, \(\gcd(m, 0)\) is understood as the largest divisor of \(m\) (every \(d \ge 1\) divides \(0 = 0 \cdot d\)), that is, \(m\) itself. □
5.4.1 Euclidean algorithm¶
-
The Euclidean algorithm (attributed to the ancient Greek mathematician Euclid, c. 300 BC) computes \(\gcd(n, m)\) efficiently by repeatedly replacing the pair \((n,m)\) with \((m,\ n \bmod m)\).
-
The algorithm does not need \(n \ge m\): if \(n < m\), then \(n \bmod m = n\), and the first replacement simply swaps the two numbers, turning \((n, m)\) into \((m, n)\).
-
From the previous observation, replacing \((n,m)\) with \((m,\ n \bmod m)\) does not change the greatest common divisor.
-
Since \(n \bmod m < m\), the second argument strictly decreases at each replacement.
-
Therefore, the algorithm terminates when the remainder becomes \(0\); at that point, the greatest common divisor is the other argument:
\[ \gcd(n,0)=n. \]
- Pseudo-code of the Euclidean algorithm:
Algorithm: EuclideanAlgorithm
-
Explanation of the algorithm:
-
At each step, \((n, m)\) is replaced by \((m,\ n \bmod m)\).
-
By the previous observation, this replacement preserves the value of \(\gcd(n,m)\): denoting by \(n_0\) and \(m_0\) the input values, at every iteration \(\gcd(n, m) = \gcd(n_0, m_0)\).
-
Since \(n \bmod m < m\), the value of \(m\) strictly decreases at every iteration, so the algorithm terminates.
-
When \(m = 0\), the equality \(\gcd(n, m) = \gcd(n_0, m_0)\) together with \(\gcd(n, 0) = n\) gives the result.
-
Example 5.10: GCD computation
-
Compute \(\gcd(48, 18)\):
step \(n\) \(m\) \(r = n \bmod m\) \(1\) \(48\) \(18\) \(48 \bmod 18 = 12\) \(2\) \(18\) \(12\) \(18 \bmod 12 = 6\) \(3\) \(12\) \(6\) \(12 \bmod 6 = 0\) \(4\) \(6\) \(0\) — (loop ends) Result: \(\gcd(48, 18) = 6\).
-
Compute \(\gcd(252, 105)\):
step \(n\) \(m\) \(r = n \bmod m\) \(1\) \(252\) \(105\) \(252 \bmod 105 = 42\) \(2\) \(105\) \(42\) \(105 \bmod 42 = 21\) \(3\) \(42\) \(21\) \(42 \bmod 21 = 0\) \(4\) \(21\) \(0\) — (loop ends) Result: \(\gcd(252, 105) = 21\).
5.4.2 Coprimality¶
Definition 5.14: coprime numbers
Two natural numbers \(n, m \ge 1\) are said to be coprime (or relatively prime) if they share no common divisor other than \(1\):
- Since the Euclidean algorithm computes \(\gcd(n,m)\), to test coprimality it suffices to call the algorithm and check whether the result is equal to \(1\).
Example 5.11: coprimality test
-
Compute \(\gcd(35, 12)\): the Euclidean algorithm returns \(1\). Therefore, \(35\) and \(12\) are coprime.
-
Compute \(\gcd(48, 18)\): the Euclidean algorithm returns \(6\). Therefore, \(48\) and \(18\) are not coprime.
Key points.
-
The pairs \((n, m)\) and \((m,\ n \bmod m)\) have the same common divisors, hence the same greatest common divisor.
-
The Euclidean algorithm repeats the replacement until the second number is \(0\), and returns the first one, since \(\gcd(n, 0) = n\).
-
Two numbers are coprime if and only if the Euclidean algorithm returns \(1\).
Exercises on this chapter: Numerical algorithms
Interactive lab¶
The algorithm of the chapter, one step at a time: move the sliders and press ▶. The same graph full page, with the other ones of the course, is in the interactive lab.