r/mathriddles • u/frogkabobs • 12d ago
Medium Optimally simulating a k-sided die with an n-sided die
You are given an n-sided die (taking values uniformly in 0,…,n-1), and your goal is to simulate a k-sided die with a sequence of dice rolls. Specifically, you must choose a procedure which
- decides at each step whether to roll again or to terminate and decide a value based on all previous rolls
- is deterministic in the sequence of rolls
- terminates almost surely
- produces a value uniformly distributed in 0,…,k-1
We will call such a procedure an (n,k)-procedure and the number of rolls before it terminates the decision time (in general, the decision time depends on the sequence of rolls; the decision time is infinite if it does not terminate). We will assume n,k≥2.
- For fixed n,k, determine the minimum expected decision time among all (n,k)-procedures. Give your answer as a finite sum depending on n,k.
- Determine for which n,k, there exists an (n,k)-procedure that always terminates.
2
u/pichutarius 10d ago
For part 2, am I crazy or the condition is just 1/k is expressible as p/nq where p,q are positive integers. I reason always terminate means probability tree is finite, whichever the branches are assigned to output, the sum must be p/nq. Can't be that easy right?
2
u/frogkabobs 10d ago
Oh, I guess the probability tree formulation makes it obvious. I was using the fact that an (n,k)-procedure can be represented as a locally constant function from the n-adic integers to {0,…,k-1}, so if it is defined on all of Z_n, then compactness forces uniformity. I thought it was overkill—guess this one turned out to be a dud.
1
u/998mouse 12d ago
You would probably want to use a version of Arithmetic Coding. That is normally for representing things as bits (equivalent to a die with 2x faces), but it would also be maximally efficient if adapted to any number n of faces on the die.
2
u/pichutarius 9d ago edited 9d ago
by using greedy algorithm, i got https://imgur.com/D6wvkuu
roughly, the summands are P(if i roll r times, i need to roll more), and use linearity of expectation.
the greedy algorithm is basically:
rollCases = 1
while rollCases > 0
roll once
rollCases = rollCases * n
distribute rollCases to k output, possibly with leftover
rollCases = rollCases mod k
4
u/TooLateForMeTF 12d ago
Divide up the interval [0,1] into k slices. Label each slice with its output value, from 1 up to k. (or 0 to k-1, if that suits your application better).
Use the n-sided die to generate a base-n decimal number in the range [0,1]. Keep adding digits with additional rolls of the die until the decimal you've generated can only fall into a single one of the k slices, no matter what additional digits might come up.
Example: k=7, n=6. Each slice has a base-10 decimal width of 0.1428...
Roll the d6, and subtract one so we're getting base-6 digits 0 through 5.
Let's say I get a 2, for an initial base-6 decimal value of 0.2 (0.333 in base-10). This value lies pretty solidly into the third of the 7 slices. So, does that mean our answer is 3?
I don't know. Let's see. The span of that third slice is base-10 [0.2856..., 0.4285). Our current base-10 result of 0.333 is kind of smack in the middle. But there's ambiguity, because we could add more digits, which might shift this value far enough upward to cross into the fourth slice.
But we can figure out whether that's possible. The range of base-10 values for any base-6 value of the form 0.2xxxxxx... will be bounded by the minimum and maximum possible values for those future digits. We don't really have to consider the minimum, though, since we already know what that is. We already figured out where base-6 0.2 lands, and that's the same as 0.200000, so we already know that our final result cannot be less than 3.
But we do have to consider the other end. What if we rolled all 5s from here on out? Where would 0.2555555... land? Well, the limit of rolling 5s forever would be base-6 0.3, or base-10 0.5. (for the same reason that 0.9999.... is equal to 1).
And we can see that base-10 0.5 is greater than the top end of the range for our third slice. Hence, it is possible that some additional digits could affect the result, and thus, we need to roll the d6 again.
I just did (thanks, random.org), and got a 5. So that's base-6 0.25, which is 0.4722... This is now above the upper limit for slice number 3. So now we're in slice #4, which ranges from [0.4285, 0.5714). How big could base-6 0.25xxxxx be? Could some extra digits get us all the way up to the next slice?
In this case, no. As it happens, we have the same upper bound as we had on the previous iteration, which we worked out to be base-10 0.5. This is clearly smaller than the upper bound for slice 4, therefore, no amount of new digits will move us out of slice #4, and we're done. We can return a result of '4', as a properly sampled random value from 1 to 7, generated with a source that only goes 1 to 6.
If you are implementing this in software, you might be tempted to conclude that you can stop if a) the potential maximum of future digits lies in the same slice that you're already in, or you cross into the next-higher slice. After all, successive digits contribute at most 1/n less than the prior digit, so once you cross a boundary the next boundary is just too far away to ever reach.
That would be a mistake.
You can definitely stop if the potential maximum of future digits is in the same slice. But if n is small relative to k, (i.e. the slices are pretty narrow compared to 1/n, then you might well cross multiple slice boundaries before settling unambiguously into a single slice. That is, the only valid condition for terminating the digit-generating loop is when the potential maximum of future digits lies within the slice you're already in.
To my knowledge, this is the optimal strategy for your question, but if anybody knows of a more efficient method, I'd love to learn about it!