Generate random integer without an upper bound
Question:
I want to generate a random seed in a predictable way.
I was hoping to do this
seed = 12345
prng_0 = random.Random(seed)
prng_1 = random.Random(prng_0.rand_int(0))
There, 0
is the lower bound, but it turns out I need to give it an upper bound as well. I don’t want to set a fixed upper bound.
If you are curious about my reason, I’m doing this because I need reproducibility when testing. Namely, this is a function receiving a seed and building its prng, prng_0
, then calling multiple times another function that needs to receive a different seed every time.
def funct_a(seed=None):
prng_1 = random.Random(seed)
prng_2 = numpy.random.RandomState(prng_1.randint(0, 4294967296))
print(prng_1.random())
print(prng_2.random())
def funct_b(seed=None):
prng_0 = random.Random(seed)
for i in range(0, 5):
seed = prng_0.randint(0) # not working, needs upper bound
funct_a(seed)
funct_b(12345) # test call
EDIT: interestingly enough, numpy (which I’m also using) has a definite upper seed value, as testified by the doc and by this error
ValueError: Seed must be between 0 and 4294967295
Answers:
You can’t avoid an upper bound. How would the code work without one? This is how the code generates a random number between x
and y
:
0______________________________________________r__________________________________________1
r
is a random decimal between 0
and 1
. This is generated with a fixed algorithm.
Then, it takes r
and multiplies it by the upper bound minus the lower bound. This pretty much means that 0
becomes x
, and 1
becomes y
. If rand
is the random number, r : (1 - 0) :: rand : (y - x)
EDIT: There actually is a way to generate a random number without an upper bound, but it is not logarithmically and not uniformly distributed. Take a look at this python algorithm:
import random
def randint():
i = 0
while True:
if random.random() < 0.5: # Or whatever other probability you want
return i
else:
i += 1
Pretty much, what this is doing is starting from zero, and then every time it has a 0.5 probability of returning that number; otherwise it continues.
This means that there is a 0.5 probability of it being 0, 25% for 1, 12.5% for 2, 5.25% for 3, etc. This is logarithmic distribution “without an upper bound”.
When I don’t want an upper bound I’ll often use sys.maxint
for the upper bound as an approximation
Mathematically there does not exist a uniform distribution on an unbounded set of integers (this was correctly pointed out in the commends). +1 to that comment to Eric Renouf’s answer.
For posterity, the issue is that the probabilities of every possible outcome must collectively sum to a total value of 1 (that’s part of the definition of a probability distribution). If the chance of choosing any one integer is a positive value p>0, then when you sum up infinitely many of those (sum_np), you get a total of infinity, not 1. This is the outcome for any positive p>0. But if p=0, then you instead get a total of 0, not 1. You could say that "in theory" there is no such thing as random integer without an upper bound.
I want to generate a random seed in a predictable way.
I was hoping to do this
seed = 12345
prng_0 = random.Random(seed)
prng_1 = random.Random(prng_0.rand_int(0))
There, 0
is the lower bound, but it turns out I need to give it an upper bound as well. I don’t want to set a fixed upper bound.
If you are curious about my reason, I’m doing this because I need reproducibility when testing. Namely, this is a function receiving a seed and building its prng, prng_0
, then calling multiple times another function that needs to receive a different seed every time.
def funct_a(seed=None):
prng_1 = random.Random(seed)
prng_2 = numpy.random.RandomState(prng_1.randint(0, 4294967296))
print(prng_1.random())
print(prng_2.random())
def funct_b(seed=None):
prng_0 = random.Random(seed)
for i in range(0, 5):
seed = prng_0.randint(0) # not working, needs upper bound
funct_a(seed)
funct_b(12345) # test call
EDIT: interestingly enough, numpy (which I’m also using) has a definite upper seed value, as testified by the doc and by this error
ValueError: Seed must be between 0 and 4294967295
You can’t avoid an upper bound. How would the code work without one? This is how the code generates a random number between x
and y
:
0______________________________________________r__________________________________________1
r
is a random decimal between 0
and 1
. This is generated with a fixed algorithm.
Then, it takes r
and multiplies it by the upper bound minus the lower bound. This pretty much means that 0
becomes x
, and 1
becomes y
. If rand
is the random number, r : (1 - 0) :: rand : (y - x)
EDIT: There actually is a way to generate a random number without an upper bound, but it is not logarithmically and not uniformly distributed. Take a look at this python algorithm:
import random
def randint():
i = 0
while True:
if random.random() < 0.5: # Or whatever other probability you want
return i
else:
i += 1
Pretty much, what this is doing is starting from zero, and then every time it has a 0.5 probability of returning that number; otherwise it continues.
This means that there is a 0.5 probability of it being 0, 25% for 1, 12.5% for 2, 5.25% for 3, etc. This is logarithmic distribution “without an upper bound”.
When I don’t want an upper bound I’ll often use sys.maxint
for the upper bound as an approximation
Mathematically there does not exist a uniform distribution on an unbounded set of integers (this was correctly pointed out in the commends). +1 to that comment to Eric Renouf’s answer.
For posterity, the issue is that the probabilities of every possible outcome must collectively sum to a total value of 1 (that’s part of the definition of a probability distribution). If the chance of choosing any one integer is a positive value p>0, then when you sum up infinitely many of those (sum_np), you get a total of infinity, not 1. This is the outcome for any positive p>0. But if p=0, then you instead get a total of 0, not 1. You could say that "in theory" there is no such thing as random integer without an upper bound.