{VERSION 1 0 "X11/Motif" "1.0"}{GLOBALS 1 1}{FONT 0 "-adobe-helve
tica-bold-r-normal--14-*" "helvetica" "Helvetica-Bold" 8 14 0 "He
lvetica-Bold" 12}{FONT 1 "-adobe-times-medium-r-normal--14-*" "ti
mes" "Times-Roman" 4 14 64 "Times-Roman" 12}{FONT 2 "-adobe-couri
er-medium-r-normal--14-*" "courier" "Courier" 4 14 192 "Courier" 
12}{FONT 3 "-adobe-new century schoolbook-bold-r-normal--17-*" "n
ew century schoolbook" "Times-Bold" 8 17 64 "Times-Bold" 17}
{SCP_R 1 0 96{COM_R 2 0{TEXT 3 23 "The Euclidean Algorithm"}}
{COM_R 3 0{TEXT 1 38 "\015Michael Monagan, monagan@inf.ethz.ch\01
5"}}{COM_R 4 0{TEXT 1 373 "This worksheet  is intended to show tw
o things, firstly, how to write simple programs in Maple, and sec
ondly, a expository study of Euclid's algorithm and how to comput
e the greatest common divisor of two integers a and b.\015First, 
what is the greatest common divisor of two integers and why is th
is calculation important?  Consider the following calculation tha
t Maple does\015"}}{INP_R 5 0 "> "{TEXT 0 9 " 64 / 20;"}}{OUT_R 6
 0 5{DAG /3j2x0016j2x0005}}{COM_R 7 0{TEXT 1 349 "Maple has simpl
ified the fraction  64/20  by computing the largest integer g tha
t divides both the numerator 64 and the denominator 20, and cance
lled  it out.  In this example, g = 4.  g is the greatest common 
divisor or Gcd.  One way to compute the Gcd of two integers is to
 simply to factor them and look for all the common divisors.  For
 example\015"}}{INP_R 8 0 "> "{TEXT 0 19 "64 = ifactor( 64 );"}}
{OUT_R 9 0 8{DAG =3j2x0064*3(3n3\``,2j2x0002j2x0006}}{INP_R 10 0 
"> "{TEXT 0 19 "20 = ifactor( 20 );"}}{OUT_R 11 0 10{DAG =3j2x002
0*5(3n3\``,2j2x0002p8(3p5,2j2x0005j2x0001}}{COM_R 12 0{TEXT 1 873
 "\015It is obvious from the factorizations that the Gcd(64,20) =
 2^2 = 4.  Unfortunately, this is a real dumb way to compute the 
Gcd because integer factorization is horribly expensive when you 
have bigger numbers.  These days, the fastest computers using the
 best known  methods can only factor numbers of about 100 digits.
  But we will give an algorithm here that can compute the Gcd of 
two integers of tens of thousands of digits quite quickly, even o
n your computer.\015\015Euclid's algorithm dates back to approxim
ately 300 BC and is perhaps the oldest algorithm in Mathematics. 
 Euclid did not have a programming language in which to describe 
his algorithm.  He described it in about a page of  Greek text.  
I hope that what follows is not Greek to you!  The algorithm is r
eally simple.  It assumes that the following statements are true.
  Let a and b be nonnegative integers.\015"}}{INP_R 13 0 "> "
{TEXT 0 13 "Gcd(a,0) = a;"}}{OUT_R 14 0 13{DAG =3(3n3\`Gcd`,3n3\`
a`j2x0000p5}}{INP_R 15 0 "> "{TEXT 0 20 "Gcd(a,b) = Gcd(b,a);"}}
{OUT_R 16 0 15{DAG =3(3n3\`Gcd`,3n3\`a`n3\`b`(3p2,3p7p5}}{INP_R 
17 0 "> "{TEXT 0 22 "Gcd(a,b) = Gcd(a-b,b);"}}{OUT_R 18 0 17{DAG 
=3(3n3\`Gcd`,3n3\`a`n3\`b`(3p2,3+5p5j2x0001p7i2x0001p7}}{COM_R 19
 0{TEXT 1 835 "\015The first two I hope you believe.  The last on
e needs a little proof.  Let  g = Gcd(a,b)  and  h = Gcd(a-b,b). 
 The proof goes in two steps.  First we show that g divides h.  T
hen we show that h divides g.  If that true, then g must be equal
 to h.  In the proof we use the symbol | as shorthand for ``divid
es''.\015\015(i) (Show g | h).  g = Gcd(a,b) by definition theref
ore g divides a and b.  Consider a-b.  If g | a and g | b then we
 can write\015\015     a - b  =  g*(a/g) - g*(b/g)  =  g*(a'-b')\
015\015i.e. g | a-b.  But if g|b and g|(a-b) then g is a COMMON d
ivisor of a-b and b.  Therefore g must divide the Gcd(a-b,b).  Th
erefore g | h because h is the GREATEST common divisor of a-b and
 b.\015\015(ii) (Show h | g).  h = Gcd(a-b,b) hence h | a-b and h
 | b.  Therefore h must also divide a.  Therefore h is a COMMON d
ivisor of a and b hence h | g.\015 "}}{COM_R 20 0{TEXT 1 372 "Now
, Euclid's algorithm is based on this fact that Gcd(a,b) = Gcd(a-
b,b).  If  a >= b, then we want to use this theorem to reduce the
 size of the integer a.  If a < b, since Gcd(a,b) = Gcd(b,a), we 
just interchange a and b.  Keep doing this until one of a or b be
come 0.  Then we stop because Gcd(a,0) = a.  This algorithm is mo
st easily expressed recursively as follows:\015"}}{INP_R 21 0 "> 
"{TEXT 0 125 "GCD := proc(a,b)\012     if b = 0 then RETURN( a ) 
fi;\012     if a < b then RETURN( GCD(b,a) ) fi;\012     RETURN( 
GCD(a-b, b) )\012end:"}}{COM_R 22 0{TEXT 1 52 "\015Let's see the 
algorithm work on a = 64 and b = 20.\015"}}{INP_R 23 0 "> "{TEXT 
0 12 "GCD(64, 20);"}}{OUT_R 24 0 23{DAG j2x0004}}{COM_R 25 0{TEXT
 1 140 "\015Now I'm just going to tell Maple to print out what is
 happening when the Gcd program is executed so you can see the co
mputation occuring. \015"}}{INP_R 26 0 "> "{TEXT 0 19 "printlevel
 := 1000;"}}{OUT_R 27 0 26{DAG :3n5\`printlevel`j2x1000}}{INP_R 
28 0 "> "{TEXT 0 11 "GCD(64,20);"}}{OUT_R 29 0 28{TEXT 2 665 "\{-
-> enter GCD, args = 64, 20\012\{--> enter GCD, args = 44, 20\012
\{--> enter GCD, args = 24, 20\012\{--> enter GCD, args = 4, 20\0
12\{--> enter GCD, args = 20, 4\012\{--> enter GCD, args = 16, 4\
012\{--> enter GCD, args = 12, 4\012\{--> enter GCD, args = 8, 4\
012\{--> enter GCD, args = 4, 4\012\{--> enter GCD, args = 0, 4\0
12\{--> enter GCD, args = 4, 0\012<-- exit GCD (now in GCD) = 4\}
\012<-- exit GCD (now in GCD) = 4\}\012<-- exit GCD (now in GCD) 
= 4\}\012<-- exit GCD (now in GCD) = 4\}\012<-- exit GCD (now in 
GCD) = 4\}\012<-- exit GCD (now in GCD) = 4\}\012<-- exit GCD (no
w in GCD) = 4\}\012<-- exit GCD (now in GCD) = 4\}\012<-- exit GC
D (now in GCD) = 4\}\012<-- exit GCD (now in GCD) = 4\}\012<-- ex
it GCD (now at top level) = 4\}\012"}}{OUT_R 30 0 28{DAG j2x0004}
}{COM_R 31 0{TEXT 1 178 "\015There are some things we'd like to d
o to correct and simplify our Maple code here.  We should check t
hat the inputs a and b are nonnegative integers.  This can be don
e this way\015"}}{INP_R 32 0 "> "{TEXT 0 125 "GCD := proc(a:nonne
gint, b:nonnegint)\012     if b = 0 then a\012     elif a < b the
n GCD( b,a )\012     else GCD(a-b, b)\012     fi\012end:"}}{INP_R
 33 0 "> "{TEXT 0 16 "printlevel := 1;"}}{OUT_R 34 0 33{DAG :3n5\
`printlevel`j2x0001}}{INP_R 35 0 "> "{TEXT 0 14 "GCD( 64, 20 );"}
}{OUT_R 36 0 35{DAG j2x0004}}{COM_R 37 0{TEXT 1 854 "\015The effi
ciency of the algorithm now needs to be improved.  Think about ho
w long the algorithm will take if we give it the numbers a = 10^1
0 and b = 10.  What will happen?  It will repeatedly subtract 10 
from 10^10 until it gets to 0.  This will take 10^10 steps which 
will take a very long time.  Don't try it.  What the algorithm re
ally does is keep on subtracting b from a until a < b.  In other 
words, it computes the remainder of a divided by b.  We can do th
is repeated subraction more efficiently by computing one long int
eger division.  In Maple the functions irem and iquo compute the 
remainder r and the quotient q of  a divided by b such that a = b
*q + r and r < b .  So instead of using the theorem Gcd(a,b) = Gc
d(a-b,b) we can use the theorem Gcd(a,b) = Gcd(irem(a,b),b) and g
et a much more efficient version of Euclid's algorithm.  Namely\0
15 "}}{INP_R 38 0 "> "{TEXT 0 129 "GCD := proc(a:nonnegint, b:non
negint)\012    if b = 0 then a\012    elif a < b then GCD( b,a )\
012    else GCD( irem(a,b), b )\012    fi\012end:"}}{INP_R 39 0 "
> "{TEXT 0 17 "GCD( 10^10, 10 );"}}{OUT_R 40 0 39{DAG j2x0010}}
{INP_R 41 0 "> "{TEXT 0 30 "GCD( 1234567890, 9876543210 );"}}
{OUT_R 42 0 41{DAG j2x0090}}{COM_R 43 0{TEXT 1 237 "\015It turns 
out that Maple uses Euclid's algorithm almost exactly as we have 
described it.  The program is not recursive though.  It is progra
mmed in a while loop.  So here is another version of Euclid's alg
orithm which uses a while loop.\015"}}{INP_R 44 0 "> "{TEXT 0 123
 "GCD := proc(a,b) local c,d,r;\012     c := a; d := b;\012     w
hile d > 0 do   r := irem(c,d);  c := d; d := r;   od;\012     c\
012end:"}}{INP_R 45 0 "> "{TEXT 0 14 "GCD( 64, 20 );"}}{OUT_R 46 
0 45{DAG j2x0004}}{COM_R 47 0{TEXT 1 1511 "\015A computer scienti
st would be interested in how efficient this algorithm really is.
  How many steps does it take and what is the total cost?  In com
puter science, we have developed a methodology for talking about 
the efficiency of an algorithm that is independent of the particu
lar computer and independent of the programming language.  The id
ea is to measure the number of ``atomic'' operations, i.e. operat
ions which take a constant (bounded) amount of time.  In our case
, the inputs a, and b are integers. Let us suppose that a and b h
ave <= n decimal digits.  What we would like is a count on the nu
mber of operations as a function of n.  Euclid's algorithm turns 
out to be an O(n^2) algorithm.  Thsi means that the number of ope
rations is < c*n^2 for some constant c.  What this means in pract
ice is that as the size of the numbers a and b increases, the num
ber of operations in Euclid's algorithm grows quadratically.  So 
if the size of the numbers doubles, the number of operations take
n will go up by a factor of 4.  This is quite a good algorithm.  
It is the same cost as using the highschool method for multiplyin
g two integers -which is what Maple uses.  For the interested rea
der who wants to know where the figure O(n^2) for Euclids algorit
hm comes from, we refer the reader to the non-trivial analysis in
 Knuth, The Art of Computer Programming: Vol II Seminumerical Alg
orithms.  It turns out that the ``worst case'' of Euclides algori
thm is related to the Fibonacci numbers Fn.  I.e. the numbers\015
"}}{INP_R 48 0 "> "{TEXT 0 25 "F := combinat[fibonacci]:"}}{INP_R
 49 0 "> "{TEXT 0 21 "seq( F(i), i=0..10 );"}}{OUT_R 50 0 49{DAG 
,Cj2x0000j2x0001p3j2x0002j2x0003j2x0005j2x0008j2x0013j2x0021j2x00
34j2x0055}}{COM_R 51 0{TEXT 1 251 "\015These numbers are a worst 
case because if we try to compute Gcd(F(n),F(n-1) ) we will have 
to compute the Gcd(F(n-1),F(n-2))  and hence end up computing all
 the Fibonacci numbers in reverse order.  Let's see that by traci
ng our latest version of GCD\015"}}{INP_R 52 0 "> "{TEXT 0 19 "pr
intlevel := 1000;"}}{OUT_R 53 0 52{DAG :3n5\`printlevel`j2x1000}}
{INP_R 54 0 "> "{TEXT 0 14 "GCD( 55, 34 );"}}{OUT_R 55 0 54{TEXT 
2 30 "\{--> enter GCD, args = 55, 34\012"}}{OUT_R 56 0 54{DAG :3n
3\`c`j2x0055}}{OUT_R 57 0 54{DAG :3n3\`d`j2x0034}}{OUT_R 58 0 54
{DAG :3n3\`r`j2x0021}}{OUT_R 59 0 54{DAG :3n3\`c`j2x0034}}{OUT_R 
60 0 54{DAG :3n3\`d`j2x0021}}{OUT_R 61 0 54{DAG :3n3\`r`j2x0013}}
{OUT_R 62 0 54{DAG :3n3\`c`j2x0021}}{OUT_R 63 0 54{DAG :3n3\`d`j2
x0013}}{OUT_R 64 0 54{DAG :3n3\`r`j2x0008}}{OUT_R 65 0 54{DAG :3n
3\`c`j2x0013}}{OUT_R 66 0 54{DAG :3n3\`d`j2x0008}}{OUT_R 67 0 54
{DAG :3n3\`r`j2x0005}}{OUT_R 68 0 54{DAG :3n3\`c`j2x0008}}{OUT_R 
69 0 54{DAG :3n3\`d`j2x0005}}{OUT_R 70 0 54{DAG :3n3\`r`j2x0003}}
{OUT_R 71 0 54{DAG :3n3\`c`j2x0005}}{OUT_R 72 0 54{DAG :3n3\`d`j2
x0003}}{OUT_R 73 0 54{DAG :3n3\`r`j2x0002}}{OUT_R 74 0 54{DAG :3n
3\`c`j2x0003}}{OUT_R 75 0 54{DAG :3n3\`d`j2x0002}}{OUT_R 76 0 54
{DAG :3n3\`r`j2x0001}}{OUT_R 77 0 54{DAG :3n3\`c`j2x0002}}{OUT_R 
78 0 54{DAG :3n3\`d`j2x0001}}{OUT_R 79 0 54{DAG :3n3\`r`j2x0000}}
{OUT_R 80 0 54{DAG :3n3\`c`j2x0001}}{OUT_R 81 0 54{DAG :3n3\`d`j2
x0000}}{OUT_R 82 0 54{DAG j2x0001}}{OUT_R 83 0 54{TEXT 2 37 "<-- 
exit GCD (now at top level) = 1\}\012"}}{OUT_R 84 0 54{DAG j2x000
1}}{INP_R 85 0 "> "{TEXT 0 16 "printlevel := 1;"}}{OUT_R 86 0 85
{DAG :3n5\`printlevel`j2x0001}}{COM_R 87 0{TEXT 1 1135 "\015A nat
ural question to ask is: can the Gcd of two integers of size <= n
 digits in length be computed in significantly fewer operations t
han O(n^2)?  Can it be computed in say only O(n) a linear number 
of operations.  We can add two integers in O(n) operations.  What
 about Gcd's?  Well, the answer is yes.  We can compute the Gcd o
f two integers faster than O(n^2) but not as fast as O(n).  See K
nuth for a historical development.  We want to conclude this work
sheet by studying a different method that is particularly suited 
to computers.  It is called the ``Binary Gcd Algorithm''.  Like E
uclid's algorithm, it is an O(n^2) method, so as the size of the 
integers a and b increase, the cost of this method will grow quad
ratically.  The advantage of this method is that if the numbers a
 and b are represented in a binary base B = 2^m for some m, then 
certain operations can be done faster.  The overall running time 
will be faster by a constant factor.  If the implementation is go
od, perhaps by a factor of 2.  The algorithm is like Euclid's alg
orithm:  very simple.  It rely's on the same three observations o
f Euclid plus this one.\015"}}{INP_R 88 0 "> "{TEXT 0 26 "Gcd(2*a
,2*b) = 2*Gcd(a,b);"}}{OUT_R 89 0 88{DAG =3(3n3\`Gcd`,3+3n3\`a`j2
x0002+3n3\`b`p8+3(3p2,3p6pBp8}}{COM_R 90 0{TEXT 1 381 "\015and th
e fact that the Gcd of two odd integers must be odd and the diffe
rence of two odd integers is even.  The algorithm begins by writi
ng a = 2^i*a' and b = 2^j*b' such that a' and b' are now odd.  He
nce Gcd(a,b) = 2^min(i,j) * Gcd(a',b'). Determining i and j and d
ividing out by 2^i and 2^j is very easy if your integers are repr
esented in a binary base.  Here is the algorithm\015"}}{INP_R 91 
0 "> "{TEXT 0 341 "GCD := proc(a:nonnegint,b:nonnegint) local c,d
,i,j;\012        if b = 0 then a\012        elif a = 0 then b\012
        elif irem(a,2)=0 and irem(b,2)=0 then 2*GCD(iquo(a,2),iqu
o(b,2))\012        elif irem(a,2)=0 then GCD(iquo(a,2),b)\012    
    elif irem(b,2)=0 then GCD(a,iquo(b,2))\012        elif a < b 
then GCD(b,a)\012        else GCD(a-b,b)\012        fi\012end:"}}
{INP_R 92 0 "> "{TEXT 0 16 "GCD(3*64,3*4*5);"}}{OUT_R 93 0 92{DAG
 j2x0012}}{COM_R 94 0{TEXT 1 336 "\015Now this routine is faster 
than the original version of Euclid's algorithm that we showed be
cause in two steps it divides at least one of the numbers by 2.  
This is because if both a and b are odd, after doing a subtractio
n, it can divide by 2.  Our implementation in Maple needs some im
proving.  Here it is as an iterative procedure.\012"}}{INP_R 95 0
 "> "{TEXT 0 611 "GCD := proc(a:nonnegint,b:nonnegint) local c,d,
i,j;\012        if b = 0 then RETURN(a) elif a = 0 then RETURN(b)
 fi;\012        c := a; d := b;\012        for i from 0 while ire
m(c,2)=0 do c := iquo(c,2) od;\012        for j from 0 while irem
(d,2)=0 do d := iquo(d,2) od;\012        if c < d then t := c; c 
:= d; d := t fi;\012        do # Loop invariant c >= d and c,d ar
e both odd             \012             if c = d then RETURN( 2^m
in(i,j)*c ) fi;\012             c := c-d;\012             while i
rem(c,2)=0 do c := iquo(c,2) od;\012             if c < d then t 
:= c; c := d; d := t fi;\012        od;\012        RETURN( 2^min(
i,j) * c )\012end:"}}{COM_R 96 0{TEXT 1 549 "\015The progam has b
ecome trickier.  And it is trickier to prove that it is correct. 
 You'll notice that I've included a comment on the loop do ... od
.  The comment says that at the beginning of the loop the conditi
on c >= d is supposed to always hold and both c,d are odd.  This 
is called a loop invariant and such a thing is the key to proving
 a program with a loop correct.  Can you argue that this program 
is really correct?\015\015Finding out the cost of this program is
 much easier than Euclid's algorithm.  Can you argue that the alg
orithm is O(n^2)?\015"}}{INP_R 97 0 "> "{TEXT 0 0 ""}}}{END}
