Recently I came across this very interesting problem : How can you simulate an unbiased coin flip with a biased coin? What about the other way round?.
Although the problem is not new (to me as well), this time I found myself with a good solution for the same, which I though would be nice to share here. I will try to provide my implementation (in Python3) of the problems and also discuss some theoretical aspects of the more interesting problem : What is the minimum number of biased coin flips required to generate an unbiased flip? I will be referring to the paper [MU08] for this.
Unbiased Flips from biased coins
So, let’s talk about the easy case first. Given an unbiased coin, how do I simulate a biased flip. Slight formalization : Let a coin flip be either or , where and for some . Thus, a flip is said to be unbiased if and biased, otherwise.
We have two sub-cases here. First, when is not known to us, and second, when it is. For the first case, we have to find a way around simulating an unbiased flip without making any assumption about in our algorithm. An important motivation for dealing with such a case is when dealing with sources of randomness whose accuracy is not known. With this respect, I will be briefly discussing the following question : How can we deal with biased coin flips on a bit-precision computer? In other words, is never really a perfect real number, when working on modern computers, due to the bit precision. We can only make it as small as the smallest floating point number that the computer can represent in its word size. So, how does our solution change in such a situation? Turns out that the solution for unknown works here as well. Try reasoning this out yourself (or leave a comment otherwise) after reading the algorithm below.
Consider flipping the biased coin twice. Note that among the four possible outcomes, the events and are equally likely. Each occurs with probability . Thus, the following algorithm immediately becomes a candidate solution to our problem.
def unbiasedFlipCase1(): (x,y) = (flip(), flip()) if x==y: return biasedFlipCase1() else: return x
Here, is a function that returns with probability and , otherwise. Recall that is unknown to us, and we don’t care about it either as long as we are assured that all calls to are identical and independent of all previous calls. To see why this solution works, it will suffice if we show that . This can be easily seen through the geometric sum .
Neat, right! So the next time someone flips a coin for you to make some decision and you are not sure if you trust the coin, flip it twice and do as the above algorithm suggests and you can always be sure of an unbiased outcome. One can also see that the expected number of recursive calls before this function returns something is computed as . Can we do better than this? Turns out that if is unknown, we probably can not.
Let us now turn our focus to the known case. Wondering why I am discussing this after the seemingly harder case? The reason(s) will be apparent shortly. Note that when we know , we can take advantage of this knowledge to produce an unbiased bit in fewer expected number of steps than the above algorithm, which also works perfectly fine here. A little math before we proceed with this thought : let us look at the variance of the number of recursive calls of the above function. This is easily calculated to be . Hence, by using Chebyshev bounds from [AS72], the probability that the number of recursive calls differs by more than from the mean is at most , which decreases quadratically as fast as . Hence, it is unlikely that this number deviates too much from the mean, implying that the solution is efficient. But, it is the most efficient we can get? The answer is negative. We can do much better than this. Let’s see how.
Recall the concept of entropy and information from any of your undergraduate probability theory classes. The entropy of a biased coin flip is given by , which denotes the average information gained on one flip of this biased coin. You can now see where I am going with this. [MU08] shows that the most we can get out a biased coin is this information, which directly implies that flips of the biased coin will produce one unbiased flip. (Note that [MU08] was not the first person to prove this result. However, I find the discussion in this report very approachable.)
For , we have attains its maximum value of , which gives . Hence, one coin flip suffices, which justifies well with intuition. As deviates in either direction, decreases and hence, the number of biased coin flips increases. Once we have these flip outcomes, a decision is made to return or using the Advanced Multilevel algorithm as in [MU08]. Comparing with , here is the plot I obtained.
As you can see, the entropy based solution always produces lesser number of biased coin flips. Here, I have only varied in increments of , but the graph remains similar for more refined values as well. However, the entropy-based algorithm is quite complicated to understand (at least for me). If you have any interest in coding theory or information theory, help me understand it!
Biased Flips from an unbiased coin
Let us now turn look at the case of simulating a biased coin flip from an unbiased one. On the face of it, this problem seems to be more of a mathematical puzzle than one having any perceivable real word application, but that’s not even remotely true. A ton of applications require us to make decisions which are not always equally favorable to all its possible outcomes. A very simple example being as follows. Suppose we want to simulate the outcome of the sum of a pair of six-sided die. Clearly, all outcomes here are not equally likely. It becomes increasingly impractical to throw two die everytime we want a sample. If only we had a way around!
Unbiased coin flips to the rescue! It turns out that any biased coin flip can be simulated by an appropriate number of unbiased coin flips. The only caveat here is that the unbiased coin flips must be perfectly random, or else we land into the territory of another very interesting question : Given a biased coin with , what is the minimum number of coin flips required to generate a biased flip with for some ? Although I will not spend much time on this question in this post and assume that all unbiased coin flips are available perfectly to me, a brief answer to this question is to consider the maximum amount of information that is obtained about the coin to be simulated from a single flip of the coin used in the simulation. Then, the problem becomes very similar to the technique discussed in [MU08], as described above.
So, how should we pray to the perfection of our unbiased flip in order to be bestowed with a biased Gold coin? Let us discuss an easy-to-understand algorithm to begin with. Recall that if we were instead given a uniform random number generator (RNG), say , which produces a real number in the range uniformly at random, we could have easily been able to simulate a biased flip by the following algorithm.
def biasedFlipFromRNG(p): u = rand() if (u<=p): return 1 else: return 0
This works because the probability that a uniformly generated random number is no more than is exactly . However, the world is not so generous to bestow us with such a Godly RNG. Lose no hope! There is an easy fix.
We can simulate the algorithm above exactly by treating our sequence of ‘s and ‘s, as generated by repeatedly flipping our unbiased coin, as the binary representation of a special number, which I call . I use the tilde sign to partially indicate that it is just an estimate of the actual in the algorithm above. With this approach, all we have to do is flip the unbiased coin, say times (starting with , of course) and record the outcomes in an ordered tuple , and then obtain our estimate at the end of iteration as . Now comes the trick! Let be a number whose binary expansion matches that of up to bits and then it is all zeros. For example, if and , then since the first two bits in the binary expansion of are zero. Compare with . If it so happens that , then repeat this process with . Otherwise, return if and if not. The rationale behind this approach is that once becomes less than , no further addition of bits will make it larger. Similarly for the case when is smaller. Only when the two are equal, can we make no decision. The algorithm to do the above is summarized in the code below.
def getBinDigit(i,p,q): if (q + 2**(-i) <= p): return 1 else: return 0 def biasedCoinFlip(p): flip = 1 value = 0 while(True): unbiasedFlip = unbiasedCoinFlip() decimalDigit = getBinDigit(flip,p,value) if decimalDigit: value = value + 2**(-flip) if unbiasedFlip < decimalDigit: return 1 if unbiasedFlip > decimalDigit: return 0 flip = flip+1
Here, the function simulates the unbiased coin for us. Note that in the code above, I do not produce the entire tuple of bits for every time. Instead, I generate bits only if I require them. A natural question that arises here is the expected number of iterations of the loop before a bit is returned by the function. But first, let us convince ourselves that the probability that this function returns is indeed .
To prove this, let us assume that the binary expansion of has bits . Note that we are only considering the bits after the decimal point, since . Now, the question becomes the following. Given some and a sequence of unbiased bits , what is the probability of the event ? Clearly, this is equal to , since is fixed. Hence, with probability , we enter into yet another iteration of the loop. Once we are done with these iterations, we return if , which is the same as . This happens with probability if and probability otherwise. Thus, the probability that we return after iterations of the loop can be written as .
We are almost there! All that remains to be done is to sum the above expression for , which gives , by definition. Hence, the algorithm is correct. Phew! One last thing before I wrap up. How efficient is this algorithm? We can calculate the expected number of loop iterations before returning using the expression above as . Wait! This seems like a tricky sum. However, we can compute an upper bound for this sum. Writing and using for all , we get that the average number of loop iterations before the algorithm returns a bit is no more than . This may not be tight, but at least it tells us that in fewer than two iterations, the algorithm will return a bit, which is pretty fast if you ask me! A tighter analysis may be non-trivial since I am not aware of any closed form expression for in terms of and . If you do, please let me know!
So people! I hope you enjoyed reading this post as much as I enjoyed writing it. Leave your comments below and I will try my best to address them. I have of course left out a lot of details and other cool algorithms to tackle the problems above, but I want to confess that I understand what I presented the most. I would love to hear about more techniques. Do stay tuned for my next post, in which I will talk about one of my other favorite topics : Dynamic programming. Until then, ciao!
[MU08] Mitzenmacher, Michael. “Tossing a biased coin.” (2008).
[AS72] Abramowitz, M. and Stegun, I. A. (Eds.). Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables, 9th printing. New York: Dover, p. 11, 1972.