本文翻译自 Doug Hellmann 的 PyMOTW-3项目的random模块。
原文链接:https://pymotw.com/3/random/index.html
本文使用cc-by-nc-sa 4.0 协议共享。
random—伪随机数生成器
random
模块提供了基于Mersenne Twister 算法的快速伪随机数生成器。起初开发它是为了产生蒙特卡洛模拟的输入,Mersenne Twister 生成的数近乎均匀分布并且周期大,使其适合更加广泛的应用。
生成随机数
random
函数从生成序列中返回下一个随机浮点数。所有返回值都包含在0 <= n < 1.0。
1 2 3 4 5 6 7 import randomfor i in range (5 ): print ('%04.3f' % random.random(), end=' ' )print ()
重复运行程序以获得不同的值序列。
1 2 3 4 5 6 7 $ python3 random_random.py 0.859 0.297 0.554 0.985 0.452$ python3 random_random.py 0.797 0.658 0.170 0.297 0.593
为获得特定范围的数,使用uniform()
。
1 2 3 4 5 6 7 import randomfor i in range (5 ): print ('{:04.3f}' .format (random.uniform(1 , 100 )), end=' ' )print ()jiang
传递最大值和最小值, 然后uniform()
使用公式min + (max - min) * random()
调整random()
的返回值。
1 2 3 $ python3 random_uniform.py 12.428 93.766 95.359 39.649 88.983
种子
random()
在每次调用时产生不同的值,在重复任何值前有相当大的周期。这对产生唯一的值或变量很有用,有时也对以不同的方式处理相同的数据集很有用。一种技术是使用程序生成随机值,并保存随机值到独立的步骤中处理。但是,这对大型数据集来说不切实际,因此random
提供了初始化伪随机数生成器的seed()
函数,以生成一组预期的值。
1 2 3 4 5 6 7 8 9 import random random.seed(1 )for i in range (5 ): print ('{:04.3f}' .format (random.random()), end=' ' )print ()
种子控制了伪随机数生成公式的第一个值,并当公式确定时也设置了种子改变后生成的完整序列。seed()
的参数可以是任何hashable 对象。默认使用平台特定的随机资源,如果有的话。否则,使用当前时间。
保存状态
random()
中使用的伪随机数算法的内部状态可以被保存下来,用以控制后续运行中生成的数字。在继续运行前保存先前的状态可减少从以前的输入重复值或值序列的可能性。getstate()
返回的数据可用于稍后通过setstate()
重新初始化随机数生成器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import randomimport osimport pickleif os.path.exists('state.dat' ): print ('Found state.dat, initializing random module' ) with open ('state.dat' ,'rb' ) as f: state =pickle.load(f) random.setstate(state)else : print ('No state.dat, seeding' ) random.seed(1 )for i in range (3 ): print ('{:04.3f}' .format (random.random()), end=' ' )print ()with open ('state.dat' , 'wb' ) as f: pickle.dump(random.getstate(), f)print ('\nAfter saving state:' )for i in range (3 ): print ('{:04.3f}' .format (random.random()), end=' ' )
getstate()
返回的数据是一个实现细节,因此本例中使用pickle将数据保存在文件中,在其他地方可看作黑箱。如果程序开始时文件存在,则加载旧状态继续运行。每次运行在保存态前后生成一些数字,可说明恢复状态导致生成器再次生成相同的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ python3 random_state.py No state.dat, seeding 0.134 0.847 0.764 After saving state: 0.255 0.495 0.449$ python3 random_state.py Found state.dat, initializing random module 0.255 0.495 0.449 After saving state: 0.652 0.789 0.094
随机整数
random()
生成浮点数。虽然将浮点数转化整数是可行的,但是使用randint()
直接生成整数更加便捷。
1 2 3 4 5 6 7 8 9 10 11 12 13 import randomprint ('[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 ()
ranint()
的参数是值范围的闭区间。参数可以是正数或负数,但第一个值应该小于第二个值。
1 2 3 4 $ python3 random_randint.py [1, 100]: 98 75 34 [-5, 5]: 4 0 5
randrange()
是从范围中选择值的更一般形式。
1 2 3 4 5 6 7 import randomfor i in range (3 ): print (random.randrange(0 , 101 , 5 ), end=' ' )print ()
randrange()
除了支持start和stop参数,还支持step参数。因此它等效于从range(start, stop, step)
中选择值。这更有效,因为范围不是实际构造的。
随机挑选项目
随机数生成器的一个常见用途是从枚举序列中随机挑选项目,尽管这些值可能不是数。 random
提供了choice()
函数来从序列中做随机挑选。此例模拟了抛硬币1000次,以计算正面朝上和反面朝上的次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import randomimport 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' ])
抛硬币有两种结果,因此不是使用数字,而是将他们转化为"head"和 "tail"再传递给choice()
。使用结果名称作为键,将结果列在字典中。
1 2 3 4 $ python3 random_choice.py Heads: 5091 Tails: 4909
排列
扑克游戏模拟需要打乱牌堆,并将牌发给玩家,不能多次发同一张牌。使用choice()
会导致同一张牌被发两次,因此,可以结合使用shuffle()
在发牌后移走某张牌。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 import randomimport itertools FACE_CARDS = ('J' , 'Q' , 'K' , 'A' ) SUITS = ('H' , 'D' , 'C' , 'S' )def new_deck (): return [ '{:>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 () deck = new_deck()print ('Initial deck:' ) show_deck(deck) random.shuffle(deck)print ('\nShuffled deck:' ) show_deck(deck) hands = [[], [], [], []]for i in range (5 ): for h in hands: h.append(deck.pop())print ('\nHands:' )for n, h in enumerate (hands): print ('{}:' .format (n + 1 ), end=' ' ) for c in h: print (c, end=' ' ) print ()print ('\nRemaining deck:' ) show_deck(deck)
纸牌用字符串表示,字符串的第一位为面值(2-11,J,Q,K,A),第二位指示花色。”发牌手“每次向四组list依次增加一张牌,然后从牌堆将其移除以使不能再发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ python3 random_shuffle.py Initial deck: 2H 2D 2C 2S 3H 3D 3C 3S 4H 4D 4C 4S 5H 5D 5C 5S 6H 6D 6C 6S 7H 7D 7C 7S 8H 8D 8C 8S 9H 9D 9C 9S 10H 10D 10C 10S JH JD JC JS QH QD QC QS KH KD KC KS AH AD AC AS Shuffled deck: QD 8C JD 2S AC 2C 6S 6D 6C 7H JC QS QC KS 4D 10C KH 5S 9C 10S 5C 7C AS 6H 3C 9H 4S 7S 10H 2D 8S AH 9S 8H QH 5D 5H KD 8D 10D 4C 3S 3H 7D AD 4H 9D 3D 2H KC JH JS Hands: 1: JS 3D 7D 10D 5D 2: JH 9D 3H 8D QH 3: KC 4H 3S KD 8H 4: 2H AD 4C 5H 9S Remaining deck: QD 8C JD 2S AC 2C 6S 6D 6C 7H JC QS QC KS 4D 10C KH 5S 9C 10S 5C 7C AS 6H 3C 9H 4S 7S 10H 2D 8S AH
抽样
许多模拟都需要从输入值的总体随机抽样。sample()
函数可在不产生重复值和不修改输入序列的情况下生成样本。下面的例子从系统辞典中打印单词的随机样本。
1 2 3 4 5 6 7 8 9 10 import randomwith open ('/usr/share/dict/words' , 'rt' ) as f: words = f.readlines() words = [w.rstrip() for w in words]for w in random.sample(words, 5 ): print (w)
生成结果集的算法考虑了输入的大小,以使尽可能快地生成样本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ python3 random_sample.py streamlet impestation violaquercitrin mycetoid plethoretical$ python3 random_sample.py nonseditious empyemic ultrasonic Kyurinish amphide
多重独立生成器
除了模块级函数,random
提供了Random
类来管理各随机数生成器的内部状态。前面描述的函数都可以看作Random
实例的方法,每个实例可以独立的初始化和使用,并且不干扰其他实例返回的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import randomimport timeprint ('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 2 3 4 5 6 7 8 9 10 11 12 13 $ python3 random_random_class.py Default initializiation: 0.862 0.390 0.833 0.624 0.252 0.080 Same seed: 0.466 0.466 0.682 0.682 0.407 0.407
系统随机
许多操作系统提供随机数生成器,它们可以访问更多可以被引入生成器的熵源。random
通过SystemRandom
类来获得这些特性,该类与Random
拥有相同的API,但使用os.urandom()
来生成构成其他算法基础的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import randomimport timeprint ('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()
不起任何作用)。
1 2 3 4 5 6 7 8 9 10 11 12 13 $ python3 random_system_random.py Default initializiation: 0.110 0.481 0.624 0.350 0.378 0.056 Same seed: 0.634 0.731 0.893 0.843 0.065 0.177
非均匀分布
虽然random
生成的均匀分布值在很多方面有用,但是其他分布能更加准确地针对特定问题建模。random
模块也提供生成这些分布的函数。函数罗列如下,但不包含细节,因为它们的用途是特定的并需要更复杂的例子。
正态
正态分布广泛应用于非均匀的连续值,例如成绩、身高、体重等等。正态分布生成的曲线形状独特,被称为“钟形曲线”。random提供了两个函数生成正态分布的函数,normalvariate()
和稍快的gauss()
(正态分布又叫做Guassian分布)。
相关函数lognormalvariate()
生成一组伪随机数,它们的对数满足正态分布。对数正态分布对于不相互作用的随机变量的乘积很有用。
近似
三角分布用于描述小型样本的近似分布。三角分布的曲线在已知的最大值和最小值处具有最低点,在众数处具有最高点,这是基于“最可能”结果的估计(由triangular()
的众数参数反映)。
指数
expovariate()
生成的指数分布,用于模拟均匀泊松过程的到达时间或时间间隔值,例如放射性衰变或请求进入服务器的速率。
帕累托分布,又称幂律,匹配了许多观测到的现象,并由Chris Anderson在 The Long Tail 中推广。paretovariate()
函数对于模拟个体资源的分配很有用(财富对个人,对音乐家的需求,博客的关注等等)。
角度
冯米塞斯分布,又称循环正态分布(由vonmissesvariate()
生成)用于计算循环值的概率,例如角度,日历日(cm calendar days),时间。
大小(cm size)
由betavariate()
生成的 $\Beta$分布,广泛应用于贝叶斯统计和应用中,例如任务工期建模。
由gammavariative
生成的 $\Gamma$分布,用于建模计算诸如等待时间,降雨量,计算误差的大小(cm size)。
由weibullvariate()
计算得出的韦伯分布用于失效分析,工业设计,天气预报。他描述了粒子或其他离散物体大小(cm size)的分布。
See also