文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【Python】蒙特卡洛模拟 | PRNG 伪随机数发生器 | 马特赛特旋转算法 | LCG 线性同余算法 | Python Random 模块

2023-08-31 19:52

关注

     猛戳订阅! 👉 《一起玩蛇》🐍

💭 写在前面:本篇博客将介绍经典的伪随机数生成算法,我们将  重点讲解 LCG(线性同余发生器) 算法与马特赛特旋转算法,在此基础上顺带介绍 Python 的 random 模块。 本篇博客还带有练习,无聊到喷水的练习,咳咳…… 学完前面的内容你就会了解到 Python 的 Random 模块的随机数生成的实现,是基于马特赛特旋转算法的,比如 random_uniform 函数。而本篇博客提供的练习会让你实现一个基于 LCG 算法的random_uniform,个人认为还是比较有意思的。练习题的环境为 Google Colaboratory(K80 GPU)Jupyter Notebook:https://colab.research.google.com/ 


Ⅰ. 蒙特卡洛模拟(Monte Carlo simulation)

0x00 引入:概念说明

💡 引入: 非确定性模型(deterministic model)概率模型(stochastic model)中,用分析法难以下手,这时我们就可以利用 "蒙特卡洛模拟" (或称蒙特卡洛仿真) 去解决问题。

📚 概念:"蒙特卡洛模拟" 是一种反复使用随机采样技术进行模拟,并从总体概率分布计算出所需数值结果的一种计算算法。

命名的由来

蒙特卡洛模拟是在二战期间,在原子弹研制的项目中用来模拟裂变物质的中子随机扩散现象,由美国数学家冯·诺伊曼和乌拉姆研发的模拟算法。蒙特卡洛是在欧洲摩纳哥的一个城市,这个城市在当时是非常著名的一个赌城。因为赌博的本质是算概率,而蒙特卡洛模拟正是以概率为基础的一种方法,所以用赌城的名字为这种方法命名。

 (摩纳哥公国 - 蒙特卡洛)

蒙特卡洛模拟可作用于数值积分、概率分布计算、基于概率的优化等领域。例如:

Simulation:为了模拟抛硬币,在 [0,1] 范围内取随机值:

Monte Carlo method:倒一盒硬币,数 head 和 tail 的数字,由此获得 head 的概率。

Monte Carlo simulation[0,1] 的范围内,反复提取随机值,由此获得 head 的概率。蒙特卡罗算法表示采样越多,越近似最优解。

🔍 百度百科:百科举了个容易理解的例子

举个例子,假如筐里有100个苹果,让我每次闭眼拿1个,挑出最大的。

于是我随机拿1个,再随机拿1个跟它比,留下大的,再随机拿1个……我每拿一次,留下的苹果都至少不比上次的小。拿的次数越多,挑出的苹果就越大,但我除非拿100次(即把筐中的苹果都拿了一遍),否则无法肯定是否挑出了最大的那一个。

这个挑苹果的算法,就属于蒙特卡罗算法。

告诉我们样本容量足够大,则最接近所要求解的概率。

0x01 蒙特卡洛仿真常规模式

🔺 统共分为如下四步:

💭 举个例子:两个简单的蒙特卡罗模拟的实例

掷两次骰子点数之和为 8 的概率:

求圆周率:

(撷取自维基百科)

Ⅱ. 伪随机数发生器(PRNG)

0x00 引入:随机数生成问题

为了给蒙特卡洛模拟生成随机数,我们需要置随机数 "种子"。

这里我们选择采用伪随机数发生器 (Pseudo Random Number Genarator) ,简称 PRNG

伪随机数并非完全随机

伪随机数发生器用于在系统需要随机数的时候,通过一系列种子值计算出来的伪随机数。因为生成一个真正意义上的“随机数”对于计算机来说是不可能的,伪随机数也只是尽可能地接近其应具有的随机性,但是因为有“种子值”,所以伪随机数在一定程度上是可控可预测的。

本章我们将介绍以下几种经典的伪随机数生成算法:(我们将重点讲解前两种算法)

0x01 线性同余发生器(LCG)

📚 线性同余发生器(Linear Congruential Generator),简称 LCG

它能产生具有不连续计算的伪随机序列的分段线性方程,生成器由循环关系定义如下:

X_{n+1}=(aX_n+C)\,\, \textrm{mod}\, \, m

LCG 由下列参数唯一决定:

0<m ,0<a0\leq c  ,0\leq X_0  < m 

📜 为保持 LCG  的最大周期 m 的充分必要条件如下:

其中参数 a, C, m 比较敏感,它们直接影响了伪随机数产生的质量,所以参数的取值非常重要!

LCG算法优点:

  • LCG 能以较小的内存去生成随机数,因此适用于嵌入式开发环境。
  • 有可观的计算速度,且在不需要考虑相关性的情况下也能使用 LCG,实现起来非常简单。

LCG算法缺点:

  • 如果知道了 LCG 的参数和最后生成的随机数,就可以预测其后生成的所有因数,因此从密码学角度来看,我们不能认为它是安全的随机数生成器。

0x02 马特赛特旋转算法(MersenneTwister)

"马特赛特旋转算法是当今生成随机数质量最好的算法"

LCG 算法实现的伪随机数很不错,但是周期不够长,很容易被 "不好的人" 推算出随机数种子。两个小日子过得不错的学者 —— 松本和西村,在 1997 年又研究提出了新的伪随机数算法:

马特赛特旋转算法(MersenneTwister),简称 MT。是基于有限二进制字段上的矩阵线性递归。马特赛特旋转算法可迅速产生高质量的伪随机数,修正了古老随机数产生算法的很多缺陷。该算法中用到了梅森素数,所以又称为梅森旋转算算法。(但翻译大部分选用了 "马特赛特" 作为翻译,所以这里我们还是称之为马特赛特旋转算法)

补充:梅森素数

梅森素数的意思是形如的素数。利用梅森素数的性质可以设计出周期长度为梅森素数长度的随机数周期。比如目前Python、C++11等语言当中用的随机数计算包都是用的这种算法。目前常用的版本周期是,这是一个巨大的天文数字。

即使你从来没有听过这个算法,但你一定用过 Python 中的 random 模块:

import random

Python 中的 random 模块就是采用了马特赛特旋转算法实现随机数的 ,不仅 Python 用,PHP、Perl 等流行编程语言内置的 PRNG 也都是采用该算法实现,可见马特赛特旋转算法真是个备受大佬青睐的算法,它的地位可见一斑!

MT 算法的三个阶段:

MT算法优点: 

  • 马特赛特旋转能更快的生成高质量的伪随机数,随机数的反复周期取决于马特赛特的因数。
  • 在 MT19937 上跑,周期最长可达到 2^{19937}-1 ,具有 623 维均匀分布的性质,真的很大!(使用 624 个整型,623*32=19936!)它的序列关联比较小,能通过多种随机性测试,甚至包括 Hdamard Test 量子计算!
  • 只需位运算就能实现的算法,速度异常迅速。

MT算法缺点: 

  • 该算法因其必须分配能容纳 624 个数字的空间,对容量有限的嵌入式环境来说是一大缺陷。
  • 从密码学的角度来看,马特赛特旋转算法仍然是不安全的,当你知道随机数的特性时,仅利用有限的随机数(624个)就可以知道当前生成器所处状态并预测出即将出现的随机数。

0x03 ·诺伊曼平方取中法(Mid-square Method)

平方取中法(Mid-square Method)是最早出现的伪随机数发生器,最早由冯诺依曼提出。

平方取中法的意思如同其名:

并将此数规范化(化为小于1的2s位数值),即第一个 (0,1) 上的随机数,以此类推,重复以上过程就能得到一系列伪随机数。其递推公式如下:

x_{i+1} = \left [ \frac{x^2_i}{10^s} \right ] \, \textrm{mod} \,\, 10^{2s}

产生伪随机数数列:

u_{i+1} = \frac{x_{i+1}}{10^{2s}}

✅ 平方取中法的优点:

  • 易于实现,内存占用少,计算简单。

❌ 平方取中法的缺点:

  • 存对小数目偏倚现象,均匀性差,对初始数据依赖大,很难说明取什么样的种子值可以保证有足够长的周期。
  • 很容易出现重复元素的段循环,而且容易退化为一常数,甚至退化为零,如果某个元素退化为0,那么后面所有元素都将会是0。

📜 平方取中法的变形和推广:

若要产生具有10进制 2s 位的伪随机数序列,任意选取两个初始随机数 x_0,x_1,递推公式:

x_{i+2} = \left [ \frac{x_{i+1}x_i}{10^s} \right ] \, \textrm{mod} \,\, 10^{2s}

产生伪随机数数列:

r_i = \frac{x_i}{10^{2s}}

递推公式:

x_{i+1} = \left [ \frac{k\times x_i}{10^s} \right ] \, \textrm{mod} \,\, 10^{2s}

产生伪随机数数列:

u_{i+1}=\frac{x_{i+1}}{10_{2s}}

0x04 斐波那契法(Fibonacci Method)

遗憾的是,该方法并不是一个好的伪随机数发生器,该方法基于 Fibonacci 数列,其递推公式为:

\left\{\begin{matrix} x_{i+2} = (x_{i+1}+x_i) & \\ r_n = \frac{x_i}{m} & \end{matrix}\right.     (n=1,2,...)

该方法有  x_0, x_1 两个初始种子,最大的优点就是计算周期快,且达到满周期。此发生器没有乘法运算,产生速度很快。缺点是序列中的数可能会重复出现,独立性较差,且有让人难以容忍的不居中现象。

📜 斐波那契法的变形和推广(了解):

时滞斐波那契生成器,简称 LFG。时滞斐波那契生成器的理论相当复杂,理论也不够充分到能指导如何选择 j 与 k 。生成器的初始化也非常敏感,其递推公式为:

其中,新项由两个老项计算生成。m 通常是 2 的幂(M=2^M), 经常是 2^{32} 或 2^{64}。其中 ★ 运算符表示一般的二元运算符,这可以是加法、减法、乘法或者位运算异或。

0x05 移位法(Shift Method)

计算机善于进行移位等逻辑运算,移位法就是利用计算机的这个特点去实现的。移位法是关于平方取中法的另一种改进形式,它是移位运算于指令相加运算相结合的迭代过程。方法如下:

取一个初始值 x_0,使它左右移位,分别为 x_{01},x_{02} ,然后指令相加得到 x_1,再对 x_1 进行上述过程,如此重复下去,就能产生随机数序列了。递推公式如下(适用于32位机器):

x_{i+1} = 2^7x_n+2^{-7}x_n(\, \, \textrm{mod}\, \, 2^{32})

产生伪随机数数列:

r_n = x_n / 2^{32}

移位法运算速度快,但是对初始值的依赖性很大,一般初始值不能取的太小,初始值选的不好会使伪随机数序列长度较短。同时独立性交叉,随后随机数序列周期 T 与计算机的字长有关。

Ⅲ. Python 的随机数生成函数(Random 模块)

0x00 引入:Random 模块介绍

我们刚才讲解马特塞特旋转算法时提到了,Python 中的 Random 模块就是采用该算法实现的。

其生成 53 位精度的 float,周期为  2 ^{19337}-1

提供了可提取分布的函数:Uniform,Normal,lognormal,negative exponential,gamma, beta 

 📚 使用前需引入 random 模块:

import random

0x01 random - 生成 0.0 ~ 1.0 间的随机数

random.random()   # 在 0.1 与 1 之间的实数中生成随机数

📚 作用:随机生成一个 [\, 0,1) 范围的实数。

💬 代码演示:生成实数 0.0-1.0 之间的随机数:

print(random.random())

🚩 运行结果如下:

0x02  uniform - 生成指定范围的随机数

random.uniform(a, b)   # 在 a 和 b 之间生成随机数

📚 作用:随机生成一个指定范围的实数。

💬 代码演示:生成一个 100~300 之间的数

print(random.uniform(10, 30))

🚩 运行结果如下:

0x03  randint - 生成指定范围的整数随机数

random.randint(a, b)      # 在 a 和 b 之间生成整数随机数

📚 作用:随机生成一个指定范围的整数。

💬 代码演示:生成一个100~300之间的整数

print(random.randint(100, 300))

🚩 运行结果如下:

0x04  choice - 在样本集中随机选择

random.choice(sample_set)    # 在样本集 sample_set 中随机选择一个样本

📚 作用:在样本集中随机选择一个样本

💬 代码演示:从样本集 L 中随机选择一个样本

L = [1, -3, 5, -2, 6, 8, -3, 0]  print(random.choice(L))

🚩 运行结果如下:

0x05 sample - 在样本集中随机选择多次

random.sample(sample_set, n)    # 在样本集 sample_set 中随机选择 n 个样本

📚 作用:执行 n 次 "在样本集中随机选择一个样本" 。

💬 代码演示:从样本集 L 中随机选择多次

L2 = [1, 'CSDN', 5, 'bilibili', 6, -3, 0]print(random.sample(L2, 3))   # 在样本集L2中选择3次print(random.sample(L2, 6))   # 在样本集L2中选择6次print(random.sample(L2, 3))   # 在样本集L2中选择3次

🚩 运行结果如下:

Ⅳ. 练习(Assignment)

练习1:绘制对数直方图(利用LCG实现)📈

import randomimport matplotlib.pyplot

我们再回过头来看一下 LCG 的循环关系定义:

X_{n+1}=(aX_n+C)\,\, \textrm{mod}\, \, m

我们刚才提到过,LCG 算法的 a, C, m 参数比较敏感,它们直接影响了伪随机数产生的质量,所以这里的取值是很重要的!这里介绍一种业内常用的参数取值:

a=25214903917,\,\, \, \, C=11, \, \, \, \, m = 2^{48}

a = 25214903917C = 11m = 2**48

这些值都是经过专家精密计算得到的,并非是信手拈来,随随便便取的!

参考代码:(仅供参考)

import matplotlib.pyplot as pltfrom tqdm import tqdm # optionalimport random# my LCGdef LCG(x, a, c, m):  while True:      x = (a * x + c) % m      yield xnum_iterations = 100    # 次数random_integers = []def random_uniform_sample(n, space, seed=0):  # Random seed  a = 214013  C = 2531011  m = 2**32    get = LCG(seed, a, C, m)  lower, upper = space[0], space[1]  # create the random value  for i in range(n):    value = (upper - lower) * (next(get) / (m - 1)) + lower    random_integers.append(value)     return random_integers     # return the resultX = random_uniform_sample(num_iterations, [0, 99])# print(X)fig = plt.figure()plt.hist(X)plt.title(f"Check Uniform Distribution of {num_iterations} iterations")plt.xlabel("Numbers")plt.ylabel("N of each number")plt.xlim([0, 99])plt.show()

🚩 运行结果:100次

🚩 运行结果:1000次

 

🚩 运行结果:10000次 

🚩 运行结果:100000次

 

练习2:检验 LCG 和 马特赛特旋转 中谁生成的分布更接近 🔍

利用  LCG(刚才练习1中自己实现的) 和 MT算法(Python random 模块)  查看分布, 查看哪种随机数生成器更符合均匀分布。

参考代码:

# 模拟掷骰子def roll_dice():    roll = random.randint(1, 6)   # 按题目要求,使用randint在1~6范围取值  return roll                   # 将结果返回# 记录区dice_tries1 = []dice_tries2 = []num_iterations = 100    # 次数hits = 0   # 命中数# 投掷次数for i in range(num_iterations):    dice_tries1.append(roll_dice())  # 投掷100次 存入数组    dice_tries2.append(roll_dice())  # 投掷100次 存入数组print("="*100)  print(colored("* 红色表示两个骰子点数之和为8*", 'red'))   # 打印标题change = 0for j in range(num_iterations):  if (change == 5):      print()      change = 0  if (dice_tries1[j] + dice_tries2[j] == 8):    print(colored("try %2s : %s %s" % (j, dice_tries1[j], dice_tries2[j]), "blue"), end = " ")    change += 1    hits += 1  else:    print("try %2s : %s %s" % (j, dice_tries1[j], dice_tries2[j]), end = " ")    change += 1print(colored("\n实际值 : 0.138889", "red"))print(colored(f"计算值 : {round(hits / num_iterations,6)}", "red"))print(colored(f"误差率 : {abs(hits / num_iterations - 5/36) / (5/36) * 100} %", "red"))print("="*100)

练习3:骰子游戏胜利预测 🎲

玩家在进行骰子游戏,初始拥有费用为1000美元。
掷两个骰子,如果骰子的数字相同,得到 4 美元,骰子的数字如果不同,将失去 1美元
使用蒙特卡罗算法,根据游戏执行次数计算玩家的平均速率,确认平均余额。

 参考代码:

import matplotlib.pyplot as pltimport randomdef roll_dice():  A = random.randint(1, 6)  B = random.randint(1, 6)  return A == B# Inputsnum_simulations = 10000max_num_rolls = 1000bet = 1win_probability = []end_balance = []plt.figure(figsize=(12,6))for i in range(num_simulations):  # 投一万次骰子  arr = [1000]  rolls = [0]  wins = 0  while (rolls[-1] < max_num_rolls):    if (roll_dice() == True):      arr.append(arr[-1] + 4 * bet)      wins += 1    else:      arr.append(arr[-1] - bet)    rolls.append(rolls[-1] + 1)  win_probability.append(wins / rolls[-1])  end_balance.append(arr[-1])  plt.plot(rolls, arr)plt.title("Monte Carlo Dice Game [" + str(num_simulations) + " players simulations]")plt.xlabel("Roll Number")plt.ylabel("Balance [$]")plt.xlim([0, max_num_rolls])

…… 待更新

📌 [ 笔者 ]   王亦优📃 [ 更新 ]   2022.11.11❌ [ 勘误 ]   📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料:

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

来源地址:https://blog.csdn.net/weixin_50502862/article/details/126732514

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯