Continued Fraction Streams
Every real number can be represented as a potentially infinite stream of digits and also as a potentially infinite stream of continued fraction coefficients, for example:
(The final expression uses a less cool-looking but probably more practical notation for continued fractions.)
I wrote some Python code which converts streams of digits to streams of continued fraction coefficients, and vice-versa.
This writeup shows some example uses of the software, and discusses some of the nuances of converting between these infinite streams.
Next time you forget the digits of the golden ratio, just remember that its continued fraction coefficients are all ones:
…then grab an endless stream of ones from the unexpectedly useful
> yes 1 1 1 1 1 1 …
…and pipe them into the coefficients-to-digits script:
> yes 1 | python3 as_digits.py 1.6180339887498948482045868343656381177203091798057628621354 486227052604628189024497072072041893911374847540880753868917 521266338622235369317931800607667263544333890865959395829056 383226613199282902678806752087668925017116962070322210432162 695486262963136144381497587012203408058879544547492461856953 …
This will output a never-ending stream of the digits of .
To check that things are working, you can even pipe the digits back into the digits-to-coefficients script:
> yes 1 | python3 as_digits.py | python3 continued_digits.py 1 1 1 1 1 …
Cool it works!
One of the countless interesting things about the golden ratio is that it’s the “most irrational” number.
One way to glimpse this is while converting its stream of continued fraction coefficients to digits—it takes many coefficients to get each digit:
Lochs’ theorem says that for almost all numbers, it takes 0.97 continued fraction coefficients on average to determine each decimal digit. But for the golden ratio it takes 2.39 coefficients to determine each decimal digit, the worst case1 possible.
We can see both of these behaviors by comparing to a few random streams of digits. It takes about 970 coefficients to get 1,000 digits for random numbers, but it takes 2,395 coefficients to get 1,000 digits of :
Let’s say we’ve received the digits from our input stream of digits. If we just truncate and compute2 the continued fraction we get:
So maybe we can safely emit these coefficients into our output stream and then keep emitting whatever coefficients come next?
Let’s say we receive the digit next, so far giving us
The first three coefficients are the same as what we’ve already outputted, but the fourth coefficient has changed from to !
In most streaming situations there’s no way to undo or change something we’ve already outputted—and we can’t wait until the stream ends, because it might be infinite—so we’re doomed.
We should emit a coefficient only once we’re sure it will never change.
We can do this by seeing what would happen if we receive the smallest or largest digits from the stream in the future, giving us bounds on what could possibly happen.
For example, if we’ve so far received then the smallest number it could possibly be is if we receive zeroes forever, , and the largest number it could be is if we receive nines forever, So we can bound the true number with:
Of course, (equivalent to the stream simply ending) and , both of which have finite continued fractions that we can compute:
Both bounds have in common, so across the entire range of possible values, no matter what the future digits in the stream are, these coefficients will remain the same, and we can safely output them:
Here’s a more thorough example of what a program would do, processing one digit at a time, and outputting coefficients as soon as they’re certain:
In the other direction
What if we want to go in the other direction, from streams of continued fraction coefficients to streams of digits?
The two biggest changes that can happen to the number are if there are no more coefficients, or if there is one more coefficient and it is a .
If that sounds confusing, remember that all coefficients must be positive integers, and ponder some examples:
(Notice that the inequalities alternate back-and-forth. Continued fractions oscillate around their limit—unlike digit representations, which increase toward their limit.)
Using these bounds, we can convert the stream of coefficients to a stream of digits, emitting whatever digits remain unchanged across both bounds:
Generalizing the previous examples, we can always bound a stream of continued fraction coefficients3 as:
And we get an almost identical inequality for digit streams:
Here’s how to compute the continued fraction :
What happens to the bounds if we know the stream of continued fraction coefficients is infinite? (This is equivalent to knowing that the number is irrational.)
We can get arbitrarily close to the stream “ending” if the next coefficient is arbitrarily large. And for the other bound, we can get a followed by an arbitrarily large coefficient.
So all that happens is that the inequality becomes strict, i.e. it is no longer possible to actually reach the bounds:
(Another way to notice this is that the bounds are rational, so of course our irrational number can’t equal them.)
With digits, on the other hand, nothing changes even if we know the stream is infinite. We can still reach both bounds, with an endless stream of zeroes or an endless stream of nines. ↩