random — 伪随机数生成器(史上总结最全)

简介: random — 伪随机数生成器(史上总结最全)

阅读本文需要6.5分钟

目的:实现几种类型的伪随机数生成器。

random 模块基于 Mersenne Twister 算法提供了一个快速的伪随机数生成器。Mersenne Twister 最初开发用于为蒙特卡洛模拟器生成输入,可生成具有分布均匀,大周期的数字,使其可以广泛用于各种应用。

生成随机数

random() 函数从生成的序列中返回下一个随机浮点数。所有返回值都在  0<= n < 1.0 范围内。

random_random.py

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

反复运行程序生成不同序列的数字。

$ 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() 方法。

random_uniform.py

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

传入最小和最大值, uniform() 使用公式 min + (max - min) *random() 调整 random() 的返回值。

$ python3 random_uniform.py
12.428 93.766 95.359 39.649 88.983

Seeding

random() 每次调用的时候都生成不同的值,并且在它重复任何数字之前有一个很大的周期。这对于生成唯一值及其变体很有用,但有时以不同的方式处理相同的数据集是很有用的。一种技术是用一个程序生成随机数并保存他们以通过单独的步骤进行处理。然而,对于大量数据可能不实用,所以,random 模块包含了 seed() 函数用于初始化伪随机数生成器以生成预期的一组值。

random_seed.py

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

种子值用于控制根据公式生成的伪随机数序列的第一个值,并且由于公式是确定的,所以种子改变后它实际上设置了生成的完整序列。传入 seed()  的参数可以是任何可哈希的对象。默认使用基于平台的随机源(如果可用),否则,使用当前时间。

$ python3 random_seed.py
0.134 0.847 0.764 0.255 0.495
$ python3 random_seed.py
0.134 0.847 0.764 0.255 0.495

保存状态

random() 使用的伪随机数生成算法的内部状态可以被保存下来,然后用于控制子序列运行时生成的数字。在继续之前,从较早的输入恢复状态减少了生成重复值和序列的可能性。getstate() 函数可以返回随后用于 setstate() 的重新初始化随机数生成器的数据。

random_state.py

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:
    # 使用一个初始状态
    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=' ')
print()

getstate() 返回的数据是一个实现细节,所以这个例子使用 pickle  保存数据到文件,仅仅将它视作一个黑盒子。当程序开始的时候,如果该文件存在,它加载旧的状态然后继续。每次在保存状态前后运行生成了一些数字,去演示恢复状态导致生成器产生了再次产生了相同的值。

$ 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() 直接生成整数更方便。

random_randint.py

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() 的取值范围是其参数的闭区间。数字可以是正数或负数,但第一个值应小于第二个值。

$ python3 random_randint.py
[1, 100]: 98 75 34
[-5, 5]: 4 0 5

randrange() 是从范围中选择值的更一般形式。

random_randrange.py

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

randrange() 支持 step 参数,除了开始和结束值, 所以它完全等同于从 range(start, stop, step) 中选择一个随机值。它效率更高,因为范围实际上并没有构建。

$ python3 random_randrange.py
15 20 85

随机选择序列值

随机数生成器的一个常见用途是从枚举序列中返回随机项,既是这些值不是数字。 random 模块包含了 choice() 函数用于从序列中随机获取值。这个例子模拟了投 10000 次硬币正面和反面出现的次数。

random_choice.py

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'])

这里仅有两个可允许的结果,因此不是使用数字并转换他们,而是直接将 "heads" 和 "tails" 与 choice() 一起时候用。

$ python3 random_choice.py
Heads: 5091
Tails: 4909

排列

对棋牌游戏的模拟需要混合一副牌,然后把它们发给玩家,并且不能多次使用同一张牌。使用 choice() 会导致相同的牌被多次使用,因此可以使用 shuffle() 洗牌,然后在发牌的时候移除他们。

random_shuffle.py

import random
import 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)
# Deal 4 hands of 5 cards each
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)

卡片表示为带有面值和数字。通过每次向四个列表中添加一张卡片,并且将其从牌桌上移除以使其无法再次使用而创建默认的 「hands」。

$ 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()  函数用于生成不重复样本值,并且不改变输入序列。这个例子展示了从系统字典中打印随机样本单词。

random_sample.py

import random
with 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)

用于产生结果集的算法考虑了输入的大小和所请求的样本以尽可能有效地产生结果。

$ python3 random_sample.py
streamlet
impestation
violaquercitrin
mycetoid
plethoretical
$ python3 random_sample.py
nonseditious
empyemic
ultrasonic
Kyurinish
amphide

多个同时生成器

除了模块级别的函数之外,random 包含了一个 Random 类管理集合随机数生成器的内部状态。前面描述的所有函数都可以作为 Random 实例的可用方法,并且每个实例可以被单独初始化使用,而不会影响其他实例的返回值。

random_random_class.py

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()))

在一个具有良好原生随机值种子的系统上,实例以一个唯一状态运行。然而,如果没有好的平台随机数生成器,实例很可能被使用当前时间播种,然后就产生了相同的值。

$ 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()  生成构成其它算法基础的值。

random_system_random.py

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_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() (正太分布也被叫做高斯分布)。

相关函数  lognormvariate() 生成的伪随机值的对数符合正太分布。对数正态分布对于作为几个不相互作用的随机变量的乘积的值很有用。

近似分布

三角分布用于小样本量的近似分布。三角形分布的曲线在已知的最小和最大值处具有低点,并且在模式处具有高点,其基于最可能的结果( 由 triangular() 的模式参数反映)。

指数分布

expovariate() 生成一个指数分布,用于模拟均匀 Poisson 过程中的到达和间隔时间值,例如放射性衰减或者进入服务器的请求数。

Pareto 或者 幂等分布符合许多由 Long Tail 观察到的现象。paretovariate() 可以模拟个人资源分配(人们的财富,对音乐家的需求,对博客的关注等)。

Angular

Von Mises 或者 圆形正态分布(由 vonmisesvariate() 生成)用于计算循环值的概率,日历 T 天数和时间。

大小

betavariate() 使用 Beta 分布生成值,这通常用于贝叶斯统计和应用程序(如任务持续时间建模)。

gammavariate()  产生的 Gamma 分布用于模拟诸如等待时间,降雨量和计算误差之类事物的大小。

weibullvariate() 计算的 Weibull 分布用于故障分析,工业工程和天气预报。它描述了粒子或者其他离散对象的分布。


推荐阅读

Python常用的几种常用的内置函数(干货)

Python加密服务(一)

Python怎么删除字符

岁月有你   惜惜相处

相关文章
|
Web App开发 关系型数据库 RDS
电源缓启动(软起动)原理
该文讨论了电源的缓启动(软起动)技术,主要是为了解决热插拔过程中可能产生的电源振荡和大电流冲击问题。缓启动通过防抖动延时和控制电流上升斜率来避免系统受影响或设备受损。文章提到了两种类型的缓启动电路:电压斜率型和电流斜率型,并详细解释了电压型缓启动电路的工作原理,包括各个元件的作用和电路的缓启动阶段。
873 12
|
缓存 Kubernetes 数据安全/隐私保护
k8s1.18多master节点高可用集群安装-超详细中文官方文档
k8s1.18多master节点高可用集群安装-超详细中文官方文档
|
网络协议 Shell Android开发
解决adb网络连接中出现的“由于目标计算机积极拒绝,无法连接”错误
解决adb网络连接中出现的“由于目标计算机积极拒绝,无法连接”错误
解决adb网络连接中出现的“由于目标计算机积极拒绝,无法连接”错误
Java 大视界 -- 基于 Java 的大数据实时数据处理在车联网车辆协同控制中的应用与挑战(197)
本文深入探讨了基于 Java 的大数据实时数据处理在车联网车辆协同控制中的关键应用与技术挑战。内容涵盖数据采集、传输与实时处理框架,并结合实际案例分析了其在车辆状态监测、交通优化与协同驾驶中的应用效果,展示了 Java 大数据技术在提升交通安全性与效率方面的巨大潜力。
|
8月前
|
CDN
阿里云 ESA 边缘加速(免费版) - 领取CDN加速免费套餐
阿里云ESA免费版全面开放!无论国际站或中国站用户,均可免费申请,享受无限流量CDN服务,大幅提升网站访问速度。
3634 7
|
C++ Windows
(1)Qt的基本数据类型以及基本输出
这篇文章介绍了Qt框架中的基本数据类型和日志输出方法,包括如何使用QDebug类及其相关函数进行调试和日志记录,以及如何取消输出时的空格和字符串引号,还提供了抑制输出的两种方式。
826 4
(1)Qt的基本数据类型以及基本输出
|
Java
flyway报错SLF4J: No SLF4J providers were found.或者SLF4J: Defaulting to no-operation (NOP) logger implem
flyway报错SLF4J: No SLF4J providers were found.或者SLF4J: Defaulting to no-operation (NOP) logger implem
1058 2
|
监控 前端开发 网络协议
《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(5)-Fiddler监控面板详解
【2月更文挑战第7天】《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(5)-Fiddler监控面板详解 按照从上往下,从左往右的计划,今天就轮到介绍和分享Fiddler的监控面板了。监控面板主要是一些辅助标签工具栏。有了这些就会让你的会话请求和响应时刻处监控中毫无隐私可言。监控面板是fiddler最核心的功能之一。记录了来自于服务器端(webServer)的请求会话。包括页面的请求和静态文件的请求。状态面板主要显示的是会话及会话的状态。位于软件界面右边的这一大块面板,即为辅助标签 + 工具,宏哥称之为监控。
707 0
|
算法 SoC
在写PWM风扇驱动之前:如何透过FG信号,获取风扇转速?
本文介绍了如何通过PWM风扇的FG信号线获取风扇转速的方法,包括风扇规格的解读、FG信号的公式推导、软件算法的解析,以及在全志H713平台上实现风扇转速获取的驱动代码示例。
3665 2
在写PWM风扇驱动之前:如何透过FG信号,获取风扇转速?
如何查看你的公网ip?
在公司你的终端一般会给你分配一个内网ip,然后经过NAT,因为不可能办公室里面的 IP 也是公网可见的,公网地址实在是太贵了,所以一般就是整个办公室共用一个到两个出口 IP 地址。
1311 0