Buffon's needle simulation in python

我只是一个虾纸丫 提交于 2021-02-05 11:53:17

问题


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())

For those who unfamiliar with Buffon's problem, here is the http://mathworld.wolfram.com/BuffonsNeedleProblem.html

or

implementing the same idea (and output) http://pythonfiddle.com/historically-accurate-buffons-needle/

My expected output should be the value of pi but my code give me around 4. Can anyone point out the logical error?


回答1:


The sampling of the needle's alignment should be a uniform cosine. See the following link for the method: http://pdg.lbl.gov/2012/reviews/rpp2012-rev-monte-carlo-techniques.pdf

Also, there were a few logical problems with the program. Here is a working version.

#!/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())



回答2:


Buffon's needle work accurately only when the distance between the two lines is double the length of needle. Make sure to cross check it.

I have seen many baffon's online simulation which are doing this mistake. They just take the distance between two adjacent lines to be equal to the needle's length. That's their main logical errors.




回答3:


I would say that the problem is that you are defining the alignment of the needle by a simple linear function, when in fact the effective length of the needle from its centre is defined by a sinusoidal function.

You want to calculate the effective length of the needle (at 90° to the lines) by using a function that will calculate it from its angle.

Something like:

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

This will give the cosine of a random angle between -90° and +90°, times the length of the needle.

For reference, cos(+/-90) = 0 and cos(0) = 1, so at 90°, the needle with have effectively zero length, and at 0°, its full length.

I have neither mathplotlib or numpy installed on this machine, so I can't see if this fixes it, but it's definitely necessary.




回答4:


Looks like you were committing a simple rounding error. The code below works, though the results are not very close to 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())

Also note that you were using the same sample for all simulations!




回答5:


I used Python turtle to approximate the value of 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



回答6:


NOT answer to original question, if you just want the pi estimate, here's some simple code from I did in a computational revision exercise yesterday at Uni Sydney (Aust), against my early inclinations, to reduce complexity, we only modelled for a random point between zero and distance between lines and a random angle from zero to 90 degrees.

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)

It was in MatLab, I wanted to try it in Python, see if I could use any comprehension lists, any ideas to speed this up WELCOME.



来源:https://stackoverflow.com/questions/31291174/buffons-needle-simulation-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!