3  Worst-case analysis

First let’s record a result that will be useful in the sequel.

Proposition 3.1 We have the following for \(a \ge b \ge 1\).

(a) \(T(a,b) = 1\) if and only if \(b \mid a\).
(b) If \(a > b\), \(T(b,a) = T(a,b) + 1\).
(c) For all positive integers \(d\), \(T(da,db) = T(a,b)\).

Proof. (a) Simply note that \(b \mid a\) if and only if there exists an integer \(q\) such that \(a = qb + 0\).

(b) If \(1 \le b < a\) then the first two divisions of the Euclidean algorithm for \(\gcd(b,a)\) are: \(b = 0a + b\) and \(a = qb + r\), \(0 \le r < b\). The second division is the first done in the Euclidean algorithm for \(\gcd(a,b)\).

(c) If (2.1) is the remainder sequence given by Euclid’s algorithm for \(\gcd(a,b)\), then for any positive integer \(d\), \[da = dr_0, db = dr_1 > dr_2 > \cdots > dr_n > dr_{n + 1} = 0\] is the remainder sequence given by Euclid’s algorithm for \(\gcd(da,db)\) (the quotients \(q_i\) in (2.2) won’t change).

# Let's illustrate the invariance of the number of divisions under (a,b) -> (da,db). 
# Take (a,b) as in the previous code block and d = 67.
print(ea.write_euclid(67*1011,67*69))
67737 = 14*4623 + 3015   ∴ gcd(67737,4623) = gcd(4623,3015)
 4623 =  1*3015 + 1608   ∴  gcd(4623,3015) = gcd(3015,1608)
 3015 =  1*1608 + 1407   ∴  gcd(3015,1608) = gcd(1608,1407)
 1608 =  1*1407 +  201   ∴  gcd(1608,1407) = gcd(1407,201) 
 1407 =  7* 201 +    0   ∴   gcd(1407,201) = gcd(201,0)    

gcd(67737,4623) = 201, T(67737,4623) = 5

What are the smallest possible values of \(a\) and \(b\) if \(a \ge b \ge 1\) and \(T(a,b) = n\)? If \(n = 1\) we need only note that \(T(1,1) = 1\) to see that the answer is \(a = b = 1\). Since \(T(a,b) = 1\) if and only if \(b \mid a\), the answer for \(n \ge 2\) must satisfy \(a > b \ge 2\) and \(b \nmid a\). (In fact, by Proposition 3.1(c), the answer in general must satisfy \(\gcd(a,b) = 1\), i.e. \(r_n = 1\).) Noting that \(T(3,2) = 2\) then gives the answer for \(n = 2\) as \(a = 3\) and \(b = 2\).

# Some very naive code to answer the question: what are the smallest possible values of a and b if 
# a >= b >= 1 and T(a,b) = n?
# Basically a very inefficient way of computing Fibonacci numbers, the code really starts to get slow for n = 16.
# But the first few n are enough to help us see a pattern.

def W(n):
    a, b = 1, 1
    while gcd_steps(a,b,0)[1] < n:
        a += 1
        for b in range(1,a+1):
            if gcd_steps(a,b,0)[1] == n:
                return a, b
    return a, b

print(W(1), W(2), W(3), W(4), W(5), W(6), W(7), W(8), W(9), W(10))
(1, 1) (3, 2) (5, 3) (8, 5) (13, 8) (21, 13) (34, 21) (55, 34) (89, 55) (144, 89)

It isn’t difficult to see that for a given \(n \ge 2\), if \(T(a,b) = n\), \(a\) and \(b\) will be minimal if, in (2.2), \(r_n = 1\), \(r_{n - 1} = 2\), and \(q_1 = q_2 = \cdots = q_{n - 1} = 1\). Thus, \(r_{n - 2} = 1(2) + 1 = 3\), \(r_{n - 3} = 1(3) + 2 = 5\), and so on.

print(ea.write_euclid(55,34))
55 = 1*34 + 21   ∴ gcd(55,34) = gcd(34,21)
34 = 1*21 + 13   ∴ gcd(34,21) = gcd(21,13)
21 = 1*13 +  8   ∴ gcd(21,13) = gcd(13,8) 
13 = 1* 8 +  5   ∴  gcd(13,8) = gcd(8,5)  
 8 = 1* 5 +  3   ∴   gcd(8,5) = gcd(5,3)  
 5 = 1* 3 +  2   ∴   gcd(5,3) = gcd(3,2)  
 3 = 1* 2 +  1   ∴   gcd(3,2) = gcd(2,1)  
 2 = 2* 1 +  0   ∴   gcd(2,1) = gcd(1,0)  

gcd(55,34) = 1, T(55,34) = 8

These are the Fibonacci numbers!

Definition 3.1 Let \(f_0 = 0\), \(f_1 = 1\), and \(f_{n+2} = f_{n + 1} + f_{n}\) for \(n \ge 0\). Then \((f_0,f_1,\ldots)\) is the Fibonacci sequence, and \(f_n\) is the \(n\)-th Fibonacci number.

Proposition 3.2 Let \(a \ge b \ge 1\). (a) For \(n \ge 0\), we have \(\gcd(f_{n + 1},f_{n}) = 1\). For \(n \ge 1\), we have \(T(f_{n + 2},f_{n + 1}) = n\). (b) If \(n \ge 1\) and \(T(a,b) = n\), then \(a \ge f_{n + 2}\) and \(b \ge f_{n+1}\).

Proof. (a) Since \(f_{n + 2} = f_{n + 1} + f_{n}\), we have \(\gcd(f_{n + 2},f_{n + 1}) = \gcd(f_{n + 1},f_{n})\). Since \(\gcd(f_1,f_0) = \gcd(1,0) = 1\), that \(\gcd(f_{n + 1},f_n) = 1\) for all \(n \ge 0\) now follows by mathematical induction.

Also, since \(f_{n + 2} = f_{n + 1} + f_n\) and \(0 \le f_n < f_{n + 1}\) for \(n \ge 2\), we have \(T(f_{n + 2},f_{n + 1}) = T(f_{n + 1}, f_n) + 1\) for \(n \ge 2\). Since \(T(f_{1 + 2},f_{1 + 1}) = T(2,1) = 1\), that \(T(f_{n + 2},f_{n + 1}) = n\) for all \(n \ge 1\) now follows by mathematical induction.

(b) We’ve verified the result for \(n = 1\) and \(n = 2\), so let \(n \ge 2\) and suppose the result holds for \(n\). Let \(T(a,b) = n + 1\). Since this is greater than \(1\), \(b \nmid a\) (as noted above), and \(a = qb + r\) for some \(q\) and \(r\) with \(q \ge 1\) and \(1 \le r < b\). Now, \(T(b,r) = n\) and so, by inductive hypothesis, \(b \ge f_{n + 2}\) and \(r \ge f_{n + 1}\). Thus, \(a = qb + r \ge f_{n + 2} + f_{n+1} = f_{n + 3}\). In conclusion, \(a \ge f_{n + 3}\) and \(b \ge f_{n + 2}\), and the result holds for all \(n \ge 2\), by mathematical induction.

Remark. For \(a \ge b \ge 1\) and \(n \ge 1\), the contrapositive of part (b) of the above proposition is: if either \(a < f_{n + 2}\) or \(b < f_{n + 1}\), then \(T(a,b) \le n - 1\).

# Let's test this out for small n. 
# In the table below, row a column b contains T(a,b).
# Below the diagonal corresponds to a > b.
# Note that, for instance, the first time 4 appears below the diagonal is in row 8, column 5: 8 = f_6 and 5 = f_5.

test_dict = {}
for a in range(14):
    test_dict[a] = {}
    for b in range(14):
        test_dict[a][b] = gcd_steps(a,b,0)[1]
        
pd.DataFrame.from_dict(test_dict, orient='index')#.astype('int')
    0   1   2   3   4   5   6   7   8   9   10  11  12  13
0   0   1   1   1   1   1   1   1   1   1   1   1   1   1
1   0   1   2   2   2   2   2   2   2   2   2   2   2   2
2   0   1   1   3   2   3   2   3   2   3   2   3   2   3
3   0   1   2   1   3   4   2   3   4   2   3   4   2   3
4   0   1   1   2   1   3   3   4   2   3   3   4   2   3
5   0   1   2   3   2   1   3   4   5   4   2   3   4   5
6   0   1   1   1   2   2   1   3   3   3   4   4   2   3
7   0   1   2   2   3   3   2   1   3   4   4   5   5   4
8   0   1   1   3   1   4   2   2   1   3   3   5   3   6
9   0   1   2   1   2   3   2   3   2   1   3   4   3   4
10  0   1   1   2   2   1   3   3   2   2   1   3   3   4
11  0   1   2   3   3   2   3   4   4   3   2   1   3   4
12  0   1   1   1   1   3   1   4   2   2   2   2   1   3
13  0   1   2   2   2   4   2   3   5   3   3   3   2   1

3.1 The Fibonacci numbers and dynamic programming

Let’s take a detour and compute some Fibonacci numbers. Evaluating \(f_n\) from the naive recursion recomputes the same subproblems repeatedly, so it takes time exponential in \(n\): the computation of \(f_5\), for example, unwinds into a sum of \(1\)’s and \(0\)’s of length \(f_5\), and \(f_n\) is of order \(\phi^n\), where \(\phi\) is the golden ratio. Dynamic programming removes the waste. Computing \(f_0, f_1, \ldots, f_n\) in order and storing them is one version, tabulation; keeping only the last two values in a pair of variables and updating them on each of the \(n\) steps is another, memoization, and it needs only about \(n\) additions and \(O(1)\) extra space. The source gives all three (the naive recursion, the list, and the two-variable version) together with the timing code behind the figure below; see the fibonacci module (src/euclid_analysis/fibonacci.py).

We can show that if \(\phi\) and \(\psi\) are the roots of \(x^2 - x - 1\), then for \(n \ge 0\),

\[ f_n = \frac{\phi^{n} - \psi^n}{\phi - \psi}. \tag{3.1}\]

Indeed, any root \(\lambda\) of \(x^2 - x - 1\) satisfies \(\lambda^{n + 2} - \lambda^{n + 1} - \lambda = 0\) for all \(n \ge 0\), just as the Fibonacci numbers satisfy \(f_{n + 2} - f_{n + 1} - f_{n} = 0\) for \(n \ge 0\). It follows that if \(c_1\) and \(c_2\) are constants such that \(f_n = c_1\phi^n + c_2\psi^n\) for \(n = 0\) and \(n = 1\), then this equation holds for all \(n \ge 0\). Letting \(n = 0\) shows that \(c_2 = -c_1\), and then letting \(n = 1\) gives \(c_1 = 1/(\phi - \psi)\). We let \(\phi = \frac{1}{2}(1 + \sqrt{5})\) be the positive root (the golden ratio), and \(\psi = \frac{1}{2}(1 - \sqrt{5})\) be the other. Thus, \(\phi - \psi = \sqrt{5}\), and \(f_n\) is the integer nearest \(\phi^n/\sqrt{5}\).

# Let's try this out. 
# In fact, phi^n/sqrt(5) < f_n when n is odd, and phi^n/sqrt(5) > f_n when n is even.

phi, psi = np.roots([1,-1,-1])

def fib_closed_form(n):
    return (phi**(n) - psi**(n))/(phi - psi)

for n in range(11):
    print(f'n = {n}, f_n = {fib(n)}, phi^n/(phi - psi) = {phi**(n)/(phi - psi):.3f}..., nearest int = {int(np.rint(phi**(n)/(phi - psi)))}')
n = 0, f_n = 0, phi^n/(phi - psi) = 0.447..., nearest int = 0
n = 1, f_n = 1, phi^n/(phi - psi) = 0.724..., nearest int = 1
n = 2, f_n = 1, phi^n/(phi - psi) = 1.171..., nearest int = 1
n = 3, f_n = 2, phi^n/(phi - psi) = 1.894..., nearest int = 2
n = 4, f_n = 3, phi^n/(phi - psi) = 3.065..., nearest int = 3
n = 5, f_n = 5, phi^n/(phi - psi) = 4.960..., nearest int = 5
n = 6, f_n = 8, phi^n/(phi - psi) = 8.025..., nearest int = 8
n = 7, f_n = 13, phi^n/(phi - psi) = 12.985..., nearest int = 13
n = 8, f_n = 21, phi^n/(phi - psi) = 21.010..., nearest int = 21
n = 9, f_n = 34, phi^n/(phi - psi) = 33.994..., nearest int = 34
n = 10, f_n = 55, phi^n/(phi - psi) = 55.004..., nearest int = 55

We are now ready to establish the worst-case result for the number of divisions in Euclid’s algorithm. The following proposition implies that for \(N \ge 2\),

\[ \max\{T(a,b) : 1 \le b \le a \le N\} = \Theta(\log N). \]

Proposition 3.3 For \(N \ge 2\), \[\frac{\log(N - 1)}{\log \phi} - 1.328 < \max\{T(a,b) : 1 \le b \le a \le N\} < \frac{\log(N + 1)}{\log \phi} - 0.327.\]

Note that \(1/\log \phi = 2.0780869\ldots\).

Proof. First of all, if \(m \ge 1\) then \(f_{n + 1} \le m < f_{n + 2}\) for some \(n \ge 1\). Thus, in view of (3.1), we have (for \(m \ge 2\) in the left inequality)

\[\frac{\log(m - 1)}{\log \phi} - 0.328 < n < \frac{\log(m + 1)}{\log \phi} + 0.673. \tag{3.2}\]

To see this in more detail, note that \(f_{k} = \lfloor \phi^k/(\phi - \psi)\rfloor\) if \(k\) is even, while \(f_{k} = \lceil \phi^k/(\phi - \psi)\rceil\) if \(k\) is odd; also, \(\phi - \psi = \sqrt{5}\). Thus, supposing first that \(n\) is even,

\[\frac{\phi^{n+1}}{\sqrt{5}} < f_{n+1} \le m < f_{n + 2} < \frac{\phi^{n+2}}{\sqrt{5}}.\]

Taking logarithms, we see that

\[n + 1 < \frac{\log m + \frac{1}{2}\log 5}{\log \phi} < n + 2,\]

leading (after a calculation) to

\[ \frac{\log m}{\log \phi} - 0.3277\ldots < n < \frac{\log m}{\log \phi} + 0.6722\ldots .\]

Similarly, if \(n\) is odd and \(m \ge 2\), then

\[ \frac{\log(m-1)}{\log \phi} - 0.3277\ldots < n < \frac{\log(m + 1)}{\log \phi} + 0.6722\ldots .\]

Combining gives (3.2).

Now, let \(N \ge 2\) be given, and let \(n \ge 2\) be such that \(f_{n + 1} \le N < f_{n + 2}\). By Proposition 3.2(b), for \(1 \le b \le a \le N\), we have \(T(a,b) \le n - 1\), with equality attained when \(a = f_{n + 1}\) and \(b = f_{n}\). That is,

\[\max\{T(a,b) : 1 \le b \le a \le N\} = n - 1,\]

and the result follows by (3.2).

We see that, for a given \(N \ge 2\), as the pair \((a,b)\) ranges over \(1 \le b < a \le N\), \(T(a,b)\) ranges from \(1\) to around \(2.078 \log N\). What is the average value of \(T(a,b)\) over \((a,b)\) in the same region? How are the values of \(T(a,b)\) distributed? We will answer these questions, and more, below.