Python3标准库:random伪随机数生成器
Posted 爱编程的小灰灰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python3标准库:random伪随机数生成器相关的知识,希望对你有一定的参考价值。
1. random伪随机数生成器
random模块基于Mersenne Twister算法提供了一个快速伪随机数生成器。原来开发这个生成器是为了向蒙特卡洛模拟生成输入,Mersenne Twister算法会生成大周期近均匀分布的数,因此适用于大量不同类型的应用。
1.1 生成随机数
random()函数从所生成的序列返回下一个随机的浮点值。返回的所有值都落在0<=n<1.0区间内。
import random for i in range(5): print(\'%04.3f\' % random.random(), end=\' \') print()
重复运行这个程序会产生不同的数字序列。
要生成一个指定数值区间内的数,则要使用uniform()。
import random for i in range(5): print(\'{:04.3f}\'.format(random.uniform(1, 100)), end=\' \') print()
传入最小值和最大值,uniform()会使用公式min+(max-min)*random()来调整random()的返回值。
1.2 指定种子
每次调用random()都会生成不同的值,并且在一个非常大的周期之后数字才会重复。这对于生成唯一值或变化的值很有用,不过有些情况下可能需要提供相同的数据集,从而以不同的方式处理。对此,一种技术是使用一个程序生成随机值,并保存这些随机值,以便在另一个步骤中再做处理。不过,这对于量很大的数据来说可能并不实用,所以random包含了一个seed()函数,可以用来初始化伪随机数生成器,使它能生成一个期望的值集。
import random random.seed(1) for i in range(5): print(\'{:04.3f}\'.format(random.random()), end=\' \') print()
种子(seed)值会控制由公式生成的第一个值,该公式可用来生成伪随机数。由于公式是确定的,所以改变种子后便设置了将生成的整个序列。seed()的参数可用是任意的可散列对象。默认为使用一个平台特定的随机源(如果有的话)。但如果没有这样一个随机源,则使用当前时间。
1.3 保存状态
random()使用的伪随机算法的内部状态可以保存,并用于控制后续生成的随机数。如果在继续生成随机数之前恢复前一个状态,则会减少出现重复的可能性,即避免出现之前输入中重复的值或值序列。getstate()函数会返回一些数据,以后可以借助setstate()利用这些数据重新初始化伪随机数生成器。
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()
getstate()返回的数据是一个实现细节,所以这个例子用pickle将数据保存到一个文件;否则,它会把伪随机数生成器当作一个黑盒。如果程序开始时这个文件存在,则加载原来的状态并继续。每次运行时都会在保存状态之前和之后生成一些数,以展示恢复状态会使生成器再次生成同样的值。
第一次:
第二次:
1.4 随机整数
random()将生成浮点数。可以把结果转换为整数,不过直接使用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()
randint()的参数是值的闭区间的两端。这些数可以是正数或负数,不过第一个值要小于第二个值。
randrange()是从区间选择值的一种更一般的形式。
import random for i in range(3): print(random.randrange(0, 101, 5), end=\' \') print()
除了开始值(start)和结束值(stop),randrange()还支持一个步长(step)参数,所以它完全等价于从range(start,stop,step)选择一个随机值。不过randrange更高效,因为它并没有真正构造区间。
1.5 选择随机元素
随机数生成器有一种常见用法,即从一个枚举值序列中选择元素,即使这些值并不是数字。random包括一个choice()函数,可以从一个序列中随机选择。下面这个例子模拟硬币10000此来统计多少次面朝上,多少次面朝下。
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\'])
由于只允许两个结果,所以不必使用数字然后再进行转换,这里对choice()使用了单词“heads”(表示面朝上)和“tails”(表示面朝下)。结果以表格形式存储在一个字典中,使用结果名作为键。
1.6 排列
要模拟一个扑克牌游戏,需要把一副牌混起来,然后向玩家发牌,同一张牌不能多次使用。使用choice()可能导致同一张牌被发出两次,所以,可以用shuffle()来洗牌,然后在发各张牌时删除所发的牌。
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)
这些扑克牌被表示为字符串,包括面值和一个表示花色的字母。要创建发出的“一手牌”,可以一次向4个列表分别增加一张牌,然后从这副牌中将发出的牌删除,使这些牌不会再次发出。
1.7 采样
很多模拟需要从大量输入值中得到随机样本。sample()函数可以生成无重复值的样本,并且不会修改输入序列。下面的例子会打印系统字典中单词的一个随机样本。
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)
第一次:
第二次:
1.8 多个并发生成器
除了模块级函数,random还包括一个Random类以管理多个随机数生成器的内部状态。之前介绍的所有函数都可以作为Random实例的方法得到,并且每个实例都可以被单独初始化和使用,而不会干扰其他实例返回的值。
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()))
如果系统上设置了很好的原生随机值种子,那么实例会有独特的初始状态。不过,如果没有一个好的平台随机值生成器,那么不同实例往往会以当前时间作为种子,并因此生成相同的值。
1.9 SystemRandom
有些操作系统提供了一个随机数生成器,可以访问更多能引入生成器的信息源。random通SystemRandom类提供了这个特性,该类与Random的API相同,不过使用os.urandom()生成值,该值会构成所有其他算法的基础。
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()))
SystemRandom产生的序列是不可再生的,因为其随机性来自系统,而不是来自软件状态(实际上,seed()和setstate()根本不起作用)。
以上是关于Python3标准库:random伪随机数生成器的主要内容,如果未能解决你的问题,请参考以下文章