蒙特卡洛(Monte Carlo)方法,又称随机抽样或统计试验方法。当所求解的问题是某种事件出现的概率,或某随机变量的期望值时,可以通过某种“试验”方法求解。
简单说,蒙特卡洛是利用随机试验求解问题的方法。
构造一个单位正方形和一个单位圆,往整个区域内随机投入点,根据点到原点的距离判断点是落在圆内还是在圆外,从而根据落在两个不同区域的点的数目,求出两个区域的比值。如此一来,就可以求出单位圆的面积,从而求出圆周率π。

随机产生一对x,y的值,随机生成x,y的正负号,组成一组坐标。控制turtle走到这个坐标处画一个点,判断这个点的位置如果落在圆内,计数。绘制完成后,所以落在圆内的点与总点数的比值即是面积在比值。如果所有点都是随机产生,那么落在圆内的点和圆外的点的分布应该是基本均匀的,而且随点数的增加会趋于更均匀。即提高产生点的数量,可以获得更准确的PI值。
import randomn = int(input()) # 输入一个整数,100000lsx = [random.random() for x in range(n)] # 列表推导式随机生成n个小数lsy = [random.random() for y in range(n)] # 列表推导式随机生成n个小数coordinate = zip(lsx, lsy) # zip()将两个列表中对应序号的数据组成n对坐标count = 0 # 计数器置0for xy in coordinate: # zip是生成器对象,可遍历if xy[0] ** 2 + xy[1] ** 2 <= 1: # 判断坐标点是否落在圆内count = count + 1 # 落在圆内的点数量增加 1PI = 4 * count / n # 计算圆周率值print('{:.6f}'.format(PI)) # 输出结果3.140920(随机结果不同)
可以在遍历的同时进行同步赋值:
import randomn = int(input()) # 输入一个整数,例如100000lsx = [random.random() for x in range(n)] # 推导式随机生成n个小数lsy = [random.random() for y in range(n)] # 推导式随机生成n个小数coordinate = zip(lsx, lsy) # 将两个列表中对应序号的数据组成n对坐标count = 0 # 计数器置0for x, y in coordinate: # 可在遍历同时用同步赋值方法分别取得横纵坐标的值x,yif x ** 2 + y ** 2 <= 1: # 判断坐标点是否落在圆内count = count + 1 # 落在圆内的点数量增加 1PI = 4 * count / n # 计算圆周率值print('{:.6f}'.format(PI)) # 随机结果,如3.140920
用列表推导式产生落在圆内部的坐标的列表
import randomn = int(input()) # 输入一个整数,例如100000lsx = [random.random() for x in range(n)] # 推导式随机生成n个小数lsy = [random.random() for y in range(n)] # 推导式随机生成n个小数coordinate = zip(lsx, lsy) # 将两个列表中对应序号的数据组成n对坐标# 用列表推导式产生落在圆内部的坐标的列表,其长度为点的数量count = len([(x, y) for x, y in coordinate if x ** 2 + y ** 2 <= 1])PI = 4 * count / n # 计算圆周率值print('{:.6f}'.format(PI)) # 随机结果,如3.140920
用matplotlib绘制模拟过程
import randomimport matplotlib.pyplot as pltdef monte_carlo_pi(num):"""接收一个表示随机次数的整数,用蒙特卡洛法计算圆周率,返回一个圆周率、落在圆内点的列表和圆外点的列表"""hits = 0 # 落在圆内的计数器初值设为 0in_circle_lst, out_circle_lst = [],[]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 + 1in_circle_lst.append([x,y])else:out_circle_lst.append([x,y])pi = 4 * (hits / num)return pi,in_circle_lst, out_circle_lstdef 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] # 落在圆内的点的列表xy_in_circle = [x[1] for x in in_circle_lst] # 落在圆内的点的列表yx_out_circle = [x[0] for x in out_circle_lst] # 落在圆外的点的列表xy_out_circle = [x[1] for x in out_circle_lst] # 落在圆外的点的列表yplt.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() # 显示绘制结果if __name__ == '__main__':pi, in_circle_ls, out_circle_ls = monte_carlo_pi(10000) # 调用函数计算圆周率print(pi)monte_carlo_show(in_circle_ls, out_circle_ls)

用turtle绘制效果示意图
import random # 导入随机数库import turtle # 导入turtle库s,n = map(int,input().split())random.seed(s)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)hits = 0 # 落在圆内的计数器初值设为 0turtle.tracer(0) # 设置刷新间隔for i in range(1, n+1):x, y = random.random(), random.random() # 生成两个随机数模拟一个点的坐标signx,signy = random.choice('+-'),random.choice('+-')# 随机生成x和y的符号“+”或“-”pos = (x ** 2 + y ** 2)**0.5 # 计算坐标(x,y)到原点的距离if pos <= 1.0: # 如果距离小于等于1,点在圆内hits = hits + 1turtle.pencolor('red') # 落在圆内在点颜色用红色else: # 如果距离大于1,点在圆外turtle.pencolor('blue') # 落在圆内在点颜色用蓝色x = float(signx+str(x)) # 将 x 值与符号拼接并转为浮点数y = float(signy+str(y)) # 将 y 值与符号拼接并转为浮点数turtle.penup()turtle.goto(x * 400, y * 400) # 画笔抬起并移动到数值放大400倍的x,y处turtle.pendown()turtle.dot(5) # 画一个半径为 3 的圆点if i % 1000 == 0: # 实验为10000的倍数次时turtle.pencolor('black')pi = 4 * (hits / i) # 根据落在圆内外的点数量估算PI值turtle.penup() # 画笔抬起turtle.goto(420, 200- i // 1000 * 30) # 移动到区域外记录当前PI值turtle.pendown() # 画笔抬起turtle.write("{}次时PI的值是{:.4f}".format(i,pi),font=("宋体", 18, "normal"))turtle.update() # 刷新turtle.hideturtle() # 隐藏光标turtle.done() # 结束绘制

