草庐IT

强化学习-学习笔记6 | 蒙特卡洛算法

Clivia Du 2023-03-28 原文

Monte Carlo Algorithms. 蒙特卡洛算法是一大类随机算法,又称为随机抽样或统计试验方法,通过随机样本估计真实值。

下面用几个实例来理解蒙特卡洛算法。

6. 蒙特卡洛算法

6.1 计算 \(\pi\)

a. 原理

如果我们不知道 \(\pi\) 的值,我们能不能用随机数 来近似 \(\pi\) 呢?

假设我们用一个随机数生成器,每次生成两个范围在 \([-1,+1]\) 的随机数,一个作为 x,另一个作为 y,即生成了一个二维随机点:

假如生成 1亿 个随机样本,会有多少落在 半径=1 的圆内?这个概率就是圆的面积除以正方形的面积。

即:\(P = \frac{\pi{r^2}}{2^2}=\frac{\pi}{4}\)

假设从正方形区域中随机抽样 n 个点,那么落在圆内点个数的期望为:\(P_n=\frac{\pi{n}}{4}\)

下面我们去求落在圆内的点的个数,只需满足\(x^2+y^2\leqslant1\) 即为圆内。

如果生成的随机点的个数足够多,落在圆内的实际观测值 \(m\approx \frac{\pi{n}}{4}\)

我们已知了m 与 n,所以\(\pi \approx \frac{4m}{n}\).

事实上,根据概率论大数定律:

\(\frac{4m}{n}\rightarrow \pi\),as n → ∞

这保证了蒙特卡洛的正确性。

伯恩斯坦概率不等式还能确定 观测值和真实值之间误差的上界。

\(|\frac{4m}{n}-\pi|=O(\frac{1}{\sqrt{n}})\)

说明 这个误差与样本n的根号成反比。

b. 代码

下面放一个Python代码

#coding=utf-8
#蒙特卡罗方法计算 pi
import random,math,time
start_time = time.perf_counter()
s = 1000*1000
hits = 0
for i in range(s):
    x = random.random()
    y = random.random()
    z = math.sqrt(x**2+y**2)
    if z<=1:
        hits +=1

PI = 4*(hits/s)
print(PI)
end_time = time.perf_counter()
print("{:.2f}S".format(end_time-start_time))

# 输出
3.141212
0.89S

另外可还有一个可视化程序,可以模拟点落在方块区域圆内外:http://www.anders.wang/monte-carlo/

6.2 Buffon's Needle Problem

a. 原理

布封投针,也是用蒙特卡洛来近似 \(\pi\) 值。这是一个可以动手做的实验。

用一张纸,画若干等距平行线(距离为 d),撒上一把等长的针(长度为l),通过与平行线相交的针的数量,就可以推算出 \(\pi\)

通过微积分可以算出:相交的概率为:\(P = \frac{2l}{\pi{d}}\)

微积分推导过程:

课程里并没有讲解推导,这里我参考的是一下两篇博客的推导过程:

  1. https://zhuanlan.zhihu.com/p/479953215
  2. https://cosx.org/2009/11/a-brief-talk-on-buffon-throwing-needle-problems/

主流做法是通过对针的斜率进行积分:

这里我后续补充。

6.1 类似,我们随机扔 n 根针,这样相交个数的期望\(Pn = \frac{2ln}{\pi{d}}\) 。我们可以观察到(如果是电脑模拟即为通过公式判断出)有 m 跟针实际与线相交,如果n足够大,则 \(m\approx \frac{2ln}{\pi{d}}\)

\(\pi\) 公式即为: \(\pi\approx \frac{2ln}{md}\)

b. 代码

有了公式 \(\pi\approx \frac{2ln}{md}\),代码实现其实很简单了,仅列出一种实现思路:

import numpy as np

def buffon(a,l,n):
  xl = np.pi*np.random.random(n)
  yl = 0.5*a*np.random.random(n)
  m = 0
  for x,y in zip(xl,yl):
    if y < 0.5*l*np.sin(x):
      m+=1
  result = 2*l/a*n/m
  print(f'pi的估计值是{result}')
  
buffon(2,1,1000000)

# 输出为:
pi的估计值是3.153977165205324

当然,也有可视化的代码:

import matplotlib.pyplot as plt
import random
import math
import numpy as np

NUMBER_OF_NEEDLES = 5000


class DefineNeedle:
    def __init__(self, x=None, y=None, theta=None, length=0.5):
        if x is None:
            x = random.uniform(0, 1)
        if y is None:
            y = random.uniform(0, 1)
        if theta is None:
            theta = random.uniform(0, math.pi)

        self.needle_coordinates = np.array([x, y])
        self.complex_representation = np.array(
            [length/2 * math.cos(theta), length/2*math.sin(theta)])
        self.end_points = np.array([np.add(self.needle_coordinates, -1*np.array(
            self.complex_representation)), np.add(self.needle_coordinates, self.complex_representation)])

    def intersects_with_y(self, y):
        return self.end_points[0][1] < y and self.end_points[1][1] > y


class BuffonSimulation:
    def __init__(self):
        self.floor = []
        self.boards = 2
        self.list_of_needle_objects = []
        self.number_of_intersections = 0

        fig = plt.figure(figsize=(10, 10))
        self.buffon = plt.subplot()
        self.results_text = fig.text(
            0, 0, self.estimate_pi(), size=15)
        self.buffon.set_xlim(-0.1, 1.1)
        self.buffon.set_ylim(-0.1, 1.1)

    def plot_floor_boards(self):
        for j in range(self.boards):
            self.floor.append(0+j)
            self.buffon.hlines(
                y=self.floor[j], xmin=0, xmax=1, color='black', linestyle='--', linewidth=2.0)

    def toss_needles(self):
        needle_object = DefineNeedle()
        self.list_of_needle_objects.append(needle_object)
        x_coordinates = [needle_object.end_points[0]
                         [0], needle_object.end_points[1][0]]
        y_coordinates = [needle_object.end_points[0]
                         [1], needle_object.end_points[1][1]]

        for board in range(self.boards):
            if needle_object.intersects_with_y(self.floor[board]):
                self.number_of_intersections += 1
                self.buffon.plot(x_coordinates, y_coordinates,
                                 color='green', linewidth=1)
                return
        self.buffon.plot(x_coordinates, y_coordinates,
                         color='red', linewidth=1)

    def estimate_pi(self, needles_tossed=0):
        if self.number_of_intersections == 0:
            estimated_pi = 0
        else:
            estimated_pi = (needles_tossed) / \
                (1 * self.number_of_intersections)
        error = abs(((math.pi - estimated_pi)/math.pi)*100)
        return (" Intersections:" + str(self.number_of_intersections) +
                "\n Total Needles: " + str(needles_tossed) +
                "\n Approximation of Pi: " + str(estimated_pi) +
                "\n Error: " + str(error) + "%")

    def plot_needles(self):
        for needle in range(NUMBER_OF_NEEDLES):
            self.toss_needles()
            self.results_text.set_text(self.estimate_pi(needle+1))
            if (needle+1) % 200 == 0:
                plt.pause(1/200)
        plt.title("Estimation of Pi using Probability")

    def plot(self):
        self.plot_floor_boards()
        self.plot_needles()
        plt.show()


simulation = BuffonSimulation()
simulation.plot()

效果如图:

以上内容参考:

  1. 课程视频
  2. https://www.section.io/engineering-education/buffon-needle/
  3. https://github.com/topics/buffon-needle
  4. https://github.com/GunnarDahm/buffon_monte_carlo_sim/blob/master/buffon_monte_carlo.py
  5. https://blog.csdn.net/qq_45757739/article/details/108387567
  6. https://blog.csdn.net/TSzero/article/details/111604960

理解思想即可,如果后续有机会,可能单出一篇介绍介绍,也有可能将这部分丰富一下。

6.3 估计阴影部分的面积

我们稍微推广一下,试着用蒙特卡洛解决一个阴影部分面积的求解。比如下图:

我们如何使用蒙特卡洛的思路解决这个阴影部分面积的求解呢?

类似于上面的思路,在正方形内做随机均匀抽样,得到很多点,怎么确定点在阴影部分呢?

可知,阴影部分的点满足:

\[\begin{cases} x^2+y^2>4\\ (x-1)^2+(y-1)^2\leq1\end{cases} \]

  • 易知,正方形面积 \(A_1=4\);设阴影部分面积为 \(A_2\)
  • 随机抽样的点落在阴影部分的概率为:\(P=\frac{A_2}{A_1}=\frac{A_2}{4}\)
  • 从正方形区域抽样 n 个点,n尽可能大,则来自阴影部分点的期望为:\(nP=\frac{nA_2}{4}\)
  • 如果实际上满足上述条件的点 有 m 个,则令 \(m\approx nP\)
  • 得到:\(A_2\approx \frac{4m}{n}\)

代码与 6.1 相近。

6.4 求不规则积分

近似求积分是蒙特卡洛在工程和科学问题中最重要的应用。很多积分是没有解析的积分(即可以计算出来的积分),特别是多元积分,而只能用数值方法求一个近似值,蒙特卡洛就是最常用的数值方法。

一元函数步骤如下:

我们要计算一个一元函数的定积分 \(I = \int_a^bf(x)dx\);

  • 从区间 \([a,b]\) 上随机均匀抽样 \(x_1,x_2,...,x_n\);

  • 计算 \(Q_n = (b-a)\frac{1}{n}\sum_{i=1}^nf(x_i)\),即均值乘以区间长度;

    这里均值乘以区间长度是 实际值,而 I 是期望值

  • \(Q_n\) 近似 \(I\)

大数定律保证了 当\(n\rightarrow\infty,Q_n\rightarrow I\)

多元函数步骤如下:

我们要计算一个多元函数的定积分 \(I = \int_a^bf(\vec{x})d\vec{x}\),积分区域为 \(\Omega\);

  • 从区间 \(\Omega\) 上随机均匀抽样 \(\vec{x_1},\vec{x_2},...,\vec{x_n}\);

  • 计算 \(\Omega\) 的体积V(高于三维同样):\(V=\int_\Omega{d\vec{x}}\)

    hh值得注意的是,这一步仍要计算定积分,如果形状过于复杂,无法求得 V,那么无法继续进行,则无法使用蒙特卡洛算法。所以只能适用于比较规则的区域,比如圆形,长方体等。

  • 计算 \(Q_n =V \frac{1}{n}\sum_{i=1}^nf(\vec{x_i})\),即均值乘以区间长度;

    这里均值乘以区间长度是 实际值,而 I 是期望值

  • \(Q_n\) 近似 \(I\)

下面我们从积分的角度再来看看 蒙特卡洛近似求 pi

  • 定义一个二元函数 \(f(x,y)=\begin{cases} 1 \ \ if点在圆内\\ 0 \ \ if 点在圆外\end{cases}\);
  • 定义一个区间 \(\Omega=[-1,1]×[-1,1]\)
  • \(I =\pi {r^2}=\pi\)
  • 接下来用蒙特卡洛近似 I,得到关于 \(\pi\)的算式即可得到近似的\(\pi\);
    • 随机抽样 n 个点,记为\((x_1,y_1),...,(x_n,y_n)\)
    • 计算 区域面积 \(V = \int_\Omega{dxdy}=4\)
    • 计算 \(Q_n =V \frac{1}{n}\sum_{i=1}^nf(x_i,y_i)\)
    • 蒙特卡洛近似 Q 与 I 近似相等:\(\pi=Q_n=\int_\Omega{f(x,y)}{dxdy}\)

这是从蒙特卡洛积分的角度得到的pi,6.1 中则是从蒙特卡洛概率和期望的角度得到的。

6.5 用蒙特卡洛近似期望

这个方法对于统计学和机器学习很有用。

  • 定义 X 是 d 维的随机变量,函数 p(x) 是一个PDF,概率密度函数;
  • 函数 \(f(x)\) 的期望:\(\mathbb{E}_{x\sim{p}}[f(X)]=\int_{R^d}f(X)\cdotp(x)dx\)
  • 直接以上面的方式求期望可能并不容易,所以通常使用蒙特卡洛近似求期望:
    1. 随机抽样:根据概率密度函数 \(p(x)\) 进行随机抽样,记为\(X_1,X_2,...,X_n\)
    2. 计算 \(Q_n =\frac{1}{n}\sum_{i=1}^nf(x_i)\)
    3. 用 Q 近似 期望\(\mathbb{E}_{x\sim{p}}[f(X)]\)

6.6 总结 | 蒙特卡洛算法的思想

我的想法是尽量精简,即:

模拟---抽样---估值,通过模拟出来的大量样本集或者随机过程,以随机抽样的方式,去近似我们想要研究的实际问题对象。

补充蒙特卡洛相关:

  • 蒙特卡洛是摩洛哥的赌场;

  • 蒙特卡洛算法得到的结果通常是错误的,但很接近真实值,对于对精度要求不高的机器学习已经足够。

    随机梯度下降就是一种蒙特卡洛算法,用随机的梯度近似真实的梯度,不准确但是降低了计算量。

  • 蒙特卡洛是一类随机算法,除此以外还有很多随机算法,比如拉斯维加斯算法(结果总是正确的算法)

x. 参考教程

有关强化学习-学习笔记6 | 蒙特卡洛算法的更多相关文章

  1. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  2. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  3. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  4. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  5. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  6. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  7. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  8. 机器学习——时间序列ARIMA模型(四):自相关函数ACF和偏自相关函数PACF用于判断ARIMA模型中p、q参数取值 - 2

    文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk​=Var(yt​)Cov(yt​,yt−k​)​其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞

  9. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  10. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

随机推荐