Random Number Generator - Why Seed Every Time

random.seed(): What does it do?

Pseudo-random number generators work by performing some operation on a value. Generally this value is the previous number generated by the generator. However, the first time you use the generator, there is no previous value.

Seeding a pseudo-random number generator gives it its first "previous" value. Each seed value will correspond to a sequence of generated values for a given random number generator. That is, if you provide the same seed twice, you get the same sequence of numbers twice.

Generally, you want to seed your random number generator with some value that will change each execution of the program. For instance, the current time is a frequently-used seed. The reason why this doesn't happen automatically is so that if you want, you can provide a specific seed to get a known sequence of numbers.

Why different random numbers are generated even after specifying seed value?

Yes. When you call random.seed(), it sets the random seed. The sequence of numbers you generate from that point forwards will always be the same.

The thing is, you're only set the seed once, and then you're calling np.random.uniform() three times. That means you're getting the next three numbers from your random.seed(). Of course they're different – you haven't reset the seed in between. But every time you run the program, you'll get the same sequence of three numbers, because you set the seed to the same thing before generating them all.

Setting the seed only affects the next random number to be generated, because of how pseudo-random number generation (which np.random uses) works: it uses the seed to generate a new random number deterministically, and then uses the generated number to set a new seed for the next number. It effectively boils down to getting a really really long sequence of random numbers that will, eventually, repeat itself. When you set the seed, you're jumping to a specified point in that sequence – you're not keeping the code there, though.

Why does my random function generate the same number every time I call it in a loop?

Seeding repeatedly with tight timings will generate the same initial result after-seed every time. If seeding once per process isn't an option due to caller locality, then you should provide an initial static-state instead. What may work for you is this:

#include <random>

double Utils::randomNumber(int min, int max)
{
static std::mt19937 rng{ std::random_device{}() };
std::uniform_real_distribution<double> dist(min, max);
return dist(rng);
}

Note there is an intrinsic problem on the potential ceiling of the uniform real range, and it may be applicable to your usage (not likely, but never say never). See the notes here for more information.

Change seed for every generated random number

Essentially, your technique simply shifts the "randomness" to when you call the random() function: if you call the function at deterministic times (e.g. once per second) the results will be deterministic. If you call the function at random times, the results will be random.

More accurately, it depends on the what value you call the seed() function with. If you call seed() with deterministic values, the results of the subsequent call to random() will be deterministic.

update (by request):

A software random generator is COMPLETELY deterministic; the sequence of values it produces is exactly determined by what you pass to the initial call to seed(). As a result, if you call seed() with a known value, then you can predict exactly what series of values you'll get from subsequent calls to random(). If you call seed() with a truly random number, then calls to random() will be correspondingly random.

(But that begs the question: if you have random number to pass to seed(), why bother calling random() at all?)

Is it worth changing the seed periodically when using python random number generation?

Calling random.seed without passing any value for a, as you are doing, will likely cause it to fetch state from os.urandom() every time. This might be wasteful as it requests a full 19968 bits, i.e. 624 uint32_ts, every time. Hence, if you are only generating 100 random variates every time you reseed, secrets.randbelow(999)+1 is probably better.

Given that output from the MT19937 can be predicted after just 624 variates then, contrary to what Mark says, this should indeed make it less predictable. Assuming of course that your OS is making a reasonable CSRNG available to os.urandom, recent versions of Linux, OSX, Windows, and most BSDs, are all fine.

Note the MT19937 is especially bad in this regard, especially given the amount of state it has, and there are a number of generators that do much better.

Should I seed the random number generator?

The default is probably best if you want different random numbers with each run. If for some reason you need repeatable random numbers, in testing for instance, use a seed.

Does using a true random number as seed result in truely random numbers?

Nothing is ever truly "random", but there are some useful measurements you can do on a random number generator, like generate a bunch of numbers from it and see if they're fairly evenly distributed. If you generate numbers from 1-100, and you do that 100*100 times, you should expect to see each individual number about 100 times in the final result, and you'd expect the most frequently rolled number to come up only a little more than 100 times.

>>> import random
>>> from collections import Counter
>>> c = Counter()
>>> for _ in range(10000):
... c[random.randint(1, 100)] += 1
...
>>> max(c.values()
123

What would a completely unrandom number generator look like? Let's apply the same process but this time instead of random.randint(1, 100) we'll just use the same number each time.

>>> c = Counter()
>>> for _ in range(10000):
... c[42] += 1
...
>>> max(c.values())
10000
>>> c
Counter({42: 10000})

So very roughly speaking, the higher that max value is, the less random our random number generator is.

Now let's try the method where we seed the RNG each time with a random number from 1-6:

>>> c = Counter()
>>> for _ in range(10000):
... random.seed(random.randint(1, 6))
... c[random.randint(1, 100)] += 1
...
>>> max(c.values())
5000
>>> c
Counter({31: 5000, 80: 5000})

Not great! Each time you call random.seed you're resetting the RNG to a specific state, from which point if you make the same calls to it, it will always return the same sequence of values. At absolute best you should only expect to get 6 different answers from that random.randint(1, 100) call if you're only ever giving it one of 6 possible different seeds immediately beforehand, which would give you a minimum maximum count of 1667. It turns out the actual randomness is worse, because we don't even get 6 possible seeds (since the random.randint(1, 6) call is falling into the same determinism trap).

The tl;dr is that if you want an actual pseudorandom sequence of numbers you should not re-seed the RNG.

(edit) Supposing that you are required to seed the RNG using only the output of a 6-sided die -- are you allowed to roll the die more than once? Consider:

>>> c = Counter()
>>> for _ in range(10000):
... random.seed(sum(random.randint(1, 6) * 6**i for i in range(100)))
... c[random.randint(1, 100)] += 1
...
>>> max(c.values())
128

How to properly seed random number generator

Each time you set the same seed, you get the same sequence. So of course if you're setting the seed to the time in a fast loop, you'll probably call it with the same seed many times.

In your case, as you're calling your randInt function until you have a different value, you're waiting for the time (as returned by Nano) to change.

As for all pseudo-random libraries, you have to set the seed only once, for example when initializing your program unless you specifically need to reproduce a given sequence (which is usually only done for debugging and unit testing).

After that you simply call Intn to get the next random integer.

Move the rand.Seed(time.Now().UTC().UnixNano()) line from the randInt function to the start of the main and everything will be faster. And lose the .UTC() call since:

UnixNano returns t as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC.

Note also that I think you can simplify your string building:

package main

import (
"fmt"
"math/rand"
"time"
)

func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(randomString(10))
}

func randomString(l int) string {
bytes := make([]byte, l)
for i := 0; i < l; i++ {
bytes[i] = byte(randInt(65, 90))
}
return string(bytes)
}

func randInt(min int, max int) int {
return min + rand.Intn(max-min)
}

Why this repeats the same random number?

By calling the rand.Seed() function, passing it a (random) seed (typically the current unix timestamp). Quoting from math/rand package doc:

Top-level functions, such as Float64 and Int, use a default shared Source that produces a deterministic sequence of values each time a program is run. Use the Seed function to initialize the default Source if different behavior is required for each run.

Example:

rand.Seed(time.Now().UnixNano())

If rand.Seed() is not called, the generator behaves as if seeded by 1:

Seed uses the provided seed value to initialize the default Source to a deterministic state. If Seed is not called, the generator behaves as if seeded by Seed(1).



Related Topics



Leave a reply



Submit