Random随机的详解

1.什么是随机

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

对于一个取值有限的事件

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

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

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

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

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

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

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

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

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

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

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

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

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

2.2 实用方法

(1)生成[0,1)上的随机浮点数
import random
random.random()
(2)生成[a,b][a,b)上一个随机浮点数(区间可以不是整数)
  • 当 a<=ba<=b 时, a<=N<=ba<=N<=b ;
  • 当 b<ab<a 时, b<=N<=ab<=N<=a.
  • 取决于等式 a+(b−a)∗random()a+(b−a)∗random() 中的浮点舍入,终点 b 可以包括或不包括在该范围内。
import random
random.uniform(2, 3)
(3)生成[a,b]上的随机整数
random.randint(a, b)
import random
random.randint(0, 9) # 生成[0,9]上的随机整数
(4)在区间[start, stop]上以间隔step生成随机整数
random.randrange(start, stop[, step])
import random
random.randrange(1, 9[, 2])
(5)按高斯分布(正态分布)生成随机数
random.gauss(mu, sigma)

mu 是平均值,sigma 是标准差

import random
random.gauss(mu, sigma)
(6)按ββ分布生成[0,1]上的随机数
random.betavariate(alpha, beta)

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

import random
random.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 random
    random.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 random
    x = ["我","爱","学","习","Python","编","程"]
    random.shuffle(x)
    x
    
    Out[R]:[‘程’, ‘编’, ‘我’, ‘习’, ‘Python’, ‘学’, ‘爱’]
(9)无重复随机抽样
random.sample(population, k, *, counts=None)

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

  • population: 集合或者序列,为样本总体的容器;
  • k: int,抽取的子序列的长度(元素个数);
  • counts:list,元素为重复样本的权重,即该样本的出现次数。counts中的元素与population的元素一一对应。
    【代码1】
    import random
    population = ['王', '者','荣','耀']
    random.sample(population,counts=[4, 3, 2, 1], k=3)
    
    和下面的代码时等效的:
    【代码2】
    import random
    population = ['王', '王', '王', '王', '者', '者', '者', '荣', '荣', '耀']
    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 MT19937
from numpy.random import RandomState

rs1 = RandomState(12345)          # 获取seed=12345的RandomState()实例 rs
mt19937 = MT19937()              # 旧版MT19937 BitGenerator
mt19937.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_rng
    rng = default_rng(12345)     # 构造一个随机数生成器类rng
    rng.random()                 # 从[0.0,1.0)上随机抽取浮点数样本
    

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

    from numpy.random import default_rng
    rng = default_rng()          # 构造一个随机数生成器类rng
    rng.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_rng
rng = default_rng()          # 构造一个随机数生成器类rng

rng.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 np
    from numpy.random import default_rng
    rng = 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 ])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值