gunslinger - 3 months ago 20

Python Question

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 "Dimes"

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.

Answer

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 minusndigits; if two multiples are equally close, rounding is done toward the even choice (so, for example, both`round(0.5)`

and`round(-0.5)`

are 0, and`round(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.