蒙特卡洛(Monte Carlo)方法是由数学家冯·诺伊曼提出的,诞生于上世纪40年代美国的“曼哈顿计划”。蒙特卡洛是一个地名,位于赌城摩纳哥,象征概率。蒙特卡洛方法的原理是通过大量随机样本,去了解一个系统,进而得到所要计算的值。
1. 计算圆周率
用蒙特卡洛方法计算圆周率π的原理如下:一个边长为2r的正方形内部相切一个半径为r的圆,圆的面积是πr2,正方形的面积为4r2,二者面积之比是π/4,因为比值与r大小无关,所以可以假设半径 r的值为1。
在这个正方形内部,随机产生n个点,坐标为(x,y),当随机点较多时,可以认为这些点服从均匀分布的规律。计算每个点与中心点的距离是否大于圆的半径(x2+y2>r2),以此判断是否落在圆的内部。统计圆内的点数c,c与n的比值乘以4,就是π的值。理论上,n越大,计算的π值越准,但由于随机数不能保证完全均匀分布,所以蒙特卡洛法每次计算结果可能不同。
编程实现用蒙特卡洛方法计算π值,为了自动测评的需要,请先读入一个正整数sd作为随机数种子,并要求使用 x,y = random.uniform(-1,1) , random.uniform(-1,1) 语句来生成随机点的坐标值。
import random
def monte_carlo_pi(num):
"""接收正整数为参数,表示随机点的数量,利用蒙特卡洛方法计算圆周率
返回值为表示圆周率的浮点数"""
hits = 0 #计数器,落在圆内的点数,初值为0
for i in range(num): #
x,y = random.uniform(-1,1),random.uniform(-1,1)
if x**2 + y**2 < 1 :# 计算坐标(x,y)到原点的距离,小于半径则在圆内
hits += 1
pi =(hits / num) * 4
return pi
if __name__ == '__main__':
sd = int(input()) #读入随机数种子
random.seed(sd) #设置随机数种子
times = int(input()) # 输入正整数,表示产生点数量
print(monte_carlo_pi(times)) # 输出圆周率值,浮点数
2. Tutle演示计算圆周率
import random
import turtle
s,n = map(int,input().split())
random.seed(s)
hits = 0
turtle.tracer(1000) #刷新间隔
turtle.update() #刷新
turtle.pensize(4)
turtle.penup()
turtle.goto(-400, -400)
turtle.pendown()
for i in range(4):
turtle.forward(800)
turtle.left(90)
turtle.forward(400)
turtle.pencolor('green')
turtle.circle(400)
for i in range(1, n+1):
x, y = random.random(), random.random()
signx,signy = random.choice('+-'),random.choice('+-')
pos = (x ** 2 + y ** 2)**0.5
if pos <= 1.0:
hits = hits + 1
turtle.pencolor('red')
else:
turtle.pencolor('blue')
x = float(signx+str(x))
y = float(signy+str(y))
turtle.penup()
turtle.goto(x * 400, y * 400)
turtle.pendown()
turtle.dot(5)
pi = 4 * (hits / n)
if i%1000 ==0:
turtle.write(pi, font=("Arial", 18, "normal"))
pi = 4 * (hits/n)
print("Pi值是{}。".format(pi))
turtle.hideturtle()
turtle.done()
3. matplotlib演示计算圆周率
# ------------ ------- -------- ----------- -----------
# @File : 4.4.4 计算圆周率.py
# @Contact : vasp@qq.com
# @Copyright : 2018-2025, Wuhan University of Technology
# @Modify Time: 2021/4/26 23:22
# @Author : 赵广辉
# @Version : 1.0
# @License : 仅限用于Python程序设计基础实践教程(赵广辉,高等教育出版社)配套实验
# ------------ ------- -------- ----------- -----------
# 本题中增加了用Matplotlib和 turtle演示蒙特卡洛的函数,可以考虑给学生运行查看演示
# 增加了'梅钦法'和'拉马努金法'计算圆周率,可以考虑给学生
# 函数文档中增加了测试,在Pycharm中运行时需要点if __name__ == '__main__':旁边的绿三角符号,再运行当前文件名
# 也可以先删除文档注释中的测试用例,给学生的模板中我没有加,如果有老师需要,可以加上测试部分。
import math
import random
import matplotlib.pyplot as plt
import numpy as np
import turtle
def monte_carlo_pi(num, s):
"""
接收一个表示随机次数的整数和一个整数做随机数种子,用蒙特卡洛法计算圆周率。
当产生点的坐标(x,y)落在圆内时执行in_circle_lst.append((x, y))将坐标加入到列表in_circle_lst,
产生点的坐标(x,y)落在圆外部时执行out_circle_lst.append((x, y))将坐标加入到列表out_circle_lst,
以浮点数形式返回圆周率值。
"""
random.seed(s)
hits = 0 # 落在圆内的计数器初值设为 0
for i in range(1, num + 1):
x, y = random.uniform(-1, 1), random.uniform(-1, 1) # 生成两个随机数模拟一个点的坐标
pos = (x ** 2 + y ** 2) ** 0.5 # 计算坐标(x,y)到原点的距离
if pos <= 1.0: # 如果距离小于等于1,点在圆内
hits = hits + 1
in_circle_lst.append((x, y))
else:
out_circle_lst.append((x, y))
pi = 4 * (hits / num)
monte_carlo_show(in_circle_lst, out_circle_lst) # 调用函数可演示蒙特卡洛估计圆周率值
monte_carlo_pi_turtle(num, s)
return pi
def monte_carlo_show(in_circle_lst, out_circle_lst):
"""
@参数 in_circle_lst:落在圆内的点的坐标列表
@参数 out_circle_lst:落在圆外的点的坐标列表
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.figure(figsize=(11, 11)) # 设置画布长宽
x_in_circle = [x[0] for x in in_circle_lst] # 落在圆内的点的列表x
y_in_circle = [x[1] for x in in_circle_lst] # 落在圆内的点的列表y
x_out_circle = [x[0] for x in out_circle_lst] # 落在圆外的点的列表x
y_out_circle = [x[1] for x in out_circle_lst] # 落在圆外的点的列表y
plt.scatter(x_out_circle, y_out_circle, s=10, facecolors='blue') # 绘制散点,落在圆外在点颜色用蓝色
plt.scatter(x_in_circle, y_in_circle, s=5, facecolors='red') # 绘制散点,落在圆内在点颜色用红色
plt.title('蒙特卡洛法计算圆周率演示') # 图的标题
plt.show() # 显示绘制结果
# 给出turtle模板,学生填monte_carlo代码,帮助学生理解算法,了解turtle绘图
def monte_carlo_pi_turtle(n, s):
"""用turtle绘图模拟蒙特卡洛n次结果"""
random.seed(s)
turtle.tracer(1000)
turtle.pensize(4)
turtle.penup()
turtle.goto(-300, -300)
turtle.pendown()
for i in range(4):
turtle.forward(600)
turtle.left(90)
turtle.forward(300)
turtle.pencolor('green')
turtle.circle(300)
hits = 0 # 落在圆内的计数器初值设为 0
for i in range(1, n + 1):
x, y = random.uniform(-1, 1), random.uniform(-1, 1) # 生成两个随机数模拟一个点的坐标
pos = (x ** 2 + y ** 2) ** 0.5 # 计算坐标(x,y)到原点的距离
if pos <= 1.0: # 如果距离小于等于1,点在圆内
hits = hits + 1
turtle.pencolor('red') # 落在圆内在点颜色用红色
else: # 如果距离大于1,点在圆外
turtle.pencolor('blue') # 落在圆内在点颜色用蓝色
turtle.penup()
turtle.goto(x * 300, y * 300) # 画笔抬起并移动到数值放大400倍的x,y处
turtle.pendown()
turtle.dot(3) # 画一个半径为 3 的圆点
if i % 10000 == 0: # 实验为10000的倍数次时
turtle.pencolor('black')
pi = 4 * (hits / i) # 根据落在圆内外的点数量估算PI值
turtle.penup() # 画笔抬起
turtle.goto(320, 150 - i // 1000 * 30) # 移动到区域外记录当前PI值
turtle.pendown() # 画笔抬起
turtle.write("{}次时PI的值是{:.4f}".format(i, pi), font=("宋体", 18, "normal"))
turtle.hideturtle() # 隐藏光标
turtle.update() # 刷新
turtle.done() # 结束绘制
if __name__ == '__main__':
num = int(input()) # 输入模拟次数
in_circle_lst, out_circle_lst = [], []
print(monte_carlo_pi(num, 1973)) # 输出函数运行结果
4.模拟股票价格
在金融工程中,有下面这样一个公式,他利用目前的股价 去预测
时间之后的股价
:
这其中的参数我来解释一下: 表示股票收益率的期望值,这里我们设定为
,即
表示股票的波动率,这里设定为
,其中
表示整数年份,
表示在整个估算周期内,取的具体步数,就好比说
为一年,
如果取244,那么
的粒度就是每个交易日了(一年有244个交易日)。
这里面似乎所有的参数都是确定的,唯独除了 之外,
是一个服从标准正态分布的随机变量,这是这个
,决定了每日的股价
是一个随机变量,而由股价构成的序列是一个随机过程。
我们同样的用蒙特卡罗方法,利用大样本来估计一下在目前股价为 的情况下,1年之后股价的概率分布情况。
这是我们模拟了10000个样本经过上述随机过程,在一年之后股价的分布情况。
这里的核心就是:
e = numpy.random.randn() s = s+musdt+sigmasemath.sqrt(dt)
每一轮 ,我们都生成一个服从标准正态分布的随机变量
,不断通过递推公式
,迭代出下一个时间点的股价,循环往复直到生成一年后的最终结果,这样就模拟出了一年过程中股价随机变量序列构成的随机过程。
我们采用蒙特卡罗方法,设置大样本量(这里设置10000个),最终迭代出10000个对应的一年后股价,然后用柱状图就能看出其总体分布特征。
*股价变化曲线的过程展现
上面我们分析的是这10000个样本在一年之后最终股价的整体分布情况,实际上我们还有一个同样重要的过程可以进行监测和展现,那就是从 时刻起到1年后的这一段时间内,每隔
时间间隔点由实时价格随机变量构成的序列,换句话说就是随机过程的整体展现。这个在上面的基础上进一步完成,其实不难,就是我们不光要计算出股票最终的价格,还有记录下每个
时间点的价格,并把他记录下来。
这里我们清晰的展现出了由244个 时间点的股价数据所构成的序列,这是随着时间变化的随机过程。我们为了从整体把握他的分布特征,设定了100个样本,因此模拟出了100条价格曲线。
这种结果图表面上看起来比较凌乱,实际上可以从整体上发现许多端倪,例如股价在运行过程中的整体分布区间,上下界,集中程度等等,都可以有一个整体的把握。
因此我们不仅可以得到最终股价的分布,也可以知道股价运行变化的完整价格路径,这个价格路径代表了蒙特卡罗方法的精髓,通过这个价格路径的可视化呈现,让我们更加直观的从宏观上把握了随机过程。
import math
import numpy as np
import matplotlib.pyplot as plt
s0 = 10.0
T = 1.0
n = 244 * T
mu = 0.15
sigma = 0.2
n_simulation = 1000
dt = T / n
s_array = []
for i in range(n_simulation):
s = s0
for j in range(int(n)):
e = np.random.randn()
# e = scipy.random.normal()
s = s + mu * s * dt + sigma * s * e * math.sqrt(dt)
s_array.append(s)
plt.hist(s_array, bins=30, edgecolor='k')
plt.show()
dt = T / n
random_series = np.zeros(int(n), dtype=float)
x = range(0, int(n))
for i in range(n_simulation):
random_series[0] = s0
for j in range(1, int(n)):
e = np.random.randn()
# e = scipy.random.normal()
random_series[j] = random_series[j - 1] + mu * random_series[j - 1] * dt + sigma * random_series[
j - 1] * e * math.sqrt(dt)
plt.plot(x, random_series)
plt.show()