在python中布冯的针模拟

时间:2015-07-08 11:21:46

标签: python simulation probability

import numpy as np
import matplotlib.pylab as plt

class Buffon_needle_problem:

    def __init__(self,x,y,n,m):
        self.x = x #width of the needle
        self.y = y #witdh of the space
        self.r = []#coordinated of the centre of the needle
        self.z = []#measure of the alingment of the needle
        self.n = n#no of throws
        self.m = m#no of simulations
        self.pi_approx = []

    def samples(self):
        # throwing the needles
        for i in range(self.n):
            self.r.append(np.random.uniform(0,self.y))
            self.z.append(np.random.uniform(0,self.x/2.0))
        return [self.r,self.z]

    def simulation(self):
        self.samples()
        # m simulation
        for j in range(self.m):
            # n throw
            hits = 0 #setting the succes to 0
            for i in range(self.n):
                #condition for a hit
                if self.r[i]+self.z[i]>=self.y or self.r[i]-self.z[i] <= 0.0:
                    hits += 1
                else:
                    continue
            hits = 2*(self.x/self.y)*float(self.n/hits)
            self.pi_approx.append(hits)
        return self.pi_approx

 y = Buffon_needle_problem(1,2,40000,5)

 print (y.simulation())

对于那些不熟悉布冯问题的人,这里是http://mathworld.wolfram.com/BuffonsNeedleProblem.html

实现相同的想法(和输出) http://pythonfiddle.com/historically-accurate-buffons-needle/

我的预期输出应该是pi的值,但是我的代码给了我4左右。任何人都可以指出逻辑错误吗?

7 个答案:

答案 0 :(得分:1)

针的对齐采样应该是均匀的余弦。请参阅以下链接以了解该方法:http://pdg.lbl.gov/2012/reviews/rpp2012-rev-monte-carlo-techniques.pdf

此外,该计划存在一些逻辑问题。这是一个工作版本。

#!/bin/python
import numpy as np

def sample_cosine():
  rr=2.
  while rr > 1.:
    u1=np.random.uniform(0,1.)
    u2=np.random.uniform(0,1.)
    v1=2*u1-1.
    rr=v1*v1+u2*u2
  cc=(v1*v1-u2*u2)/rr
  return cc

class Buffon_needle_problem:

     def __init__(self,x,y,n,m):
        self.x = float(x)  #width of the needle
        self.y = float(y)  #witdh of the space
        self.r = [] #coordinated of the centre of the needle
        self.z = [] #measure of the alignment of the needle
        self.n = n  #no of throws
        self.m = m  #no of simulations
        self.p = self.x/self.y
        self.pi_approx = []

    def samples(self):
        # throwing the needles
        for i in range(self.n):
            self.r.append(np.random.uniform(0,self.y))
            C=sample_cosine()
            self.z.append(C*self.x/2.)
        return [self.r,self.z]

    def simulation(self):
        # m simulation
        for j in range(self.m):
            self.r=[]
            self.z=[]
            self.samples()
            # n throw
            hits = 0 #setting the success to 0
            for i in range(self.n):
                #condition for a hit
                if self.r[i]+self.z[i]>=self.y or self.r[i]-self.z[i]<0.:
                    hits += 1
                else:
                    continue
            est =self.p*float(self.n)/float(hits)
            self.pi_approx.append(est)
        return self.pi_approx

y = Buffon_needle_problem(1,2,80000,5)

print (y.simulation())

答案 1 :(得分:1)

仅当两行之间的距离是针长的两倍时,布冯针才能正确工作。确保进行交叉检查。

我已经看到许多baffon的在线模拟都在犯此错误。它们只是将两条相邻线之间的距离等于针的长度。这是他们的主要逻辑错误。

答案 2 :(得分:0)

我想说的问题是你是通过一个简单的线性函数定义针的对齐方式,而实际上针的中心有效长度是由正弦函数定义的。

您想通过使用从其角度计算它的函数来计算针的有效长度(与线成90°)。

类似的东西:

self.z.append(np.cos(np.random.uniform(-np.pi/2, np.pi/2))*self.x)

这将给出在-90°和+ 90°之间的随机角度的余弦,即针的长度。

作为参考,cos(+/-90) = 0cos(0) = 1,因此在90°时,针的长度实际上为零,而在0°时,其全长。

我没有在这台机器上安装mathplotlib或numpy,所以我看不出这是否修复了它,但这绝对是必要的。

答案 3 :(得分:0)

看起来你提交了一个简单的舍入错误。下面的代码有效,但结果并不是非常接近pi ...

import numpy as np
import matplotlib.pylab as plt

class Buffon_needle_problem:
def __init__(self,x,y,n,m):
    self.x = x #width of the needle
    self.y = y #witdh of the space
    self.r = []#coordinated of the centre of the needle
    self.z = []#measure of the alingment of the needle
    self.n = n#no of throws
    self.m = m#no of simulations
    self.pi_approx = []

def samples(self):
    # throwing the needles
    for i in range(self.n):
        self.r.append(np.random.uniform(0,self.y))
        self.z.append(np.random.uniform(0,self.x/2.0))
    return [self.r,self.z]

def simulation(self):
    #self.samples()
    # m simulations
    for j in range(self.m):
        self.r=[]
        self.z=[]
        for i in range(self.n):
            self.r.append(np.random.uniform(0,self.y))
            self.z.append(np.random.uniform(0,self.x/2.0))
        # n throws
        hits = 0 # setting the succes to 0
        for i in range(self.n):
            # condition for a hit
            if self.r[i]+self.z[i]>=self.y or self.r[i]-self.z[i] <= 0.0:
                hits += 1
            else:
                continue
        hits = 2.0*(float(self.x)/self.y)*float(self.n)/float(hits)
        self.pi_approx.append(hits)

    return self.pi_approx

y = Buffon_needle_problem(1,2,40000,5)
print (y.simulation())

另请注意,您使用相同的样本进行所有模拟!

答案 4 :(得分:0)

我用Python turtle来近似Pi的值:

from turtle import *
from random import *

setworldcoordinates(-100, -200, 200, 200)
ht(); speed(0); color('blue')

drops = 20  # increase number of drops for better approximation
hits = 0    # hits counter

# draw parallel lines with distance 20 between adjacent lines
for i in range(0, 120, 20): 
    pu(); setpos(0, i); pd()
    fd(100) # length of line

# throw needles
color('red')
for j in range(drops):
    pu()
    goto(randrange(10, 90), randrange(0,100))
    y1 = ycor() # keep ycor of start point
    seth(360*random())
    pd(); fd(20)    # draw needle of length 20
    y2 = ycor() # keep ycor of end point

    if y1//20 != y2//20:    # decisive test: if it is a hit then ...
        hits += 1       # increase the hits counter by 1

print(2 * drops / hits)

Output samples
With 50 drops   3.225806451612903
with 200 drops  3.3057851239669422
with 1000 drops 3.1645569620253164

答案 5 :(得分:-1)

不回答原始问题,如果你只是想要pi估计,这里有一些简单的代码,我昨天在Uni Sydney(Aust)的计算修订练习中,反对我早期的倾向,为了降低复杂性,我们只建模了线与零之间的距离和从零到90度的随机角度之间的随机点。

import random 
from numpy import pi, sin

def buffon(L, D, N):
    '''
    BUFFON takes L (needle length), 
    D = distance between lines and N = number of drops, 
    returns probability of hitting line
    generate random number 'd' between 0 and D 
    generate theta between 0 and pi/2
    hit when L*sin(theta)) - d is great than D

    '''
    hit = 0; 
    for loop in range(N) :
        theta = pi*random.random()/2
        if L * sin(theta) > D - D*random.random(): # d = random*D
            hit += 1
    return hit/N

#% Prob_hit = 2*L/(D*pi) hence:  Pi_est = 2*L / (P_hit*D); 
L = 1
D = 4
N = int(1e8) 
Pi_est = 2*L / (buffon(L,D,N)*D)

在MatLab中,我想在Python中尝试它,看看我是否可以使用任何理解列表,任何想法来加快这一点欢迎。

答案 6 :(得分:-1)

需要注意的是,Monte Carlo 方法对于这种计算(计算数字 pi)并不是最好的。无论如何,为了获得更准确的圆周率,有必要抛出相当多的针(或点,在四分之一圆的情况下)。蒙特卡罗方法的主要缺点是其不可预测性。

https://github.com/Battle-Of-Two-K/Buffon-s-Noodle-Problem

https://github.com/Battle-Of-Two-K/Calculating-Pi-by-Monte-Carlo-Method

enter image description here