4
$\begingroup$

I have the following question I'm trying to write a simulation for:

Let T_1 be the number of crossings in n tosses of the needle, then $$E_1 = T_1d/(nl)$$ is an unbiased estimator of 2/π. Write a program to simulate E_1 using n = 100,000 needle tosses.

I have found, hopefully correctly, the minimum l in terms d as the problem asks (not shown here). My question, however, is how do I choose T_1 to get the correct estimate of 2/π? My code below seems to work, but the answer is incorrect in terms of decimal places dependent on choice of T_1. The question doesn't give any additional comments regarding T_1 either. I feel like I'm missing something.

Est <- function(n, T, d){
  E <- c()
  l <- (d*pi)/4
  for(i in 1:n) {
  E[i] <- (T*d)/(n*l)
  }
  mean <- mean(E)
  c.int <- quantile(E, c(0.025, 0.975))
  return(c(mean, c.int))
}

Additionally, how do I also get the function to return c.int?

**Updated code from OP **

Est <- function(n, d){ 
  E <- c() 
  l <- (d*pi)/4 
  for(i in 1:n) {
    x <- runif(n, 0, pi/2)
    y <- runif(n, 0, d/2)
    ncross <- y <= d/2 * sin(x)
#    E[i] <- (ncross*d)/(n*l) 
    E[i] <- mean(ncross) * d/l
  } 
  mean <- mean(E)
  c.int <- quantile(E, c(0.025, 0.975))
  return(c(mean, c.int))
}

Est(10000, 1)
$\endgroup$
7
  • $\begingroup$ I'm missing some points about your code. What is d? What is l? Isn't T what you're trying to estimate? The en.wikipedia.org/wiki/Buffon%27s_needle_problem#Solution page suggests using random uniforms to find values for x and $\theta$ and then finding if the needle at that position crosses the line. $\endgroup$
    – Ron Jensen
    Dec 17, 2019 at 23:32
  • 2
    $\begingroup$ The number of crossings is what you observe in your simulation experiment--it's not something you specify! To make those observations, the program has to simulate the random location and orientation of the needle and figure out whether it crosses any line. The latter is a (simple) geometric calculation. Your program needs only three lines: one to simulate the coordinate of the lines's center; another to compute its endpoints; and a third to test the endpoints for a line crossing and summarize the results of those tests. $\endgroup$
    – whuber
    Dec 17, 2019 at 23:48
  • $\begingroup$ d is the space of the grid lines and l is the size of the needle being tossed basically. I'm trying to show via bootstrap that E_1 is indeed an unbiased estimator for 2/pi. $\endgroup$
    – Michael
    Dec 17, 2019 at 23:49
  • $\begingroup$ I figured it was senseless to specify T_1, but the question asks for a simulation of E_1, which is an estimate of 2/pi = 0.6366198. So I shouldn't be estimating pi, which is the classic Buffon's needle question, but 2/pi. That's why I'm confused. So perhaps i need to simulate T as well and then take the result for the Est function. $\endgroup$
    – Michael
    Dec 18, 2019 at 0:26
  • $\begingroup$ I tried this, all in one function but I get errors and I don't see why I should get errors (50+ errors): Est <- function(n, d){ E <- c() l <- (d*pi)/4 for(i in 1:n) { x <- runif(n, 0, pi/2) y <- runif(n, 0, d/2) ncross <- y <= d/2 * sin(x) E[i] <- (ncross*d)/(n*l) } mean <- mean(E) c.int <- quantile(E, c(0.025, 0.975)) return(c(mean, c.int)) } Est(100000, 1) $\endgroup$
    – Michael
    Dec 18, 2019 at 1:24

2 Answers 2

4
$\begingroup$

This is how I approached the problem, based on the Wikipedia article and your code:

## Let el be the length of the needle and te be the distance between two lines.
## https://en.wikipedia.org/wiki/Buffon%27s_needle_problem

Est <- function(n, el, te) {
  theta <- runif(n, 0, pi/2)
  x <- runif(n, 0, te/2)
  # cross is a vector of true/false. Take the mean to find a proportion
  cross <- x <= el/2 * sin(theta)

  return(mean(cross))
}

I then set up some more code to call the function a few times so I could get some repeated samples:

el = 1
te = 5
E = c()

for(i in 1:1000) {
# only if el <= te
  E[i] = 2*el/(Est(1e5, el, te)*te)
}

mean(E)
quantile(E,  c(0.025, 0.975))
hist(E)

And this was my result:

> mean(E)
[1] 3.141163
> quantile(E,  c(0.025, 0.975))
    2.5%    97.5% 
3.090951 3.189290 

Histogram of 1,000 trials

Added analysis: So if $T_1$ is the number of crossings in $n$ trials, we have the probability a needle crosses is $ P = \frac{T_1}{n} $ and from the Wikipedia article, $ P = \frac{2l}{d\pi} $, where $d$ is the distance between lines (aka te in the code) and l is the length of the needle (aka el in the code). The code calculates $E = \frac{2l}{d\cdot P} \approx\pi $ or $ E=\frac{2l\cdot n}{d\cdot T_1} \approx \pi \Rightarrow \frac{2}{\pi} \approx \frac {T_1\cdot d} {n \cdot l}$

Tldr: change the calculation of E to

        # only if el <= te
  E[i] = (Est(1e5, el, te)*te)/el

which is equivalent to $\frac {T_1\cdot d} {n \cdot l}$

$\endgroup$
5
$\begingroup$

One major problem with your code is that involves the use of $\pi$, and so it cannot be regarded as a genuine implementation of Buffon's needle to estimate $\pi$. A genuine implementation of the needle algorithm would generate the position and direction of the needles, and determine whether a needle crosses a line, without ever using $\pi$ (or any trigonometric functions) in the algorithm. Generation of a series of indicators from needles would allow you to estimate $\pi$, with an appropriate confidence interval, using the central limit theorem.


Buffon's needle algorithm without using $\boldsymbol{\pi}$: Buffon's needle experiment can be implemented using a rejection-sampling method that does not require the use of $\pi$. This can be done over any appropriately sized lined space. For simplicity, we will consider the simplest case of a space that is a unit square $\mathcal{S} = [0,1]^2$, where the left and right boundaries are the "lines" in the experiment. Let $\mathbf{M} \sim \text{U}(\mathcal{S})$ denote the midpoint of the thrown needle, which is uniformly distributed on the unit square. (Note that this means that the needle may lie partly off the unit square, and may cross the left or right boundary lines.)

To determine the direction of the needle, use the following rejection-sampling method. Generate a proposed value $\mathbf{D} \sim \text{U} (\mathcal{S})$ and accept this value if $||\mathbf{D}|| \leqslant 1$. This gives a value that is uniformly distributed in the unit circle, so the value $\mathbf{D}/||\mathbf{D}||$ is uniformly distributed on the boundary of the unit circle. We take the needle to be aligned in the direction of this vector. For simplicity, we consider the case of a "short" needle, with length $\ell \leqslant 1$. Since this needle has half-length $\ell/2$, the two end-points of the needle are:

$$\mathbf{E}_1 \equiv \mathbf{M} + \frac{\ell}{2} \cdot \frac{\mathbf{D}}{||\mathbf{D}||} \quad \quad \quad \mathbf{E}_2 \equiv \mathbf{M} - \frac{\ell}{2} \cdot \frac{\mathbf{D}}{||\mathbf{D}||}.$$

The needle crosses one of the left or right boundary lines if and only if the horizontal coordinate of one of these vectors falls outside the unit interval. (They cannot both fall outside this interval, since the midpoint of the needle is in the unit square.) The indicator for crossing one of the boundary lines is denoted as $H$, and can be written as:

$$H \equiv \mathbb{I}(E_{1,1} < 0) + \mathbb{I}(E_{1,1} > 0) + \mathbb{I}(E_{2,1} < 0) + \mathbb{I}(E_{2,1} > 0).$$

This gives an algorithm to generate a single indicator value for the needle crossing the boundary. Note that this algorithm did not involve the use of $\pi$, since the direction of the needle was determined using a simple rejection-sampling method that only involved the generation of uniform random variables. It can be shown that $H \sim \text{Bern}(2 \ell / \pi)$, so we can estimate $\pi$ by generating a large number of indicator values $H_1,...,H_n$ and taking $\hat{\pi} = 2 \ell / \bar{H}$ where $\bar{H}$ is the sample mean of the indicators. Since you want to estimate $2/\pi$, you would use the point estimate $2/\hat{\pi} = \bar{H} / \ell$.


Implementation in R: We will use the above algorithm to generate a vector $H_1,...,H_n$ for $n$ needles. We construct a function where we specify a needle length l and the number of needles n. The function generates a vector of indicator values for these needles, indicating whether they crossed the boundary lines.

BUFFON_NEEDLES <- function(l, n = 1) {

#Check inputs
if (!is.numeric(n))     { stop('Error: Number of needles must be numeric') }
if (length(n) != 1)     { stop('Error: Number of needles should be a scalar') }
if (n != as.integer(n)) { stop('Error: Number of needles must be an integer') }
if (n < 1)              { stop('Error: Number of needles must be at least one') }
if (!is.numeric(l))     { stop('Error: Needle length must be numeric') }
if (length(l) != 1)     { stop('Error: Needle length should be a scalar') }
if (l < 0)              { stop('Error: Needle length must be positive') }
if (l > 1)              { stop('Error: Needle length cannot be greater than one') }

#Set output vector
H <- rep(0, n);

#Generate values for output vector
for (i in 1:n) {

  #Generate midpoint of needle
  M  <- runif(2);

  #Generate direction of needle
  D  <- c(1,1);
  while (norm(D, type = '2') > 1) { D <- runif(2); }
  DD <- D/norm(D, type = '2');

  #Determine endpoints of needle
  E1 <- M + (l/2)*DD;
  E2 <- M - (l/2)*DD;

  #Determine whether needle crosses lines
  H[i] <- (E1[1] < 0)|(E1[1] > 1)|(E2[1] < 0)|(E2[1] > 1); }

#Give output
H; }

We can implement this function for a large value of n and use this to get a point estimate of $\pi$ as follows.

#Set number of needles and needle length
l <- 0.4;
n <- 10^7;

#Generate indicators
set.seed(1);
HHH <- BUFFON_NEEDLES(l, n);

#Estimate pi
2*l/mean(HHH);
[1] 3.142286

#Estimate 2/pi
mean(HHH)/l;
[1] 0.6364792

As you can see, taking $n = 10^7$ is sufficient to get quite a good estimate of $\pi$. Since you propose to use $n = 10^5$ your estimator will have less accuracy, but it should still give you a rough estimate of the true value.

$\endgroup$
2
  • 1
    $\begingroup$ Of course, the acceptance rate of your D sampler is $\frac{\pi}{4}$ that makes it a faster way to estimate $\pi$ without the overhead of the Buffon problem. $\endgroup$
    – Ron Jensen
    Dec 18, 2019 at 2:59
  • $\begingroup$ Indeed, and a faster method still is to just use the truncated Leibniz formula, which removes the use of a stochastic component altogether. $\endgroup$
    – Ben
    Dec 18, 2019 at 3:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.