Reputation: 118
I've been gave an assignment were i had to create two small functions that gives, with equal chance, "heads" or "tails" and, similary with a 6 faces thrown dice, 1,2,3,4,5 or 6.
Important: I could NOT use randint or similar functions for this assignment.
So i've created those two functions that generate a 'pseudo-random number' utilizing time (first digit of the milliseconds) function from python library:
import time
def dice():
ctrl = False
while ctrl == False:
m = lambda: int(round(time.time() * 1000))
f = m()
d = abs(f) % 10
if d in range(1,7):
return d
ctrl = True
def coin():
m = lambda: int(round(time.time() * 1000))
f = m()
if f % 2 == 0:
return "Tails"
elif f == 0:
return "Tails"
else:
return "Heads" (EDIT: I don't know why i typed "Dimes" before)
However i've observed a tendency to give 'Tails' over 'Heads', so i've created an function to test the percentage of 'Tails' and 'Heads' in 100 throws:
def _test():
ta = 0
he = 0
x = 100
while x > 0:
c = coin()
if c == "Tails":
ta += 1
else:
he += 1
x -= 1
time.sleep(0.001)
print("Tails:%s Heads:%s" % (ta, he))
The result of the test was (for several times):
Tails:56 Heads:44
So i did the same thing with the dice function and the result was:
1:20 2:20 3:10 4:20 5:10 6:20
So, as you can see, for some reason i could not infer - if it is by some mistake of my or some other reason - the time function has a tendency to give less '3' and '5', and running the test again with all the numbers (zeros, sevens, eights and nines included) i've come to see that this tendency extends to '0' and '7'.
I would be grateful for some insight and opinions on the matter.
EDIT:
Remove the round() function from the m = lambda: int(round(time.time() * 1000))
function solved the problem - as answered by Makoto.
Upvotes: 4
Views: 322
Reputation: 106490
Your utilization of round
means that your coin flip function will tend towards even numbers if the values you get from your time-based random operation are equidistant from one another (i.e. you "flip" your coin more consistently every half second due to your computer internals).
For the built-in types supporting
round()
, values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice (so, for example, bothround(0.5)
andround(-0.5)
are 0, andround(1.5)
is 2).
It appears that both of your methods suffer from this sort of bias; if they're executed too quickly after one another, or too close to a single timestamp, then you can tend to get one value out of it:
>>> [dice() for x in range(11)]
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
>>> [coin() for x in range(11)]
['Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes', 'Dimes']
The only realistic thing that you could do is to regenerate the time sample if the values are sufficiently close to one another so that you don't run into time-based biases like this, or generate ten time samples and take the average of those instead. Principally, if your computer moves quickly enough and executes these functions fast enough, it will likely pull the same timestamp, which will lead to a strong time-based bias.
Upvotes: 2