使用python实现布丰投针法

这篇文章介绍了如何使用Python编程语言,结合numpy和matplotlib库,模拟布丰投针法来估算圆周率π的值,通过随机生成针的落点和与平行线的交点,演示了一个计算π的传统方法在现代计算机中的应用。
摘要由CSDN通过智能技术生成

对于π的值,直到1946年的时候,人类才能将π的值精确计算到小数点后2037位,而现在的超级计算机的能力可以精确的计算到小数点后几十亿位,然而在计算机发明之前,还是使用这里的布丰投针法来计算π值,是最实用的方法。

使用代码来模拟这个过程,首先是程序设计思路的基本路程:


import  math
import  numpy as np
from  numpy import random
import  matplotlib.pyplot as plt
import  matplotlib.lines as mlines

然后根据代码设计的流程图实现代码:

UPDATE_FREQ = 500  #每模拟投针500次后就将结果绘制出来
BOUND = 10  #图形绘制区域的长和宽
BORDER = 0.05 * 10  #绘制区域边缘宽度
NEEDLES=  10000  #投针的次数
NEEDLE_LENGTH = 1  #针的长度
FLOORBOARD_WIDTH = 2  #两条平行线之间的距离
FLOORBOARD_COLOR = 'black'  #两条平行线的颜色
NEEDLE_INTERSECTING_COLOR = 'red'  #铁针与线相交时的颜色
NEEDLE_NON_INTERSECTING_COLOR = 'green'  #铁针与直线不相交时的颜色
class Needle:
    def  __init__(self, x = None, y = None, theta = None, length = NEEDLE_LENGTH):
        '''
        x, y 作为铁针的中点,由于图像绘制区域的宽度为BOUND,因此我们可以把中点坐标设置在宽度内的任何地方,
        theta是铁针与水平方向的夹角,在分析时我们将该值限制在0和Pi/2之间,考虑这个区间是因为我们确定哪个平行线
        与铁针跟接近的情况下。如果不考虑这个前提条件,那么夹角的值就可以限定在0和pi之间。我们模拟时不事先考虑那条线与
        针的距离更近,因此我们对夹角区间采用0和pi之间
        '''
        if x is None:
            x = random.uniform(0, BOUND)
        if y is None:
            y = random.uniform(0, BOUND)
        if theta is None:
            theta = random.uniform(0, math.pi)
        self.center = np.array([x, y])  #设置铁针中心点坐标
        self.comp = np.array([length/2 * math.cos(theta), length/2 * math.sin(theta)])  #根据铁针与水平方向的夹角,计算中心在水平方向和竖直方向的距离
        self.endPoints = np.array([np.add(self.center, -1 * self.comp),
                                  np.add(self.center, self.comp)])  #根据中心与水平方向和竖直方向的距离运算铁针两头的坐标
    def  intersectsY(self, y):
        return  self.endPoints[0][1] < y and self.endPoints[1][1] > y  #y是平行线在竖直方向上的坐标
class Buffon_Sim:  #启动模拟进程
    def  __init__(self):
        self.floorboards = []  #存储平行线在竖直方向上的y坐标
        self.boards = int ((BOUND / FLOORBOARD_WIDTH) + 1)  #计算平行线在绘制区域内的数量
        self.needles = []  #存储模拟的铁针
        self.intersections = 0  #记录铁针与平行线相交的数量
        window = "Buffon"
        title = "Buffon Needle Simulation"
        desc = (str(NEEDLES) + " needles of length " + str(NEEDLE_LENGTH) + 
               " uniformly distributed over a " + str(BOUND) + " by " + str(BOUND) + 
               " area" + " with floorboards of width " + str(FLOORBOARD_WIDTH))  #描述当前模拟情况
        self.fig = plt.figure(figsize = (8,8))
        self.fig.canvas.set_window_title(window)
        self.fig.suptitle(title, size = 16, ha = 'center')
        self.buffon = plt.subplot()  #将模拟投针绘制出来
        self.buffon.set_title(desc, style = 'italic', size = 9, pad = 5)
        self.results_text = self.fig.text(0, 0, self.updateResults(), size = 10)  #将投针情况绘制成图像
        self.buffon.set_xlim(0 - BORDER, BOUND + BORDER)
        self.buffon.set_ylim(0 - BORDER, BOUND + BORDER)
        plt.gca().set_aspect('equal')  
    def  plotFloorboards(self):
        for j in range(self.boards):
            self.floorboards.append(0 + j * FLOORBOARD_WIDTH)  #绘制平行线
            self.buffon.hlines(y = self.floorboards[j], xmin = 0,
                              xmax = BOUND, color = FLOORBOARD_COLOR, linestyle = '--', linewidth = 2.0)
    def  tossNeedle(self):  #模拟投针过程
        needle = Needle()
        self.needles.append(needle)
        p1 = [needle.endPoints[0][0], needle.endPoints[1][0]]  #获取铁针两个端点的x坐标
        p2 = [needle.endPoints[0][1], needle.endPoints[1][1]]  #获取铁针两个端点的y坐标
        for k in range(self.boards):  #检测铁针是否与平行线相交
            if needle.intersectsY(self.floorboards[k]):
                self.intersections += 1
                self.buffon.plot(p1, p2, color = NEEDLE_INTERSECTING_COLOR, linewidth = 0.5)  #将相交的铁针用红色线条表示
                return 
        self.buffon.plot(p1, p2, color = NEEDLE_NON_INTERSECTING_COLOR, linewidth = 0.5)  #不相交的铁针用绿色线条表示
       
        
    def  plotNeedles(self):
        
        for i in range(NEEDLES):
            self.tossNeedle()
            self.results_text.set_text(self.updateResults(i + 1))
            if (i + 1) % UPDATE_FREQ == 0:  #连续模拟指定次数投针后把结果绘制出来
                plt.pause(0.0001)     
    def  updateResults(self, needlesToTossed = 0):
        if self.intersections == 0:
            sim_pi = 0
        else :
            sim_pi = (2 * NEEDLE_LENGTH * needlesToTossed) / (FLOORBOARD_WIDTH * self.intersections)  #根据公式(4)计算Pi值
        error = abs(((math.pi - sim_pi) / math.pi) * 100)  #计算模拟结果与真实结果的误差
        s = ("Intersections: " + str(self.intersections) + \
               "\nTotal Needles: " + str(needlesToTossed) + \
               "\nApproximation of pi: " + str(sim_pi) + "\nError: " + str(error) + "%")
        return s
    def  plot(self):
        legend_lines = [mlines.Line2D([], [], color = FLOORBOARD_COLOR, linestyle = '--', lw = 2),
                       mlines.Line2D([], [], color = NEEDLE_INTERSECTING_COLOR, lw = 1),
                       mlines.Line2D([], [], color = NEEDLE_NON_INTERSECTING_COLOR, lw = 1)]
        self.buffon.legend(legend_lines, ['floorboard', 'intersecting needle', 'non-intersecting needle'],
                          loc = 1, framealpha = 0.9)
        self.plotFloorboards()
         
        self.plotNeedles()
        plt.show()
      
        
bsim = Buffon_Sim()
bsim.plot()

使用的公式以及代码运行的结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值