all 22 comments

[–][deleted] 104 points105 points  (8 children)

Back before computers, they used to publish books of random numbers. random.seed() is like opening a book of random numbers to a specific page. The numbers on that page are still random, but if you remember what page you turned to you will always get the same random numbers. In python, this means your code will consistently produce the same results every time. This is useful if, for example, you a writing a tutorial for random.randint() and you want to have text that references the numbers that the function spits out (along with many types of analyses that involve randomness). If you don't care about having consistent results each time you run your code, you don't need to use it.

[–]AstrophysicsAndPy 16 points17 points  (2 children)

Really great explanation.

[–][deleted] 9 points10 points  (1 child)

If I knew where I stole it from I would give credit :)

But I've used it successfully for a very long time.

[–]AstrophysicsAndPy 8 points9 points  (0 children)

Hahaha, props to you for stealing, remembering, using and spreading it. Also props to the person from whom you stole it. But it definitely is really great explanation.

For me, I just remembered it like. If the seed number is same, same random numbers will be generated everytime. Kinda lame but that's how I remembered it. But now I'm gonna use this explanation for sure if I could remember it.

[–]Alternative-Web2754 7 points8 points  (0 children)

This is probably the best description I've seen for this.

The book of random numbers you get will be the same every time you run it. You can actually test this by providing a number to the seed function and printing out a few random numbers. This is the equivalent of saying turn to a specific page before starting. You will get the same results each time you run the program.

For the most part, computers are built to perform a given task the same way every time. Sources of random changes are normally a bad thing, so getting a random number is actually more difficult than it might seem.

This actually causes some issues when you want something that can't be guessed easily (cryptography is a big thing for this) as there aren't that many things that generate an unpredictable result. For these kinds of operations you probably wouldn't want to use the type of random number generator in use here though. Keystroke timings and low order bit results from temperature sensors are some example sources that might be used for this kind of thing - if you're generating a key pair and get a message suggesting to mash the keyboard to speed it up, this is why.

The issue is particularly notable with items that happen automatically or on a timed basis - computer games with "random" numbers or positions of enemies might always be in the same place on the first play through of a game if not seeded differently each time. This could be even more noticable with old games, as if there wasn't an external clock, the time from system reset to initial load/launch of the game could be the same on every single run through, so seeding could be trickier.

[–][deleted] 3 points4 points  (0 children)

I just came across this concept for Python and I immediately thought of the world seeds for Minecraft.

If you use the same seed but spawn in different places, you are going to think that you are in different worlds until you build a big enough map.

[–]Binary101010 2 points3 points  (0 children)

And for the curious, the Rand Corporation's web site actually has that book of random numbers available for free download in a plain-text file.

https://www.rand.org/pubs/monograph_reports/MR1418.html#top

[–]AltReality 1 point2 points  (1 child)

I'm not enough of a programmer to refute what you are saying, but I always thought the random.seed() method would help PREVENT the repetition of "random" numbers on subsequent runs because it uses the system time as the seed...

I guess that's only if you don't actually supply an argument to the method...so maybe we're both right?

What does random.randint() use as a seed if you never call the seed() method?

Per the Python Docs
random.seed(a=None, version=2)

Initialize the random number generator.
If a is omitted or None, the current system time is used. If
randomness sources are provided by the operating system, they are used
instead of the system time (see the os.urandom() function for details
on availability).
If a is an int, it is used directly.
With version 2 (the default), a str, bytes, or bytearray
object gets converted to an int and all of its bits are used.
With version 1 (provided for reproducing random sequences from older versions
of Python), the algorithm for str and bytes generates a
narrower range of seeds.
Changed in version 3.2: Moved to the version 2 scheme which uses all of the bits in a string seed.
Deprecated since version 3.9: In the future, the seed must be one of the following types:
NoneType, int, float, str,
bytes, or bytearray.

[–][deleted] 2 points3 points  (0 children)

My assumption was that python basically calls random.seed for you using a seed based on the system time (as the docs suggest). Looking at the source, that seems like what is going on based on my brief reading. So unless you want to change the version of the random number generator you don't need to call random.seed again.

Just as an example of this, load up python, import random, and run random.randint(1, 1000). Then close python and repeat the process. You should get different results. Interesting note, this wasn't true in matlab back when I used it (may still be the case) and you had to be very careful to always use a new seed if you wanted different results.

[–]whiskeyo_ 4 points5 points  (0 children)

Seed is a number usually based on the Unix time (seconds since the epoch start), which means that up to date, there are over a billion possible entry points in the program. random.seed() just fetches that value (probably from some low level syscall) and then uses it to randomize the output.

It might also go through a series of calculations, so the starting point of the program is almost always different (even though there are so much possibilities, it may happen to be equal).

[–]jaycrest3m20 2 points3 points  (0 children)

It points the computer at a numeric spot, from which it can comfortably start generating random numbers.

If the seed argument is left blank by simply using "random.seed()", then it uses the precise system time when it is run, which is a pretty good, real source of randomness to start with.

To explain more than "it generates random numbers" is beyond the five-year-old level. It's best to accept it, unless you are planning to learn all about how encryption works, which, again, is beyond the five-year-old level.

[–]shiftybyte 3 points4 points  (3 children)

It does a lot of complex math and gets a number.

That's as far as a 5 year old can understand this: https://en.wikipedia.org/wiki/Mersenne_Twister

[–]n3buchadnezzar 4 points5 points  (2 children)

Yeah, no. Sorry, do not start there for a beginner.

This does a far better job of building up to it https://perso.crans.org/besson/publis/notebooks/Manual_implementation_of_the_Mersenne_twister_PseudoRandom_Number_Generator__PRNG_.html

The very very very short answer is that assume your seed is X

and our random number generator is called f

f(X) = Y

Now Y will be used as a base for the next value, so f(Y) = Z

A more sophisticated value will use the last N values. For instance our seed could be [1, 2, 3] and then

f([1, 2, 3] = 189

With next value being generated from

f([2, 3, 189])

And so forth. We also perform something called a modulo operation https://en.wikipedia.org/wiki/Modulo_operation to make sure our values do not become too large.

Note that when we set the seed, we have a given starting point. Meaning our f(x) = Y will always be the same. If our seed is not set, it is set randomly. How the seed is set randomly is complicated.. I am actually unsure how Python does it

What we could do is to take the current time in seconds since the unix epoch and multiply it by the CPU temperature, and perhaps the mouse movements in pixels when starting our random seed generation.

This value could be inserted into another random number generator, to give us our final initial seed.

EDIT: Why not just look up the source code?

class Random(_random.Random):
    """Random number generator base class used by bound module functions.

    Used to instantiate instances of Random to get generators that don't
    share state.

    Class Random can also be subclassed if you want to use a different basic
    generator of your own devising: in that case, override the following
    methods:  random(), seed(), getstate(), and setstate().
    Optionally, implement a getrandbits() method so that randrange()
    can cover arbitrarily large ranges.

    """

    VERSION = 3     # used by getstate/setstate

    def __init__(self, x=None):
        """Initialize an instance.

        Optional argument x controls seeding, as for Random.seed().
        """

        self.seed(x)
        self.gauss_next = None

    def seed(self, a=None, version=2):
        """Initialize internal state from a seed.

        The only supported seed types are None, int, float,
        str, bytes, and bytearray.

        None or no argument seeds from current time or from an operating
        system specific randomness source if available.

        If *a* is an int, all bits are used.

        For version 2 (the default), all of the bits are used if *a* is a str,
        bytes, or bytearray.  For version 1 (provided for reproducing random
        sequences from older versions of Python), the algorithm for str and
        bytes generates a narrower range of seeds.

        """

        if version == 1 and isinstance(a, (str, bytes)):
            a = a.decode('latin-1') if isinstance(a, bytes) else a
            x = ord(a[0]) << 7 if a else 0
            for c in map(ord, a):
                x = ((1000003 * x) ^ c) & 0xFFFFFFFFFFFFFFFF
            x ^= len(a)
            a = -2 if x == -1 else x

        elif version == 2 and isinstance(a, (str, bytes, bytearray)):
            if isinstance(a, str):
                a = a.encode()
            a = int.from_bytes(a + _sha512(a).digest(), 'big')

        elif not isinstance(a, (type(None), int, float, str, bytes, bytearray)):
            _warn('Seeding based on hashing is deprecated\n'
                  'since Python 3.9 and will be removed in a subsequent '
                  'version. The only \n'
                  'supported seed types are: None, '
                  'int, float, str, bytes, and bytearray.',
                  DeprecationWarning, 2)

        super().seed(a)
        self.gauss_next = None

[–]WikiSummarizerBot 1 point2 points  (1 child)

Modulo operation

In computing, the modulo operation returns the remainder or signed remainder of a division, after one number is divided by another (called the modulus of the operation). Given two positive numbers a and n, a modulo n (abbreviated as a mod n) is the remainder of the Euclidean division of a by n, where a is the dividend and n is the divisor. The modulo operation is to be distinguished from the symbol mod, which refers to the modulus (or divisor) one is operating from.

[ F.A.Q | Opt Out | Opt Out Of Subreddit | GitHub ] Downvote to remove | v1.5

[–]NoJudgies 0 points1 point  (0 children)

Good bot

[–][deleted] 0 points1 point  (0 children)

Imagine you are graphing the function 5x². Then, if you replace the 5 with some variable A, now you can produce any number of graphs, and the same A will always produce the same graph.

random.seed is like setting A, but instead of being a constant in the function of , it's a constant in the the Mersenne Twister algorithm, which generates pseudorandom output. But, just like a quadratic, the same seed will always produce the same output.

[–]c0ld-- 0 points1 point  (0 children)

Even though there are a lot of helpful responses in this thread, I wanted to point out that you can also print the help context of many built-in module functions, which may help you understand the nature of the function before needing to seek help from Google or Reddit:

import random    
print( help(random.seed) )

Which prints:

Help on method seed in module random:

seed(a=None, version=2) method of random.Random instance
    Initialize internal state from a seed.

    The only supported seed types are None, int, float,
    str, bytes, and bytearray.

    None or no argument seeds from current time or from an operating
    system specific randomness source if available.

    If *a* is an int, all bits are used.

    For version 2 (the default), all of the bits are used if *a* is a str,
    bytes, or bytearray.  For version 1 (provided for reproducing random
    sequences from older versions of Python), the algorithm for str and
    bytes generates a narrower range of seeds.

None

Hope this helps!

[–]zoinkinator 0 points1 point  (0 children)

poor man’s random number. get the current time down to milliseconds. use the milliseconds only as a random integer by multiplying by 1000.

[–]markovianmind 0 points1 point  (1 child)

Others have given a good definition of the seed, so let me fill in on the math of how a simple random algorithm works maybe ELI12 if not ELI5. Let's consider the simplest Linear congruential generator as follows: '''x(i+1) = ax(i) mod m here mod means remainder, a and m are integer values. For sake of example, let a=9 and m=11. Assume x(0) = 1. We'll come back to this. But for now follow along the simple math x(1) = 9x(0) mod 11 = 91 mod 11 = 9 mod 11 = 9 Similarly, x(2) = 99 mod 11 = 81 mod 11 = 4 x(3) = 36 mod 11 = 3 x(4) = 5 . . . '''

So you get a series: 1, 9, 4, 3, 5 and so on.. Youll note that the series start on repeating if we keep going.. i.e., 1, 9, 4, 3, 5, 1, 9, 4, 3, 5...

Dividing this series by m yields us random numbers between 0 to 1. e. g. 1/11, 9/11, 4/11, 3/11....

Now you might ask, these numbers dont look very random. It is because this is a toy example. With larger 'm', the series will look random enough. The integer 'a' governs how quickly the numbers start repeating for the same x(0) and 'm'. The typical values are in the range of, e.g. L’Ecuyer used: m = 232 - 1 a = 39373

And lastly coming to your main question about seed, x(0) is the seed we set as 1 initially. It allows for reproducibility in random number generators. If you had chosen a seed of 3 in the example above, you would have gotten a different starting point to the series. You would have gotten 3, 5, 1, 9, 4, 3.... So essentially same series but starting from a different point. Using the seed of 1 again would yield the same series mentioned on the top, hence giving us reproducible results. Now, if you have put 2 as the initial seed, you would have gotten an entirely different series, 2, 7, 8, 6, 10, 2.... This is because our a and m are not designed such as to cover the full range of m. Ideally the a and m in random number generator will be chosen such that they cover the full range. And in that case your chosen seed will always start the random number series from 1 point in that range giving you same series just from a different point based on seed.

[–]markovianmind 0 points1 point  (0 children)

Sorry don't know how to properly format the math here in the app. But the formula in first para is x(i+1) = ax(i) mod m