Every Possible Fraction
December 9, 2014
When I saw this web page a few days ago, I knew it would make a great exercise, simple but fascinating. Starting with x = 1, every fraction in lowest terms is generated by x ′ = 1 / (n − y + 1), where n = ⌊ x ⌋ and y = x − n. The underlying math is known as the Stern-Brocot tree, and has been known since the ancient Greeks, who used terms of the Stern-Brocot tree to design the gear ratios in the Antikythera mechanism.
Your task is to write a program that generates the fractions in lowest terms. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
Haskell:
In Python.
Python 3.4, generator implementation
I used a generator because I have an implementation of generators in the Standard Prelude and because it’s good to remind my readers that generators can be useful, but generators aren’t actually needed for this exercise, because only a single value is carried forward from one iteration to the next and because each iterative step is simple to compute. Here is an alternative solution:
(define rat
(let ((x 0))
(lambda args
(when (pair? args)
(set! x (car args)))
(let* ((n (floor x))
(y (- x n)))
(set! x (/ (- n y -1))) x))))
Calling
(rat)
repeatedly returns the same sequence of fractions as the original. Additionally, calling(rat r)
, where r is some rational number, resets the sequence to start from r, so for instance(rat 11/32)
returns 32/21, and(rat 0)
restarts the sequence from the beginning.For more Haskell fun, check out this functional pearl.
[ EDIT: I linked that paper in the last paragraph of the solution page. As Graham says, it is well worth reading. Phil ]
Oops! Sorry about that.
Aha! Thanks for the link, I had just got the point of realizing that the sequence given didn’t actually generate the Stern-Brocot tree and that explains it.
Here’s a Python generator for the sequence that does the rational arithmetic itself – and we don’t need to do an explicit reduction to lowest terms.
I’m rather partial to the Calkin-Wilf tree. It contains also contains every positive rational number in lowest terms exactly once; it just has them in a different order.
@Lucas – the rational enumeration we are after _is_ the Calkin-Wilf tree order.
This paper looks interesting & suggests we can do a similar direct enumeration of the elements in the Stern-Brocot tree as well:
Click to access RecountingRationalsTwice.pdf
Here’s the algorithm from the Backhouse and Ferreira paper – it’s quite nifty: take Euclid’s GCD algorithm, for coprime p and q, this does (p,q) => (p-q,q) or (p,q) => (p,q-p) until p = q = 1; reverse the process ie. (p,q) maps to (p+q, q) or (p,p+q), these two operations generate all co-prime pairs (p,q); but this is just using the two matrices L = [1 1 0 1] and R = [1 0 1 1] to generate all invertible 2×2 integral matrices, so we can form a tree of all strings of { L, R } and map to the rationals by applying each matrix or its transpose to (1,1): whether we use the matrix or its transpose determines if we get the Stern-Brocot tree or the Calkin-Wilf tree. They also give a method for iteratively generating these matrices, which can then be used to generate the tree elements in order – for the Calkin-Wilf tree, this simplifies to the method described above:
and the output:
There is an extended version of the paper I mentioned above at:
http://eprints.nottingham.ac.uk/1856/1/scp-rationals.pdf with some interesting historical details amongst other stuff.
Roland Backhouse was one of my lecturers years ago, nice guy.
Scala
a bit shorter Scala
Using unfold from SRFI-1: