Python 3 Standard Library: random pseudo-random number generator

1. random Pseudo-random Number Generator

The random module provides a fast pseudo-random number generator based on the Mersenne Twister algorithm.This generator was originally developed to generate input to Monte Carlo simulations. The Mersenne Twister algorithm generates large periodic, nearly uniformly distributed numbers and is therefore suitable for a large number of different types of applications.

1.1 Generate Random Numbers

The random() function returns the next random floating point value from the generated sequence.All returned values fall within the range 0<=n<1.0.

import random

for i in range(5):
    print('%04.3f' % random.random(), end=' ')
print()

Repeated running of this program will result in a different sequence of numbers.

To generate a number within a specified range of values, use uniform().(

import random

for i in range(5):
    print('{:04.3f}'.format(random.uniform(1, 100)), end=' ')
print()

By passing in the minimum and maximum values, uniform() adjusts the return value of random() using the formula min+(max-min)*random().

1.2 Specified seeds

Each call to random() generates a different value and the number repeats after a very large cycle.This is useful for generating unique or varying values, but there may be cases where you need to provide the same dataset to handle it differently.One technique is to use a program to generate random values and save them for processing in another step.However, this may not be practical for large amounts of data, so random includes a seed() function that can be used to initialize the pseudo-random number generator to produce the desired set of values.

import random

random.seed(1)

for i in range(5):
    print('{:04.3f}'.format(random.random()), end=' ')
print()

The seed value controls the first value generated by a formula that can be used to generate pseudo-random numbers.Since the formula is deterministic, changing the seed sets the entire sequence that will be generated.The parameter of seed() can be any hashable object.The default is to use a platform-specific random source, if any.However, if there is no such random source, the current time is used.

1.3 Saved State

The internal state of the pseudo-random algorithm used by random() can be preserved and used to control subsequently generated random numbers.Restoring to the previous state before continuing to generate random numbers reduces the possibility of duplication, that is, avoiding duplicate values or sequences of values in previous inputs.The getstate() function returns some data that can then be used by setstate() to reinitialize the pseudo-random number generator.(

import random
import os
import pickle

if os.path.exists('state.dat'):
    # Restore the previously saved state
    print('Found state.dat, initializing random module')
    with open('state.dat', 'rb') as f:
        state = pickle.load(f)
    random.setstate(state)
else:
    # Use a well-known start state
    print('No state.dat, seeding')
    random.seed(1)

# Produce random values
for i in range(3):
    print('{:04.3f}'.format(random.random()), end=' ')
print()

# Save state for next time
with open('state.dat', 'wb') as f:
    pickle.dump(random.getstate(), f)

# Produce more random values
print('\nAfter saving state:')
for i in range(3):
    print('{:04.3f}'.format(random.random()), end=' ')
print()

The data returned by getstate() is an implementation detail, so this example uses pickle to save the data to a file; otherwise, it treats the pseudo-random number generator as a black box.If this file exists at the beginning of the program, load the original state and continue.Each run generates a number before and after the saved state to show that the restored state causes the generator to generate the same value again.

For the first time:

The second time:

1.4 Random Integer

random() generates floating point numbers.You can convert the results to integers, but it is easier to generate integers directly using randint().

import random

print('[1, 100]:', end=' ')

for i in range(3):
    print(random.randint(1, 100), end=' ')

print('\n[-5, 5]:', end=' ')
for i in range(3):
    print(random.randint(-5, 5), end=' ')
print()

The parameters of randint() are the ends of the closed interval of values.These numbers can be positive or negative, but the first value is less than the second.

randrange() is a more general form of selecting values from intervals.

import random

for i in range(3):
    print(random.randrange(0, 101, 5), end=' ')
print()

In addition to the start and stop values, randrange() supports a step parameter, so it is exactly equivalent to selecting a random value from a range(start,stop,step).However, randrange is more efficient because it does not really construct intervals.

1.5 Select random elements

A common use of random number generators is to select elements from a sequence of enumerated values, even if they are not numbers.Random includes a choice() function that can be randomly selected from a sequence.The following example simulates a coin of 10,000 to count how many times it faces up and how many times it faces down.

import random
import itertools

outcomes = {
    'heads': 0,
    'tails': 0,
}
sides = list(outcomes.keys())

for i in range(10000):
    outcomes[random.choice(sides)] += 1

print('Heads:', outcomes['heads'])
print('Tails:', outcomes['tails'])

Since only two results are allowed, there is no need to use a number and convert again. Here the words "heads" (face up) and "tails" (face down) are used for choice().The results are stored in a dictionary in tabular form, using the result name as the key.

1.6 Arrangement

To simulate a poker game, you need to mix up a suit of cards and deal to the player. The same card cannot be used more than once.The use of choice() may result in the same card being issued twice, so shuffle() can be used to shuffle the cards, and then delete the cards when each card is issued.

import random
import itertools

FACE_CARDS = ('J', 'Q', 'K', 'A')
SUITS = ('H', 'D', 'C', 'S')

def new_deck():
    return [
        # Always use 2 places for the value, so the strings
        # are a consistent width.
        '{:>2}{}'.format(*c)
        for c in itertools.product(
            itertools.chain(range(2, 11), FACE_CARDS),
            SUITS,
        )
    ]

def show_deck(deck):
    p_deck = deck[:]
    while p_deck:
        row = p_deck[:13]
        p_deck = p_deck[13:]
        for j in row:
            print(j, end=' ')
        print()

# Make a new deck, with the cards in order
deck = new_deck()
print('Initial deck:')
show_deck(deck)

# Shuffle the deck to randomize the order
random.shuffle(deck)
print('\nShuffled deck:')
show_deck(deck)

# Deal 4 hands of 5 cards each
hands = [[], [], [], []]

for i in range(5):
    for h in hands:
        h.append(deck.pop())

# Show the hands
print('\nHands:')
for n, h in enumerate(hands):
    print('{}:'.format(n + 1), end=' ')
    for c in h:
        print(c, end=' ')
    print()

# Show the remaining deck
print('\nRemaining deck:')
show_deck(deck)

These poker cards are represented as strings, including face values and a letter representing the suit.To create a issued "hand", you can add one card to each of the four lists at a time and remove the issued cards from the deck so that they are not issued again.

1.7 Sampling

Many simulations require random samples from a large number of input values.The sample() function generates a sample with no duplicate values and does not modify the input sequence.The following example prints a random sample of words from the system dictionary.

words.txt

pear
apricot
grape
pineapple
apple
peach
banana
plum
watermelon
lemon
orange
mango
strawberry

Demo.py

import random

with open('words.txt', 'rt') as f:
    words = f.readlines()
words = [w.rstrip() for w in words]

for w in random.sample(words, 5):
    print(w)

For the first time:

The second time:

More than 1.8 concurrency generators

In addition to module-level functions, random also includes a Random class to manage the internal state of multiple random number generators.All the functions described earlier can be obtained as methods for Random instances, and each instance can be initialized and used independently without interfering with the values returned by other instances.

import random
import time

print('Default initializiation:\n')

r1 = random.Random()
r2 = random.Random()

for i in range(3):
    print('{:04.3f}  {:04.3f}'.format(r1.random(), r2.random()))

print('\nSame seed:\n')

seed = time.time()
r1 = random.Random(seed)
r2 = random.Random(seed)

for i in range(3):
    print('{:04.3f}  {:04.3f}'.format(r1.random(), r2.random()))

If a good seed of native random values is set on the system, the instance will have a unique initial state.However, without a good platform random value generator, different instances tend to seed the current time and therefore generate the same value.

1.9 SystemRandom

Some operating systems provide a random number generator that provides access to more sources of information that can be introduced into the generator.The randompass SystemRandom class provides this feature, which is the same as Random's API, but uses os.urandom() to generate a value that forms the basis for all other algorithms.

import random
import time

print('Default initializiation:\n')

r1 = random.SystemRandom()
r2 = random.SystemRandom()

for i in range(3):
    print('{:04.3f}  {:04.3f}'.format(r1.random(), r2.random()))

print('\nSame seed:\n')

seed = time.time()
r1 = random.SystemRandom(seed)
r2 = random.SystemRandom(seed)

for i in range(3):
    print('{:04.3f}  {:04.3f}'.format(r1.random(), r2.random()))

The sequence generated by SystemRandom is not renewable because its randomness comes from the system, not from the software state (in fact, seed() and setstate() do not work at all).

Tags: Python less

Posted on Fri, 06 Mar 2020 17:48:30 -0800 by Brusca