An interview question:
Given a function f(x) that 1/4 times returns 0, 3/4 times returns 1. Write a function g(x) using f(x) that 1/2 times returns 0, 1/2 times returns 1.
My implementation is:
function g(x) = {
if (f(x) == 0){ // 1/4
var s = f(x)
if( s == 1) {// 3/4 * 1/4
return s // 3/16
} else {
g(x)
}
} else { // 3/4
var k = f(x)
if( k == 0) {// 1/4 * 3/4
return k // 3/16
} else {
g(x)
}
}
}
Am I right? What's your solution?(you can use any language)
Here is a solution based on central limit theorem, originally due to a friend of mine:
A refinement of the same approach used in btilly's answer, achieving an average ~1.85 calls to
f()
perg()
result (further refinement documented below achieves ~1.75, tbilly's ~2.6, Jim Lewis's accepted answer ~5.33). Code appears lower in the answer.Basically, I generate random integers in the range 0 to 3 with even probability: the caller can then test bit 0 for the first 50/50 value, and bit 1 for a second. Reason: the
f()
probabilities of 1/4 and 3/4 map onto quarters much more cleanly than halves.Description of algorithm
btilly explained the algorithm, but I'll do so in my own way too...
The algorithm basically generates a random real number
x
between 0 and 1, then returns a result depending on which "result bucket" that number falls in:But, generating a random real number given only
f()
is difficult. We have to start with the knowledge that ourx
value should be in the range 0..1 - which we'll call our initial "possible x" space. We then hone in on an actual value forx
:f()
:f()
returns 0 (probability 1 in 4), we considerx
to be in the lower quarter of the "possible x" space, and eliminate the upper three quarters from that spacef()
returns 1 (probability 3 in 4), we considerx
to be in the upper three-quarters of the "possible x" space, and eliminate the lower quarter from that spacex
down to the point where we know which result value it should map to and have no need to get a more specific value forx
.It may or may not help to consider this diagram :-):
Code
If helpful, an intermediary to feed out 50/50 results one at a time:
NOTE: This can be further tweaked by having the algorithm switch from considering an f()==0 result to hone in on the lower quarter, to having it hone in on the upper quarter instead, based on which on average resolves to a result bucket more quickly. Superficially, this seemed useful on the third call to f() when an upper-quarter result would indicate an immediate result of 3, while a lower-quarter result still spans probability point 0.5 and hence results 1 and 2. When I tried it, the results were actually worse. A more complex tuning was needed to see actual benefits, and I ended up writing a brute-force comparison of lower vs upper cutoff for second through eleventh calls to g(). The best result I found was an average of ~1.75, resulting from the 1st, 2nd, 5th and 8th calls to g() seeking low (i.e. setting
low = cutoff
).The problem with your algorithm is that it repeats itself with high probability. My code:
I've measured average number of times
f(x)
was calculated for your algorithm and for mine. For yoursf(x)
was calculated around 5.3 times per oneg(x)
calculation. With my algorithm this number reduced to around 3.5. The same is true for other answers so far since they are actually the same algorithm as you said.P.S.: your definition doesn't mention 'random' at the moment, but probably it is assumed. See my other answer.
This is much like the Monty Hall paradox.
In general.