Is there a way to convert uniformly distributed random numbers of one range to uniformly distributed random numbers of another range frugally?
Let me
Keep adding more digits. Here's some Python to compute expected yields (this is slightly worse for a particular value of n
than your approach because it doesn't save leftover bits, but it's good enough to make my point):
import math
def expected_digits(n, b):
total = 0
p = 1
while n >= b:
p *= 1 - (n % b) / n
total += p
n //= b
return total
def expected_yield(k):
return expected_digits(2 ** k, 10) / k
print(expected_yield(10))
print(expected_yield(30))
print(expected_yield(100000))
print(math.log10(2))
The output is
0.294921875
0.2952809327592452
0.301018918814536
0.3010299956639812
and as you can see, 100000
binary digits (second to last line) gets quite close to the Shannon limit (last line).
In theoretical terms, we're applying an arithmetic decoder where all output numbers have equal probability to an infinite stream of bits (interpreted as a random number between 0 and 1). The asymptotic efficiency approaches perfection, but the more samples you take, the heavier the arithmetic gets. That tends to be the trade-off.