Monte Carlo method to approximate PI

Particularly, we want to find the area of the circle centered in origin. Points in its perimeter are expressed as x * x + y * y = r * r, being r, the radius, and (x, y) a point of the circumference. Our known area is the graphic window of processing, then we'll translate the coordinate axis to the center of the circle. On that origin we'll draw a circle of radius r. Then we'll "throw" N points, and we'll count how many of them "land" inside the circle. (storing the number of points in a variable called "C") According to Monte Carlo: Area of the circle = (C / N) * (area of the graphic window). Then PI = (N / C * r * r) * (area of the graphic window).

The objective is to build a function that returns the apprx value of PI, given 3 parameters:

  • N (number of points "thrown")
  • r (radius of the circumference)
  • A boolean variable that determines if the simulation must be drawn or not.

If the simulation is drawn, there must be a circle in the center of the graphic window, and random points, it they land inside the circle, they must be BLUE, otherwise, they'll be RED.

Can someone please help me do this?

Answers

  • Sure. What code do you already have? Post it here. Make sure you format it for the forums. This is basically a requirement for anyone to help you.

    https://forum.processing.org/two/discussion/15473/readme-how-to-format-code-and-text

  • int N = 1000; 
    int radius = 200;
    
    
    void setup() {
      size(800, 800);
      translate(width/2, height/2);
      ellipse (0, 0, radius, radius);
    }
    
    void draw() {
      translate(width/2, height/2);
      int randX = (int) random(0, width);
      int randY = (int) random(0, height);
      if () {
        fill(0, 0, 255);
        point(randX, randY);
      } else {
        fill(255, 0, 0);
        point(randX, randY);
      }
    }
    
  • Okay. The first problem that jumps out at me is that you have an if statement that doesn't have a condition in it. What condition must be true for a random point to be inside a circle? Specifically, what can you say about the distance (dist()) between the point and the circle's center (even more specifically, in relation to the circle's radius)?

    Draw a diagram if you have to, and work out what that condition should be.

  • edited October 2017

    The second problem that I see is that you are not doing all your drawing in draw(). Move the ellipse that you are drawing from setup() to draw().

    Third, how many random points are you making? Just one for now, sure, but how many are you going to need? You have a variable to track that, so you should make use of it.

    Attempt to add these changes to your code and post the resulting sketch for more help.

  • edited October 2017

    Mine

    Cool, it's possible! But I think your math is wrong... If A is the area of the sketch and E is the area of the circle, then E is PI * R * R and A is W * H. If the points in the circle is C and the total number of points is N, then you'd expect E : A = C : N. So Pi is... well, you can work it out.

  • Thank you so much for your help so far. This is the code I've worked out.

    int numberOfPoints = 1000; 
    int radius = 200;
    
    void setup() {
      size(800, 800);
      translate(width/2, height/2);
      frameRate(10);
    }
    
    void draw() {
      translate(width/2, height/2);
      noFill();
      strokeWeight(1);
      ellipse (0, 0, radius, radius);
      int randX = (int) random(-width/2, width/2);
      int randY = (int) random(-height/2, height/2);
      if (dist(randX, randY, width/2, height/2) < radius) { 
        fill(0, 0, 255);
        strokeWeight(5);
        point(randX, randY);
      } else {
        fill(255, 0, 0);
        strokeWeight(5);
        point(randX, randY);
      }
    }
    
  • edited October 2017

    TfGuy44 Could you please post your code so that I see what I have to do? If I have any doubt I'll ask you

  • No. What part of your assignment is unclear to you?

    You've already been shown enough of my code.

  • You don't need a while block. But you do need a loop of some kind.

  • The thing is that we've only seen while, if, else if... etc... so anything out of that is unknown to me.

  • You need a variable to keep track of the number that fall in the circle.

    I suggest you use the float data type for randX and randY rather than int. Using whole numbers may distort your Pi approximation.

  • I was using integers because points are supposed to be int data type, or am I wrong?

  • edited October 2017

    You can use round() on the float datatype if you wanna: ~O)
    https://Processing.org/reference/round_.html

    But AFAIK, all pixels are round() internally before being rendered. :-j

  • A pixel position is represented by 2 integer values (horizontal and vertical position) but the problem you are trying to solve, the value of PI, is a floating point number type. The method point(x, y) will accept float variables as well as ints e.g. point(2.1, 70.9) is a valid statment in Processing.

  • My sketch took about 400,000 random points to estimate Pi to 3 decimal places.

  • You can write the loop you need using a while loop.

    int t = 0;
    while ( t < n ){
      // ... stuff happens here to do the t'th point.
      t++;
    }
    
  • The circle should just fit inside the square so the radius should be width/2 so add a line to setup to set the radius correctly.

    You are using translate to move the drawing origin before drawing the ellipse which according to line 14 is centred at [0,0] so the dist calculation should be dist(randX, randY, 0, 0)

    Change the data type of randX and randY to float so

    float randX = random(-width/2, width/2);
    float randY = random(-height/2, height/2);
    

    Make these changes and add a variable to count the number of points generated and another to count those inside the circle and all you have left is calculate Pi.

  • Make sure you meet the other requirements of your assignment too! You should be doing all this inside a function that returns the approximate value. And that function needs to take the three parameters mentioned. Can you make the drawing conditional on the boolean?

  • edited October 2017

    First, I don't know what c++; does inside the loop. Secondly, if I declare the radius inside setup it won't be recognized by draw(). I don't know how to make a function that counts the amount of points either

    The problem here is that our professor explains processing with very easy examples, but then, our tasks look like this, and most of my classmates have already given up on this subject (we just started using Processing)

    And btw, thank you all so much for your help

  • Answer ✓

    I didn't say declare radiusinside setup I said set its value. You have already declared and initialised radius to 200 in line 2. Change to a declaration

    int radius; // declaration

    an initialise in setup with

    radius = width/2; // initialise after the size() statement

    c++ simply increments the value in c by 1

  • I've tried to fix the thing about coloring the points inside the circle, but i don't know what's wrong. Also I don't know how to create the function to count points.

    int numberOfPoints = 1000; 
    int radius;
    
    void setup() {
      size(770, 770);
      radius = width;
      rectMode(CENTER);
      frameRate(1);
    }
    
    void draw() {
      translate(width/2, height/2);
      noFill();
      strokeWeight(1);
      ellipse (0, 0, radius, radius);
      int randX = (int) random(-width/2, width/2);
      int randY = (int) random(-height/2, height/2);
    
      if (dist(randX,randY,0,0) < radius){ 
        fill(0, 0, 255);
        strokeWeight(5);
        point(randX, randY);
      } else {
        fill(255, 0, 0);
        strokeWeight(5);
        point(randX, randY);
      }
    }
    
  • Look at my last post you got line 6 wrong and line 15 should be

    ellipse(0,0,2*radius,w*radius);

  • Nevermind, it's another 2. My doubt still remains in how to create a function that counts the points inside the circle.

  • And points spawning inside the circle are black instead of blue... I'm quite confused to be honest. Besides that, thanks for your help so far.

  • Use stroke not fill to determine the colour used by point()

  • Answer ✓

    I think you would do well to realize that you should not be generating the points one at a time - You should not just add a new point every time draw() runs.

    Instead, you should write a new function that is going to calculate the approximation. This function can start with just one input: How many points the approximation should use.

    int radius;
    
    void setup() {
      size(770, 770);
      radius = width/2;
    }
    
    void draw() {
      background(0);
      float result = do_approximation( 10000 );
      println( "Result is " + result );
      noLoop();
    }
    
    
    // n is the number of points!
    float do_approximation( int n ) {
      translate(width/2, height/2);
      noFill();
      stroke(255);
      strokeWeight(1);
      ellipse(0, 0, radius, radius);
    
    // This only generates ONE point.
    // The number of points to do is passed to this function as the value of n.
    // You will have to use this value and a loop to do this block of code over and over again.
    // Don't worry about trying to count how many are inside the circle yet.
    // just get this to generate ALL n points.
    
    
    
        int randX = (int) random(-width/2, width/2);
        int randY = (int) random(-height/2, height/2);
        if (dist(randX, randY, 0, 0) < radius) { 
          stroke(0, 0, 255);
          strokeWeight(5);
          point(randX, randY);
        } else {
          stroke(255, 0, 0);
          strokeWeight(5);
          point(randX, randY);
        }
    
    
    
      // You should eventually do some math here.
      // You will need to know a few things...
      // What values do you need to approximate pi?
      // What variables are those values stored in?
    
    
    
      return( 0 ); // Eventually you will want to return your approximation.
    }
    

    Try editing this code (is based off what you already have!) to generate a lot of points.

  • int radius;
    
    void setup() {
      size(770, 770);
      radius = width/2;
    }
    
    void draw() {
      background(0);
      float result = do_approximation( 10000 );
      println( "Result is " + result );
      noLoop();
    }
    
    
    // n is the number of points!
    float do_approximation( int n ) {
      translate(width/2, height/2);
      noFill();
      stroke(0);
      strokeWeight(1);
      ellipse(0, 0, radius, radius);
    
      int a = 0;
      while (a < n) {
        a++;
    
        int randX = (int) random(-width/2, width/2);
        int randY = (int) random(-height/2, height/2);
        if (dist(randX, randY, 0, 0) < radius) { 
          stroke(0, 0, 255);
          strokeWeight(5);
          point(randX, randY);
        } else {
          stroke(255, 0, 0);
          strokeWeight(5);
          point(randX, randY);
        }
      }
    
    
      // You should eventually do some math here.
      // You will need to know a few things...
      // What values do you need to approximate pi?
      // What variables are those values stored in?   
    
      // I need n, which I already have, and I'm missing C, the number of points inside the circle, then radius, widht and height
      // width, and height are processing global variables, radius' value was set in setup, and n, is given at the beginning
    
      // The following won't work since C hasn't been declared yet. 
      //float piApprox = (C * width * height)/(n * radius * radius); 
      return( piApprox ); // Eventually you will want to return your approximation.
    }
    

    I also don't really get what my professor means by a boolean variable that determines if the simulation must be drawn. But step by step, now I need to count the points inside the circle, those are the ones which dist(randX, randY, 0, 0) < radius), but I don't know how to continue from here. Thank you so much!!

  • Right, so you only need to work out the value for c now, which is the number of (BLUE!) points inside the circle. So you need a counter variable. Start counting at 0, and every time you draw a blue point (what code does that?), also increase the counter (counter++; which is the same as counter = counter + 1;). After all the points are drawn, since the counter will only have increased when you drew blue - and not red - points, the counter will have counted how many blue points there are... which is c, the value you need!

  • Ok, I added an if, inside the while loop. This is the code right now,

    float do_approximation( int n ) {
      translate(width/2, height/2);
      noFill();
      stroke(0);
      strokeWeight(1);
      ellipse(0, 0, radius, radius);
    
      int a = 0;
      while (a < n) {
        a++;
        float randX =  random(-width/2, width/2);
        float randY =  random(-height/2, height/2);
        if (dist(randX, randY, 0, 0) < radius) { 
          stroke(0, 0, 255);
          strokeWeight(5);
          point(randX, randY);
        } else {
          stroke(255, 0, 0);
          strokeWeight(5);
          point(randX, randY);
        }
        int c = 0; 
        if (dist(randX, randY, 0, 0) < radius) {
           float C = c + 1; 
        }
      }
    
      float piApprox = (C * width * height)/(n * radius * radius); 
      return( piApprox ); // Eventually you will want to return your approximation.
    }
    
  • edited October 2017

    So you want the counter variable, c, to be set to 0 every time the loop runs? Nope! You want to define the variable BEFORE the loop, and then only increment it (and NOT REDEFINE IT!) inside the conditional that... you already had!

    Basically, move line 22 to before the loop. This defines your counter variable, and it starts at 0. Then get rid of lines 23 to 25. You are already checking that condition on line 13. Just increment the counter inside that block. Put it right after you have drawn the blue point.

    Also, don't use c and also C. They are DIFFERENT VARIABLES and it's SUPER F%^&*ING CONFUSING. Just have one variable called c. Or counter. Or number_of_points_inside_circle. Pick a name that you can understand what it's used for and use it.

  • Alright, that's true, by defining the variable inside the loop, it'd be set to 0 every time it runs. I've done it. Now I need a variable to store the value of c++, so that I can use it inside piApprox, but if I declare it inside the if, i won't be able to use it outside that block, should I declare it globally?

    float do_approximation( int n ) {
      translate(width/2, height/2);
      noFill();
      stroke(0);
      strokeWeight(1);
      ellipse(0, 0, radius, radius);
    
      int a = 0;
      int c = 0;
      while (a < n) {
        a++;
        float randX =  random(-width/2, width/2);
        float randY =  random(-height/2, height/2);
        if (dist(randX, randY, 0, 0) < radius) { 
          stroke(0, 0, 255);
          strokeWeight(5);
          point(randX, randY);
          c++;
        } else {
          stroke(255, 0, 0);
          strokeWeight(5);
          point(randX, randY);
        }
      }
    
      float piApprox = (C * width * height)/(n * radius * radius); 
      return( piApprox ); 
    }
    
  • You can still use the value of c on line 26! But make sure you use c (lowercase), not C(uppercase)! See how confusing it is when you mix cases in variables? Stick to variables in lowercase_only.

  • edited October 2017
    float piApprox = (c * width * height)/(n * radius * radius); 
    return( piApprox ); 
    

    Now I'm using lowercase c, and it says that the result is 0.0...

  • Please, according to your advice everything should work out, but it doesn't. Please check it out please.

    int radius;
    void setup() {
      size(770, 770);
      radius = width/2;
    }
    
    void draw() {
      background(0);
      float pi = do_approximation( 10000 );
      println( "Pi approximation is " + pi );
      noLoop();
    }
    
    float do_approximation( int n ) {
      translate(width/2, height/2);
      noFill();
      stroke(0);
      strokeWeight(1);
      ellipse(0, 0, radius, radius);
    
      int a = 0;
      int c = 0;
      while (a < n) {
        a++;
        float randX =  random(-width/2, width/2);
        float randY =  random(-height/2, height/2);
        if (dist(randX, randY, 0, 0) < radius) { 
          stroke(0, 0, 255);
          strokeWeight(5);
          point(randX, randY);
          c++;
        } else {
          stroke(255, 0, 0);
          strokeWeight(5);
          point(randX, randY);
        }
      }
    
      float piApprox = (c * width * height)/(n * radius * radius); 
      return( piApprox ); 
    }
    
  • are we sure the formula in line 39 is good?

    int radius;
    void setup() {
      size(770, 770);
      radius = width/2;
    }
    
    void draw() {
      background(0);
      float pi = do_approximation( 10000 );
      println( "Pi approximation is " + pi );
      noLoop();
    }
    
    float do_approximation( int n ) {
    
      translate(width/2, height/2);
    
      noFill();
      stroke(0);
      strokeWeight(1);
      ellipse(0, 0, radius, radius);
    
      int a = 0;
      int c = 0;
    
      while (a < n) {
    
        a++;
        float randX =  random(-width/2, width/2);
        float randY =  random(-height/2, height/2);
    
        if (dist(randX, randY, 0, 0) < radius) { 
          stroke(0, 0, 255); // blue
          c++;
        } else {
          stroke(255, 0, 0); // red = outside
        }
        // output 
        strokeWeight(5);
        point(randX, randY);
      }
    
      println("done while-loop: "+c);
    
      float piApprox = map(c, 0, n, 0, 4); //   (c * (width * height)) / (n * (radius * radius)); 
      return( piApprox );
    }//
    //
    
  •    float piApprox = map(c, 0, n, 0, 4); //   (c * (width * height)) / (n * (radius * radius)); 
      return( piApprox );
    }
    

    What does this line you've added do??

  • edited October 2017

    you haven't answered my question:

    are we sure the formula in line 39 is good?

    Please do. Where does the formula come from, what does it mean, how does it work?

    Your question:

    You asked

    What does this line you've added do??

    I haven't added the line. I changed it. Look at map() in the reference to find out ;-)

  • Somehow I managed to make it work. the formula in 39 is correct, and it's been provided by my professor. Let me explain. Area of the circle/area of the square(graphic window) = blue points/total points

    Expressed the way we want in processing, it translates to:

    (Pi * radius * radius)/(width * height) = c/n

    Isolating Pi, we get:

    Pi = (c * width * height)/(n * radius * radius) My code looks like this right now.

    int radio;                                                   
    void setup() {
      size(770, 770);                                             
      radio = width/2;                                            
    }
    
    void draw() {
      background(0);                                              
      float pi = aproximaPi( 450000 );                            
      println( "El valor aproximado de π es " + pi );             
      noLoop();                                                     
    }                                                              
    
    float aproximaPi( int n ) {                                   
    
      translate(width/2, height/2);                                
    
      noFill();                                                   
      stroke(0);                                                  
      strokeWeight(1);                                            
      ellipse(0, 0, radio, radio);                                
    
    
      int a = 0;                                                  
      float c = 0;                                                
    
      while (a < n) {                                             
    
        a++;                                                      
        float randX =  random(-width/2, width/2);                  
        float randY =  random(-height/2, height/2);               
    
        if (dist(randX, randY, 0, 0) < radio) {                   
          stroke(0, 0, 255);                                      
          c++;                                                    
        } else {
          stroke(255, 0, 0);                                      
        }
        strokeWeight(5);                                          
        point(randX, randY);                                      
      }
    
      float piAproximado = (c * width * height)/(n * sq(radio));  
      return( piAproximado );                                     
    }
    

    Variables' names are now in Spanish, I'm from Spain. There's one thing missing to complete the assignment

    The objective is to build a function that returns the apprx value of PI, given 3 parameters:

    • N (number of points "thrown")
    • r (radius of the circumference)
    • A boolean variable that determines if the simulation must be drawn or not.
  • when you use text() for output, PI is visible when you use this:

      text( "El valor aproximado de "+
        char(960)
        +" es " + pi, 100, 200);     
    

    (with println I see a ? instead of PI)

    The other stuff you can solve alone I think

  • I don't really know what I have to do with what he says about using a boolean variable that determines if the simulation must be drawn or not.

  • edited October 2017 Answer ✓

    you want to pass more parameter to the function:

    float aproximaPi( int n ) {

    becomes

    float aproximaPi( int n, float r, boolean drawMe  ) {    
    

    OK? Change the call of aproximaPi accordingly. Use r.

    and then use this boolean

    to switch on/off this:

        strokeWeight(5);                                          
        point(randX, randY);  
    
  • I have to go now.

    See you

  • Thank you so much for your help. This task is now finished, and all the objectives set by my professor have been completed. Thank you to everyone who contributed to make this possible, and thank you for giving me step by step solutions, that would help me understand and do things by myself.

  • Oh no, no, no. You don't just get to say everything is good and then leave. POST YOUR CODE. The final version. That works. So we can see it.

  • edited October 2017
    void setup() {
      size(770, 770);                                             
    }
    
    void draw() {
      background(0);                                              
      float pi = aproximatePi( 100000, width/2, true );            
      println( "The approximate value of π is " + pi );             
      noLoop();                                                    
    }                                                             
    
    float aproximatePi( int n, int radius, boolean drawSimulation ) { 
    
      translate(width/2, height/2);                               
    
      noFill();                                                   
      stroke(0);                                                  
      strokeWeight(1);                                            
      ellipse(0, 0, radius, radius);                                
    
    
      int a = 0;                                                  
      float c = 0;                                                
    
      while (a < n) {                                              
    
        a++;                                                      
        float randX =  random(-width/2, width/2);                 
        float randY =  random(-height/2, height/2);               
    
        if (dist(randX, randY, 0, 0) < radius) {               
          stroke(0, 0, 255);                                  
          c++;                                                
        } else {
          stroke(255, 0, 0);                                  
        }
        if (drawSimulation == true) {          
        strokeWeight(5);                         
        point(randX, randY);                      
        }  
    }
    
    
      float apprxPi = (c * width * height)/(n * sq(radius));  
      return( apprxPi );                                      
    }
    
  • Neat! Now that you have it working, I can post my version:

    int rn;
    float rr;
    boolean rb;
    
    void setup() {
      size(400, 400);
      textSize(16);
      reset();
    }
    
    void reset() {
      rn = int(random(1, 10000));
      rr = random(10, min(width/2, height/2));
      rb = random(1) < 0.5;
    }
    
    void draw() {
      background(0);
      axpimc( rn, rr, rb );
      noLoop();
    }
    
    float axpimc(int n, float r, boolean b) {
      String error = "";
      fill(255);
    
      if (r > min(width, height)) { 
        error = "R too big!";
      }
      if (n <= 0 ) { 
        error = "N too small!";
      } 
      fill(255);
      if ( error.length() > 0 ) {
        Text( "Error: " + error, 20, 80 );
        return(0);
      }
    
      int c = 0;
      float a = width * height;
      float x, y;
    
      for ( int i = 0; i < n; i++) {
        x = random(width);
        y = random(height);
        if ( dist( x, y, width/2, height/2) < r ) {
          c++;
          if (b) {
            stroke(0, 0, 255);
            point(x, y);
          }
        } else {
          if (b) {
            stroke(255, 0, 0);
            point(x, y);
          }
        }
      }
      if (b) {
        noFill();
        stroke(255);
        ellipse(width/2, height/2, 2*r, 2*r);
      }
      float p = (c*a)/(n*r*r);
      Text("N = " + n, 20, 20);
      Text("R = " + r, 20, 40);
      Text("B = " + b, 20, 60);
      Text("PI ~= " + p, 20, 80);
      return( p );
    }
    
    void mousePressed() {
      reset();
      redraw();
    }
    
    void Text(String t, float x, float y) {
      fill(0);
      for ( int dx = -2; dx<3; dx++) {
        for ( int dy = -2; dy<3; dy++) {
          text(t, x+dx, y+dy);
        }
      }
      fill(255);
      text(t, x, y);
    }
    
  • edited October 2017

    a few errors in the code of MiguelNavarro

    Did you try different values for radius, e.g. 100? Because a lot of your code assumes radius is always width/2. E.g. in your line 44 above is width and height which is wrong. Also lines 28 and 29.

    You need to use ctrl-t to auto-format

Sign In or Register to comment.