当前位置 博文首页 > jcLee95的博客:Python基础专题 - 超级详细的 Random(随机)原

    jcLee95的博客:Python基础专题 - 超级详细的 Random(随机)原

    作者:[db:作者] 时间:2021-09-18 15:55

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

    李俊才
    CSDN博客: https://mp.csdn.net/console/home?spm=1011.2124.3001.4503

    文人博文中如有错误请Email我更正。相互学习不甚感激。
    291148484@163.com


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

    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 2n?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 < = b a <= b a<=b 时, a < = N < = b a <= N <= b a<=N<=b
    • b < a b < a b<a 时, b < = N < = a b <= N <= a b<=N<=a.
    • 取决于等式 a + ( b ? a ) ? r a n d o m ( ) 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)按 β \beta β分布生成[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.3.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分布中抽取样本