Why im getting 2 different results on the same seed number?
Question:
I tried two different ways to get a coin flip result, seeding the RNG first in order to get reproducible results.
First, I tried using random.randint
:
import random
random.seed(23412)
flip = random.randint(0,1)
if flip == 0:
print("Tails")
else:
print("Heads")
For this seed, I get a Heads result; the flip
result is 1
.
Then, I tried round
ing the result of random.random
:
import random
random.seed(23412)
flip = random.random()
print(flip) # for testing purposes
new_flip = round(flip)
if new_flip == 0:
print("Tails")
else:
print("Heads")
This time, I get a value for flip
of 0.27484468113952387
, which rounds to 0
, i.e. a Tails result.
Why does the result differ? Shouldn’t random.randint
pull the same random value (since the seed was the same), and flip the coin the same way?
Answers:
Once you set a seed, all random numbers that follow will be consistent. It does not mean that all random numbers will be the same (and not be random any more).
However, once you re-set the seed, the random number generation will start from the beginning. Therefore it’s predictable what will happen, e.g. like this, where 200 random numbers are generated in a 100 loops and I can predict each outcome (via assertions)
import random
for i in range(100):
test_seed = 23412
random.seed(test_seed)
flip = round(random.random())
assert flip == 0 # predict "Tails"
if flip == 0:
print("Tails")
else:
print("Heads")
flip = random.randint(0,1)
assert flip == 1 # predict "Heads"
if flip == 0:
print("Tails")
else:
print("Heads")
Seeding a random number generator ensures a reproducible stream of underlying raw data. Different random
module methods will compute their results in different ways, using different amounts of data.
The exact computation is an implementation detail. In the implementation I use (although I suspect it is the same for many other versions), random.randint
will use the next single bit of raw data from the stream – it does a bunch of math first to figure out how many possible answers there are in range, then rounds that up to the next power of two, then chooses the corresponding number of bits to get a value (0..2n-1), repeats that until it’s in range, and finally does more math to scale that to the original range. (If you tried, for example, random.randint(0, 2)
, it would repeatedly grab two bits interpreted as an integer, until the result isn’t 3
which is out of range.)
The implementation of random.random
is hidden in the C code, but I assume that it grabs multiple bits of data (at least 53) in order to fill the mantissa of a machine double-precision floating-point value.
I tried two different ways to get a coin flip result, seeding the RNG first in order to get reproducible results.
First, I tried using random.randint
:
import random
random.seed(23412)
flip = random.randint(0,1)
if flip == 0:
print("Tails")
else:
print("Heads")
For this seed, I get a Heads result; the flip
result is 1
.
Then, I tried round
ing the result of random.random
:
import random
random.seed(23412)
flip = random.random()
print(flip) # for testing purposes
new_flip = round(flip)
if new_flip == 0:
print("Tails")
else:
print("Heads")
This time, I get a value for flip
of 0.27484468113952387
, which rounds to 0
, i.e. a Tails result.
Why does the result differ? Shouldn’t random.randint
pull the same random value (since the seed was the same), and flip the coin the same way?
Once you set a seed, all random numbers that follow will be consistent. It does not mean that all random numbers will be the same (and not be random any more).
However, once you re-set the seed, the random number generation will start from the beginning. Therefore it’s predictable what will happen, e.g. like this, where 200 random numbers are generated in a 100 loops and I can predict each outcome (via assertions)
import random
for i in range(100):
test_seed = 23412
random.seed(test_seed)
flip = round(random.random())
assert flip == 0 # predict "Tails"
if flip == 0:
print("Tails")
else:
print("Heads")
flip = random.randint(0,1)
assert flip == 1 # predict "Heads"
if flip == 0:
print("Tails")
else:
print("Heads")
Seeding a random number generator ensures a reproducible stream of underlying raw data. Different random
module methods will compute their results in different ways, using different amounts of data.
The exact computation is an implementation detail. In the implementation I use (although I suspect it is the same for many other versions), random.randint
will use the next single bit of raw data from the stream – it does a bunch of math first to figure out how many possible answers there are in range, then rounds that up to the next power of two, then chooses the corresponding number of bits to get a value (0..2n-1), repeats that until it’s in range, and finally does more math to scale that to the original range. (If you tried, for example, random.randint(0, 2)
, it would repeatedly grab two bits interpreted as an integer, until the result isn’t 3
which is out of range.)
The implementation of random.random
is hidden in the C code, but I assume that it grabs multiple bits of data (at least 53) in order to fill the mantissa of a machine double-precision floating-point value.