> 文档中心 > Python基础专题 - 超级详细的 Random(随机)原理解析与编程实践

Python基础专题 - 超级详细的 Random(随机)原理解析与编程实践

Python基础专题 - Random(随机)详解


李俊才 的 CSDN 博客
邮箱 :291148484@163.com
CSDN 主页:https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
本文地址:https://blog.csdn.net/qq_28550263/article/details/115441596

继续阅读文本前,先说一个难以回避的事实:
随机远远不是一个单纯的统计学概念,它是一个让物理学家都讨论不休、目前一切结论都可能存在历史局限性的谜。
随机的背后依赖于不确定性,目前支撑起随机存在的主要是当下量子力学中的相关理论并且在当前的实践中似乎是对的。
可以参考一些网络上的词条以及文章:
【上帝不会掷骰子】、【真随机】、【中国科学家在国际上首次实现器件无关的量子随机数


目 录

1、什么是随机

  • 1.1 随机可能是造物者的杰作
  • 1.2 伪随机、随机种子与计算机编程中的随机

2、使用Python内建random伪随机数模块

  • 2.1 随机性设定
  • 2.2 实用方法
    • (1)生成[0,1)上的随机浮点数
    • (2)生成[a,b][a,b)上一个随机浮点数(区间可以不是整数)
    • (3)生成[a,b]上的随机整数
    • (4)在区间[start, stop]上以间隔step生成随机整数
    • (5)按高斯分布(正态分布)生成随机数
    • (6)按 β\beta β分布生成[0,1]上的随机数
    • (7)用作选择器:从序列中随机选取一个元素
    • (8)随机打乱序列元素位置(序列洗牌函数)
    • (9)无重复随机抽样

3. Numpy伪随机数模块numpy.random

  • 3.1 numpy中的随机生成器
    • 3.1.1 [简单了解]Permuted Congruential Generator(PCG)
    • 3.1.2 [重要]Generator(bit_generator)类的进一步讲解

附录:常用数学分布介绍


1、什么是随机

1.1 随机可能是造物者的杰作

随机其实并不是一个像其表面那样容易理解的问题,但我们可以这样来描述随机:

对于一个取值有限的事件

  • 从上帝视角看,在事件真是发生前没有任何理由认为其中任何一个事件更可能会真实发生,那么认为这个事件是随机事件;
  • 从实践视角看,不论实践主体做任意多次独立重复实验,事件中任意一个取值(样本点)实际发生的频率随着重复实验的次数的无限增多,各个取值实际发生的次数无限接近于完全相等

然而不得不说,世界纷繁复杂,取值有限的事件无非是我们对这个无限广袤世界从某一角度高度抽象和离散化的结果。

如果将人类认知规律分为必然规律和统计规律,必然规律指事物本质的规律,它毫无例外地适用于事物所有个体。而统计规律是指通过对随机现象的大量观察后所呈现出来的事物的集体性规律

这里所谓上帝指得是拥有一个假想中的超能力以至于能够在事件发生前可以实践出无限种结果的主体。人类不是上帝,不能真实拥有上帝视角,因此所谓随机正是一种通过统计认知的规律。

1.2 伪随机、随机种子与计算机编程中的随机

随机不是人为制造的,而是根生于物理世界。人们不可以通过一种算法凭空创造不确定性
所谓【伪随机】是用确定性的算法计算出的一种在 [ 0 , 1 ] [0, 1] [0,1]上产生均匀分布的随机数序列的算法,它并不真正的随机,它需要一个随机种子为它提供随机性的动力之源。
随机种子本质就是一个作为初始条件的真随机数,一般通过其查询随机数表后再经过一定计算获得计算机编程中的随机数。

系统是如何获得随机种子呢?

生成伪随机数需要随机种子很多时候计算机系统维护了一个熵池,不断收集非确定性的硬件事件,如机器运行环境中产生的硬件噪音,在运行时刻某一瞬间的系统时间尾数等等,以此生成随机种子。

计算机编程里生成的随机数都是伪随机数,但在一般统计意义上看我们仍然可以视为真随机数。

2、使用Python内建random伪随机数模块

2.1 随机性设定

Python中有内建模块random可以用来生成[0.0,1.0)上的精度为53位的伪随机数,该模块使用马特赛特旋转演算法(MersenneTwister)实现了各种分布的伪随机数生成器。

MersenneTwister算法中,一个n位的线性反馈移位寄存器(LFSR)能够在重复之前产生 2 n − 1 2^n-1 2n1 位长的伪随机序列,称之为m序列,将产生的伪随机序列除以序列的周期,就可以得到(0,1)上均匀分布的伪随机序列了。

  • random.getstate() - 返回捕获生成器当前内部状态的对象。
    import randomstate = random.getstate()
  • random.setstate(state) - 将生成器的内部状态恢复到state的状态,一般由getstate()先获取state。
    import randomrandom.setstate(state)

该模块中,可以使用random.seed(a=None, version=2)方法指定a的指为一个确定数在编程时固定随机种子,这样在多次运行生成随机数的代码时,你会发现"随机"出来的结果是同一个。如果 a 被省略或为 None ,则使用当前系统时间。在上一节 (1.2节) 中对于内部机制有详细介绍,这里不在论述。

2.2 实用方法

(1)生成[0,1)上的随机浮点数

import randomrandom.random()

(2)生成[a,b][a,b)上一个随机浮点数(区间可以不是整数)

  • a<=b a <= ba<=b 时, a<=N<=b a <= N <= ba<=N<=b
  • b<a b < ab<a 时, b<=N<=a b <= N <= ab<=N<=a.
  • 取决于等式 a+(b−a)∗random() a + (b-a) * random()a+(ba)random() 中的浮点舍入,终点 b 可以包括或不包括在该范围内。
import randomrandom.uniform(2, 3)

(3)生成[a,b]上的随机整数

random.randint(a, b)
import randomrandom.randint(0, 9) # 生成[0,9]上的随机整数

(4)在区间[start, stop]上以间隔step生成随机整数

random.randrange(start, stop[, step])
import randomrandom.randrange(1, 9[, 2])

(5)按高斯分布(正态分布)生成随机数

random.gauss(mu, sigma)

mu 是平均值,sigma 是标准差

import randomrandom.gauss(mu, sigma)

(6)按 β \beta β分布生成[0,1]上的随机数

random.betavariate(alpha, beta)

参数的条件是 alpha > 0 和 beta > 0。 返回值的范围介于 0 和 1 之间。

import randomrandom.gauss(1, 3)

(7)用作选择器:从序列中随机选取一个元素

random.choice(seq)
random.choices(population, weights=None, *, cum_weights=None, k=1)
  • random.choice(seq):从非空序列 seq 返回一个随机元素。其中 seq 可以是包括列表、元组、range序列,甚至字符串在内的任意Python序列类型。如果 seq 为空,则引发 IndexError。

    import randomrandom.choice(["我","爱","学","习","Python","编","程"])random.choice("今天天气不错")
  • random.choices(population, weights=None, *, cum_weights=None, k=1):从population中选择替换,返回大小为 k 的元素列表。 如果 population 为空,则引发 IndexError。

    • 如果指定了 weight 序列,则根据相对权重进行选择;
    • 如果给出 cum_weights 序列,则根据累积权重(可能使用 itertools.accumulate() 计算)进行选择;
    • 如果既未指定 weight 也未指定 cum_weights ,则以相等的概率进行选择;
    • weights 或 cum_weights 可以使用任何与 random() 返回的 float 值互操作的数值类型(包括整数,浮点数和分数但不包括十进制小数)

(8)随机打乱序列元素位置(序列洗牌函数)

random.shuffle(x[, random])

将序列 x 随机打乱位置。

  • 可选参数 random 是一个0参数函数,在 [0.0, 1.0) 中返回随机浮点数;
  • 默认情况下,这是函数 random()
    import randomx = ["我","爱","学","习","Python","编","程"]random.shuffle(x)x

    Out[R]:[‘程’, ‘编’, ‘我’, ‘习’, ‘Python’, ‘学’, ‘爱’]

(9)无重复随机抽样

random.sample(population, k, *, counts=None)

通过无重复随机抽样返回包含来自总体的元素的新列表,同时保持原始总体不变。

  • population: 集合或者序列,为样本总体的容器;
  • k: int,抽取的子序列的长度(元素个数);
  • counts:list,元素为重复样本的权重,即该样本的出现次数。counts中的元素与population的元素一一对应。
    【代码1】
    import randompopulation = ['王', '者','荣','耀']random.sample(population,counts=[4, 3, 2, 1], k=3)

    和下面的代码时等效的:
    【代码2】

    import randompopulation = ['王', '王', '王', '王', '者', '者', '者', '荣', '荣', '耀']random.sample(population,counts=[4, 3, 2, 1], k=3)

这里无重复随机抽样中的无重复不是说返回的元素不能有一样的,而是指用的是五放回抽样。

3. Numpy伪随机数模块numpy.random

3.1 numpy中的随机生成器

3.1.1 [简单了解]Permuted Congruential Generator(PCG)

该算法更详细内容可以参考https://www.pcg-random.org/。

Permuted Congruential Generator(PCG)即所谓置换同余生成器,是用于随机数生成的一系列简单、快速、节省空间的,统计上良好的算法。在Python编程中,生成器是一种特殊的函数。生成器与普通函数不同的是,它返回迭代器的函数,只能用于迭代操作。生成器(generator)的应用往往解决了存储空间不足的问题,无需再程序以开始就完整计算整个值。

从RandomState类到Generator类

在之前,Numpy.random模块与Python内建的random等模块类似,直接使用马特赛特旋转演算法(Mersenne Twister)伪随机数生成器的容器,并提供有RandomState类:

numpy.random.RandomState(seed=None)

由于该类不是最好的选择,其用法仅以伪代码简要介绍如下:

from numpy.random import MT19937from numpy.random import RandomStaters1 = RandomState(12345)   # 获取seed=12345的RandomState()实例 rsmt19937 = MT19937()# 旧版MT19937 BitGeneratormt19937.state = rs.get_state()   # get_state()方法返回一个表示生成器内部状态的元组。rs2 = RandomState(mt19937)# 获取seed为mt19937状态的RandomState()实例 rs2 # 随机返回数值rs1.random()rs2.random()# 按照标准正态分布随机返回数值rs1.standard_normal()rs2.standard_normal()# 按照指数分布随机返回数值rs1.standard_exponential()rs2.standard_exponential()# 以上是控制可获取随机状态,即rs1和rs2都传入了seed参数。# 不传参数时才能随机中获取不一样的值(见之前关于计算机随机的介绍)。# 要使用随机性,可以参考下面的做法rs = RandomState()rs.random()    # 从[0.0,1.0)上随机抽取浮点数样本rs.rand(d0, d1,, dn)# 从标准正态分布中返回一个或多个样本。rs.normal([loc, scale, size])# 从正态(高斯)分布中抽取随机样本rs.standard_normal()  # 从标准正态(高斯)分布中抽取随机样本rs .standard_exponential()   # 从标准指数分布中随机抽取样本rs.shuffle(X)  # 对X进行洗牌,即随机打乱顺序。X是一个序列,如列表。rs.beta(a, b[, size]) # 从Beta分布中随机抽取样本binomial(n, p[, size])# 从二项分布中随机抽取样本chisquare(df[, size]) # 从卡方分布中抽取样本rs.dirichlet(alpha[, size])  # 从Dirichlet分布中抽取样本rs.f(dfnum, dfden[, size])   # 从F分布中抽取样本rs.gamma(shape[, scale, size])    # 从Gamma分布中抽取样本rs.geometric(p[, size])    # 从几何分布中抽取样本rs.gumbel([loc, scale, size])     # 从Gumbel分布中抽取样本rs.hypergeometric(ngood, nbad, nsample[, size]) # 从超几何分布中抽取样本

然而根据最新的Numpy 1.20.1文档,numpy.random.Generator(bit_generator)(以下简称Generator类)是上述numpy.random.RandomState(seed=None)(以下简称RandomState类)的替代品。

numpy.random.Generator(bit_generator)
  • Generator类依赖于附加的BitGenerator来管理状态并生成随机位,然后将这些随机位从有用的分布转换为随机值。所使用的默认BitGenerator Generator为PCG64。可以通过将实例化的BitGenerator传递给来更改BitGenerator Generator。numpy.random.default_rng()方法能够使用默认的BitGenerator(PCG64)构造一个新的Generator,用法示例如:

    from numpy.random import default_rngrng = default_rng(12345)     # 构造一个随机数生成器类rngrng.random()   # 从[0.0,1.0)上随机抽取浮点数样本

    Out[]:0.22733602246716966
    或者你不必指定随机种子,以让每次运行时随机获取一个不同的值:

    from numpy.random import default_rngrng = default_rng()   # 构造一个随机数生成器类rngrng.random()   # 从[0.0,1.0)上随机抽取浮点数样本

    Out[R]:0.642785685979066

  • 新的numpy.random.BitGenerator(seed=None) 类 是Numpy中通用BitG​​enerator的基类,所使用的默认BitGenerator Generator为PCG 64。PCG-64是 O’Neill 置换同余生成器 的128位实现。PCG64状态向量由2个无符号128位值组成,这些值在外部表示为Python ints

  • 可以用numpy.random.PCG64(seed=None)类生成一个新的BitG​​enerator,用法大致如下:

    from numpy.random import (    Generator,     PCG64,     SeedSequence)sg = SeedSequence(1234)      # 获取熵值(熵在信息理论中反应不确定度,实际中有系统收集,见上文)rg = [Generator(PCG64(s)) for s in sg.spawn(10)]  # 使用这些值生成新的BitGenerator

3.1.2 [重要]Generator(bit_generator)类的进一步讲解

前一节已经讲了,default_rng()函数是推荐使用的Generator生成器,并示例了生成一个随机数的写法。本节中将介绍更多常用的方法。
以下是展示用法的伪代码

from numpy.random import default_rngrng = default_rng()   # 构造一个随机数生成器类rngrng.integers(low[, high, size, dtype, endpoint]) # 从返回随机整数low(含)到high(不含),# 或者如果endpoint=True,low(含)到 high(含)rng.random([size, dtype, out])     # 返回[0.0,1.0)上的一个随机浮点随rng.choice(a[, size, replace, p, axis, shuffle]) # 随机选择样本rng.bytes(length)    # 返回随机字节,length为字节长度rng.shuffle(x[, axis])# 序列洗牌(随机打乱顺序)rng.permutation(x[, axis])   # 随机排列序列,或返回排列范围rng.permuted(x[, axis, out]) # 随机置换X沿轴轴线rng.beta(a, b[, size])# 从Beta分布中抽取样本rng.binomial(n, p[, size])   # 从二项分布中抽取样本rng.chisquare(df[, size])    # 从卡方分布中抽取样本rng.dirichlet(alpha[, size]) # 从Dirichlet分布中抽取样本rng.exponential([scale, size])      # 从指数分布中抽取样本rng.f(dfnum, dfden[, size])  # 从F分布中抽取样本rng.gamma(shape[, scale, size])     # 从Gamma分布中抽取样本rng.geometric(p[, size])     # 从几何分布中抽取样本rng.gumbel([loc, scale, size])      # 从Gumbel分布中抽取样本rng.hypergeometric(ngood, nbad, nsample[, size]) # 从超几何分布中抽取样本rng.laplace([loc, scale, size])    # 从具有指定位置(或均值)和比例(衰减)的# 拉普拉斯或双指数分布中抽取样本rng.logistic([loc, scale, size])   # 从逻辑分布中抽取样本rng.lognormal([mean, sigma, size]) # 从对数正态分布中抽取样本rng.logseries(p[, size])    # 从对数级数分布中抽取样本rng.multinomial(n, pvals[, size])  # 从多项分布中抽取样本rng.multivariate_hypergeometric(colors, nsample) # 从多元超几何分布生成变量rng.multivariate_normal(mean, cov[, size,])    # 从多元正态分布中抽取随机样本rng.negative_binomial(n, p[, size])# 从负二项分布中抽取样本rng.noncentral_chisquare(df, nonc[, size])# 从非中心卡方分布中抽取样本rng.noncentral_f(dfnum, dfden, nonc[, size])     # 从非中心F分布中抽取样本rng.normal([loc, scale, size])      # 从正态(高斯)分布中抽取随机样本rng.pareto(a[, size]) # 从具有指定形状的Pareto II或Lomax分布中抽取样本rng.poisson([lam, size])     # 从泊松分布中抽取样本rng.power(a[, size])  # 从具有正指数a-1的功率分布中以[0,1]抽取样本。rng.rayleigh([scale, size])  # 从瑞利(Rayleigh)分布中抽取样本rng.standard_cauchy([size])  # 从mode = 0的标准柯西分布中抽取样本。rng.standard_exponential([size, dtype, method, out])   # 从标准指数分布中抽取样本rng.standard_gamma(shape[, size, dtype, out])   # 从标准Gamma分布中抽取样本rng.standard_normal([size, dtype, out])  # 从标准正态分布(均值= 0,标准差 = 1)中抽取样本rng.standard_t(df[, size])     # 从自由度为df的标准学生氏分布(t分布)中抽取样本rng.triangular(left, mode, right[, size])    # 从间隔上的三角形分布中抽取样本。[left, right]rng.uniform([low, high, size])  # 从均匀分布中抽取样本rng.vonmises(mu, kappa[, size]) # 从冯·米塞斯(von Mises)分布中抽取样本rng.wald(mean, scale[, size])   # 从Wald或高斯逆分布中抽取样本rng.weibull(a[, size])   # 从威布尔分布中抽取样本rng.zipf(a[, size])      # 从Zipf分布中抽取样本

以下精选了上面展示的部分方法进行示例

  • 【注意】后面代码公用此段,不再重复书写:
    import numpy as npfrom numpy.random import default_rngrng = default_rng()

随机选择器

a = np.array([1,5,7,9,8,7,3,1,4,6])rng.choice(a)

随机洗牌

array = np.arange(10)rng.shuffle(array)array

Out[R]:array([9, 8, 0, 3, 2, 1, 6, 7, 4, 5])

  • 也可以不是数组而仅仅是一般的Python序列:
    sequence = ["你","的","头","发","还","好","吗"]rng.shuffle(sequence)sequence

    Out[R]:[‘你’, ‘发’, ‘的’, ‘好’, ‘头’, ‘还’, ‘吗’]

从二项分布(即伯努利分布)中抽取样本

n, p = 10, .5  # 试验次数,每次试验的概率rng.binomial(n, p, 100) # 结果抛硬币10次,测试100次。

Out[R]:

array([5, 4, 5, 4, 4, 4, 4, 2, 6, 6, 6, 5, 7, 6, 7, 4, 6, 7, 7, 6, 9, 7,6, 6, 5, 6, 6, 5, 5, 5, 2, 3, 6, 3, 6, 5, 7, 4, 5, 5, 5, 8, 2, 6,6, 3, 4, 4, 9, 6, 7, 3, 8, 8, 7, 4, 4, 2, 5, 4, 3, 5, 5, 3, 6, 5,2, 4, 7, 3, 6, 5, 7, 6, 5, 6, 5, 5, 3, 5, 5, 1, 3, 5, 3, 6, 4, 5,6, 6, 4, 5, 5, 6, 2, 3, 7, 6, 5, 6], dtype=int64)

从高斯分布(正态分布)中抽取样本

mu, sigma = 0, 0.1  # 均值和标准差rng.normal(mu, sigma, 10)  # 随机挑选10个样本

Out[R]:

array([-0.08977574, -0.04510079, -0.1347215 ,  0.055061  , -0.07062946, 0.06331096,  0.11478359,  0.07054353,  0.29988904, -0.07591274])

从泊松分布中抽取样本

rng.poisson(5, 30)

Out[R]:

array([10,  5,  6,  0,  5,  8,  5,  8,  6,  3, 10,  6,  8,  4,  9,  4,  4, 4,  1,  4,  6,  4, 11,  4,  4,  2,  2,  4,  4,  4], dtype=int64)

从F分布中抽取样本

dfnum = 1.  # 组间的自由度dfden = 48. # 组内的自由度rng.f(dfnum, dfden, 100)

Out[R]:

array([1.93197989e-01, 1.24205423e+00, 4.00232880e-01, 4.85092358e-02,3.32931687e+00, 2.05889475e+00, 4.61348458e-01, 3.00949845e+00,3.90822633e+00, 2.23728556e+00, 9.34600481e-03, 5.15363963e-01,1.43485077e-01, 4.41028093e-01, 1.32146240e-01, 1.11471393e+00,1.03337187e+00, 2.30848196e-01, 4.07377166e-01, 2.05472096e-01,1.63826036e+00, 4.46181253e-01, 1.86498211e+00, 2.66618563e+00,1.08118560e+00, 4.48247556e-01, 9.43349767e-02, 1.76855083e+00,2.95544929e-03, 6.73061275e-01, 2.15134324e-01, 1.39935387e-01,1.01244134e+00, 5.53635434e-03, 8.99606895e-01, 2.66732309e-01,1.18154518e+00, 1.12718292e+00, 2.10690001e-01, 5.68640747e-01,9.89775122e-03, 4.65351259e-02, 2.16960435e+00, 1.22627946e+00,1.15816353e+00, 2.03245748e+00, 8.48710506e+00, 2.08246000e+00,1.93013549e+00, 1.09309756e-01, 8.01513824e-01, 1.65082925e-01,3.19968754e-01, 1.31481564e-02, 2.47961229e-01, 4.73627037e-03,2.81335155e-02, 9.91148164e-02, 4.86448293e-01, 4.40648125e+00,9.06591744e+00, 1.27533676e+00, 4.73046307e-02, 1.87428590e-01,4.35645568e-01, 3.03655416e-01, 1.33503179e+00, 4.69885038e-01,4.82723958e-01, 8.95961791e-01, 1.65418312e+00, 6.51444154e-04,1.15344115e-01, 1.39118341e-01, 1.51368685e-01, 5.23925653e+00,2.70342516e+00, 1.36388925e-01, 7.42148964e+00, 4.99751983e-02,7.12414489e-01, 1.04688667e+00, 4.33790508e-04, 7.12767650e-02,3.57984128e-01, 3.16070339e-02, 3.07002039e-01, 6.77355120e-03,1.09919251e-03, 4.41067472e-02, 1.82543358e+00, 2.52812298e-01,2.00608200e+00, 3.32439576e+00, 3.43718583e-01, 1.57335325e-01,6.16422855e-01, 4.06029886e-01, 1.51402319e-02, 1.17152107e+00])

从卡方分布中抽取样本

rng.chisquare(2,10)

Out[R]:

array([6.46906743, 1.74516249, 1.61624403, 1.95992536, 1.14905782,4.13322769, 0.41470648, 0.8892556 , 1.22484048, 3.3340893 ])

附录:常用数学分布介绍


在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

如果喜欢,记得点赞哦!

好看字体下载