euclid

euclid

Robbert Haarman

2006-09-19


Introduction

This program implements Euclid's algorithm for determining the greatest common divisor of two natural numbers. It demonstrates function definitions, early returns, and some integer arithmetic. The algorithm is implemeted recursively in languages that are properly tail-recursive1, and iteratively in other languages.

Euclid's algorithm works as follows: to determine the greatest common divisor of two numbers, call the largest one x and the smallest one y. Let m be the remainder after dividing x by y. If m is 0, y completely divides x, and is thus the greatest common divisor of x and y. Otherwise, the greatest common divisor of x and y is equal to the greatest common divisor of y and m.

The above description of the algorithm is recursive: it calls itself. Moreover, it is tail-recursive, because the result of the recursive call is also the result of the whole algorithm. Some languages are properly tail-recursive, meaning that tail-recursive algorithms run in bounded space. For those languages, the algorithm will be implemented as specified above. For languages in which tail-recursion is not guaranteed to run in bounded space, the algorithm is modified to use a loop which sets x to y and y to m, instead of a recursive call.

In all cases, the first thing the implementation does is check if x is less than y. If so, it recursively calls itself with x and y exchanged, and performs an early return using the resulting value.


C

An implementation of Euclid's algorithm is given below.

#include <stdio.h>

int euclid(int x, int y) {
	int m;
	if(x < y) return euclid(y, x);
	while(1) {
		m = x % y;
		if(m == 0) break;
		else {
			x = y;
			y = m;
		}
	}
	return y;
}

int main(int argc, char **argv) {
	printf("%d\n%d\n%d\n",
		euclid(11, 7),
		euclid(7, 11),
		euclid(33, 27));
	return 0;
}

As C is not properly tail-recursive, this implementation is iterative. C uses infix notation for arithmetic and comparisons, with x % y representing the modulo operation.

The program contains 56 words.


OCaml

The following program works for OCaml:

open Printf

let rec euclid x y =
	if x < y then (euclid y x)
	else let m = x mod y in
		if m = 0 then y
		else (euclid y m)

let _ = printf "%d\n%d\n%d\n"
	(euclid 11 7) (euclid 7 11) (euclid 33 27)

Since OCaml is properly tail-recursive, we've implemented the tail-recursive version of Euclid's algorithm. OCaml uses infix operators for comparison and arithmetic, with x mod y calculating x modulo y.

Note that euclid is defined with let rec, rather than the regular let. This is necessary, because the regular let doesn't make the bindings it defines visible inside itself, which would mean that euclid could not call itself.

The program contains 48 words.


Ruby

This is an implementation of Euclid's algorithm in Ruby:

def euclid x, y
	return euclid(y, x) if x < y
	while true
		m = x % y
		break if m == 0
		x = y
		y = m
	end
	y
end

puts euclid(11, 7), euclid(7, 11), euclid(33, 27)

Like C, Ruby is not properly tail recursive and uses infix notation for arithmetic, with % being used for the modulo operation. In Ruby, many control flow constructs have two forms, here illustrated using if:

expr if test

and

if test expr … end

The first one arguably reads more naturally, but allows only a single expression. The second form allows one or more expressions. Besides if, this also applies to unless, while, and until.

The program contains 39 words.


Common Lisp

The following is one of many plausible ways to implement Euclid's algorithm in Common Lisp:

(defun euclid (x y)
	(if (< x y) (return-from euclid (euclid y x)))
	(loop for m = (mod x y)
		until (= m 0)
		do (psetf x y y m)
		finally (return y)))

(format t "~A~%~A~%~A~%"
	(euclid 11 7) (euclid 7 11) (euclid 33 27))

Although many implementations of Common Lisp perform tail call optimization, the language specification does not mandate it, so we've chosen to implement Euclid's algorithm iteratively.

One of the striking characteristics of Common Lisp is that it consistently uses prefix notation, also for comparisons and arithmetic. Thus, we get (< x y) and (mod x y), etc.

Most of the work is done inside the loop form. The Loop Facility is really a language within a language, which looks like a mixture of English and Lisp. Of course, it isn't really English, but the fact that it looks like English means that code that uses loop is often easier to read than equivalent code that doesn't. The fact that the Loop Facility can be implemented as a macro in Common Lisp really shows how powerful Lisp macros are.

In the above program, each iteration of the loop form first sets m to the modulo of x and y. It then checks if m equals 0, leaving the loop if this is the case. Leaving the loop causes the finally clause to be executed, which means that y is returned (from the loop, and subsequently from the function).

As long as the loop runs, the code after the do clause is executed. (psetf x y y m) sets x to y and y to m, in parallel, so that x gets the value of y before it was updated.

A final thing to note about the code above is the use of return vs. return-from. Return-from takes two arguments: the name of the body to return from, and the value to return (which is actually optional; if not specified, nil is returned). defun defines a body with the same name as the function it defines; in this case, euclid. Hence (return-from euclid …). loop (as well as other iteration forms in Common Lisp) also defines a body, but it doesn't have name. (return …) returns (nil if not specified) from the innermost anonymous block, and is actually equivalent to (return-from nil …).

This implementation of Euclid's algorithm contains 45 words.


1 A properly tail-recursive language is one that optimizes tail-recursive algorithms so that the recursion doesn't cause them to run out of space. An algorithm is tail-recursive when the recursive calls happen in tail position. This means that the function that performs the recursive call will immediately return the value returned by the recursive call, without doing anything else. Euclid's algorithm is an example of a tail recursive algorithm.

Valid XHTML 1.1! Valid CSS! Viewable with Any Browser