Every Possible Fraction
December 9, 2014
Using the generators of a previous exercise, this is simple:
(define-generator (rats)
(let loop ((x 1))
(yield x)
(let* ((n (floor x)) (y (- x n)))
(loop (/ (- n -1 y))))))
Then it’s just a matter of taking terms from the sequence:
> (define next (rats))
> (do ((i 1 (+ i 1))) ((< 120 i))
(display (next))
(if (zero? (modulo i 10))
(newline)
(display #\tab))))
1 1/2 2 1/3 3/2 2/3 3 1/4 4/3 3/5
5/2 2/5 5/3 3/4 4 1/5 5/4 4/7 7/3 3/8
8/5 5/7 7/2 2/7 7/5 5/8 8/3 3/7 7/4 4/5
5 1/6 6/5 5/9 9/4 4/11 11/7 7/10 10/3 3/11
11/8 8/13 13/5 5/12 12/7 7/9 9/2 2/9 9/7 7/12
12/5 5/13 13/8 8/11 11/3 3/10 10/7 7/11 11/4 4/9
9/5 5/6 6 1/7 7/6 6/11 11/5 5/14 14/9 9/13
13/4 4/15 15/11 11/18 18/7 7/17 17/10 10/13 13/3 3/14
14/11 11/19 19/8 8/21 21/13 13/18 18/5 5/17 17/12 12/19
19/7 7/16 16/9 9/11 11/2 2/11 11/9 9/16 16/7 7/19
19/12 12/17 17/5 5/18 18/13 13/21 21/8 8/19 19/11 11/14
14/3 3/13 13/10 10/17 17/7 7/18 18/11 11/15 15/4 4/13
You can run the program at http://programmingpraxis.codepad.org/Pzw6Hdu0. You might also be interested in Jeremy Gibbons’ derivation of the program (he called it rats7
).
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: