Library · The Fiddler: Solutions · Chapter 150
Can You Make Something out of Nothing?
Take the two functions and , and apply them in any combination, starting from . For example , , , , and . How many of the whole numbers from to inclusive can be produced this way?
The Fiddler, Zach Wissner-Gross, July 28, 2023(original post)
The official solution appeared in the post of August 4, 2023 and gives for the main puzzle and for the Extra Credit. Both agree with the answers below; the counts itself, which produces from .
Solution
The upper limit is a hint to write numbers in binary. There, doubling a number appends a to its digits, just as multiplying by ten does in decimal. So , which doubles and adds one, appends the digit , and , which multiplies by four, appends the two digits .
Starting from , applying changes nothing, and the first produces . From then on each step appends a block, either or . So the numbers that can be produced, apart from , are exactly those whose binary digits are a leading followed by a string of the blocks and . Different strings of blocks give different numbers, because reading the digits from the left splits them back into blocks in only one way: a is a block, and a must start a .
How many such numbers have exactly binary digits? After the leading there are digits to fill with blocks of length and . Call the number of ways . The last block is either a , leaving digits before it, or a , leaving , so These are the Fibonacci numbers:
| digits | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| numbers | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 |
Every number with at most ten binary digits lies between and , so those contribute . An eleven-digit number is at least , and only itself is in range; it is followed by five blocks , which is . The total is That is itself a Fibonacci number is no accident: the first Fibonacci numbers add up to one less than the th, here .
The computation
The check makes no use of binary. It applies the functions over and over, starting from , and collects everything it reaches in the range. Neither function ever makes a nonzero number smaller in size, so a number that leaves the range can never come back, and the search can stop at the boundary.
def reachable(functions, low, high):
"""Numbers in [low, high] reachable from 0. No function
shrinks a nonzero number, so nothing outside the range
leads back into it and the search stops at the edge."""
seen, todo = {0}, [0]
while todo:
n = todo.pop()
for fn in functions:
m = fn(n)
if low <= m <= high and m not in seen:
seen.add(m)
todo.append(m)
return seen
f = lambda n: 2*n + 1
g = lambda n: 4*n
h = lambda n: 1 - 2*n
main = reachable([f, g], 0, 1024)
print("main:", len([n for n in main if 1 <= n <= 1024]))
It prints
main: 144
Extra Credit
Now use together with . How many integers from to inclusive can be produced from ?
Solution
In binary, misbehaves: it can lose digits as well as gain them, since has the same number of binary digits as . The cure is to change the base. Write and . In base , where the place values are , multiplying by appends a , so appends the digit and appends . This is the main puzzle again, with base replaced by base .
Base is a perfectly good number system. Every integer, positive or negative, has exactly one representation using the digits and with no minus sign. For example is written , and is written . The sign of a number is the sign of its leading place value, because the lower places can never outweigh it: below a leading , the places of the opposite sign add up to at most in size, which is less than .
So the reachable integers, apart from , are again exactly the strings made of a leading followed by blocks and , and there are of them with digits. What changes is which lengths fit the range. The -digit numbers lie between these bounds:
| digits | leading place | range of values |
|---|---|---|
| to | to | to |
| to | ||
| to | ||
| or more | beyond |
Every reachable number of up to ten digits is in range, and there are of them, as before. The eleven- and twelve-digit numbers need a closer look.
Eleven digits. Such a number is plus a ten-digit tail made of blocks, and it is in range exactly when the tail is zero or negative. The tail’s highest is the first block, which sits after some number of blocks, at position counting from on the right. That position is always odd, so its place value is negative, and so is the tail. All of these numbers are in range.
Twelve digits. Such a number is plus an eleven-digit tail, and it is in range only if the tail is at least . Now the tail’s highest is at an even position , and the tail reaches only if that position is , so that the tail is plus a ten-digit tail of the kind just seen, which is zero or negative. So the tail is exactly and the number is itself, written followed by ten s, which is : one number.
The total. Adding , which produces, If is not counted as produced, the count is .
The computation
The same search, now with and and the range to , also reports whether the two ends of the range were reached.
extra = reachable([g, h], -1024, 1024)
print("extra credit:", len(extra), "including 0,",
len(extra - {0}), "without it")
print("extremes reached:", 1024 in extra, -1024 in extra)
It prints
extra credit: 234 including 0, 233 without it
extremes reached: True True