Domanda

** First I want to say, that this question is "homework-inspired": it was homework in Statistical Mechanics, and did not involve programming so I left the homework tag out. **

I have tried to implement Buffon's needle method for estimation of pi in java, but my results are too off, not by much - but there is a famous results using needle/space ratio as 5/6 which should give a better estimation than I'm getting. I would like your help to understand why my results are not accurate.

(For the method, there is a good explanation, with animation, in wikipedia )

My idea was this :

if the the vertical position of the center of the needle + vertical projection (half length*sin(angle)) is greater than the position of a line AND the position minus the vertical projection is less than the position of a line: then that means that one end of the needle is above the line and the other is below it which means there's intersection.

(that's pretty much the intermediate value theorem)

Things I tried:

  • Changing the random number to get 1 for any random number greater than 0.99, didn't make a difference.
  • Checking that the position/(distance between lines) and sin(angle) values are valid, are in the range [0,1) and that the distribution is roughly even; for instance: out of 149100 iterations, 75000~ were greater than 0.5 for both numbers.
  • Checking (and proving) mathematically (intermediate value theorem) that the iteration is counted only when the pin intersect with the line.

This is the implementation ( I'm a newbie to programming, so I would gladly receive comments about the code itself):

import java.util.Scanner;
import java.lang.Math;

public class BuffonNeedle {
   public static Scanner scan = new Scanner(System.in);

   public static double checkNumber(double min) {
      String input = scan.next();
      if (input.equalsIgnoreCase("END")) {
         System.exit(0);
      }
      try { 
      double Num = Double.parseDouble(input);
      if(Num<=min ) {
         System.out.println("Try again: input must be a number greater than "+min);
         return checkNumber(min);
      }
      return Num;
      } catch (Exception e) {
         System.out.println("Try again: input must be a number greater than "+min);
         return checkNumber(min);
      } // end try-catch
   }// end checkNumber method


   public static void main(String[] args) {
     double d = 0;        // distance between two lines     
     double l = 0;        // size of needle                    
     double angle = 0;
     double position = 0; // the position of the center of the pin (in the range of 0 to the distance between two lines)
     double estimateOfPi =0; // the result we will finally give (should be Pi)
     double iterations = 0; 
     double intersections = 0; // number of times needle intersected with line, the number is a double due to calculation later
     int count = 0;
     System.out.println();
     System.out.println("Typing END at any stage will cause the program to terminate, otherwise it will run a 100 times."); 
     System.out.println("if you would like to cheat try- needle: 5 , space: 6 , iterations: a multiply of 213 (like 149100) ");
     // cheating is actually for any multiply of  (d*355/(2*l)), provided the expression is an integer
     System.out.println();
     while (count<100) {
       if (count >=1) System.out.println("Lets try again.");
       System.out.println("Please insert the size of the needle:");
       l = checkNumber(0);   
       System.out.println("Please insert the size between two lines:");
       d = checkNumber(l);
       System.out.println("How many iterations would you like? (the more iterations - the better chance for close estimate)");
       iterations =  checkNumber(0);
       for(int i=0;i<iterations;i++) {
          angle = Math.random()*Math.PI;
          position = d * Math.random();
          // checking to see if there is indeed an intersection - using the intermediate value theorem.
          if( ( ( position + l*Math.sin(angle)/2 >= d ) && ( position - l*Math.sin(angle)/2 <= d ) ) || 
            ( ( position + l*Math.sin(angle)/2 >= 0 ) && ( position - l*Math.sin(angle)/2 <= 0 ) ) ) {
             intersections++;
          } // end if 
       } // end for
       estimateOfPi = (2*l*iterations) / (d*intersections);
       intersections=0;
       System.out.println("π = "+estimateOfPi);
       count++;
    } // end while
  } // end main
} // end class
È stato utile?

Soluzione 2

Well, turns out there is no problem at all, the error of the estimation is

Math.sqrt((Math.PI*Math.PI)/(2*iterations) * (Math.PI * (iterations/intersections  - 2) ))

and can be derived using asymptotic variance of the probability.

Pi is estimated within the margin of error.

Altri suggerimenti

I'm a newbie to programming, so I would gladly receive comments about the code itself

Here goes:

  1. Don't catch Exception. Catch the specific exception or exceptions that you are anticipating, and let the rest propagate.

  2. If you do catch Exception, then make sure that you deal with all possibilities. Your current implementation assumes that the problem will be an invalid number. It could be other things; e.g. standard input is at the EOF position, some (hypothetical) bug causing an NPE, etcetera.

  3. Local variables should always start with a lower case letter; Num -> num.

  4. The way that your checkNumber deals with an error (by recursing) has a problem. If the root cause is not incorrect user input, but something that a retry won't cure, you will end up with infinite recursion! In Java, this will result in a StackOverflowError ... and a massive stack trace (if it is printed).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top