Munindra Naik
Munindra Naik

Reputation: 33

Calculating Pi using Monte Carlo method in parallel

I have been trying to calculate the value of pi using Monte Carlo simulation in parallel.

So far, I have created threads, and generated the random points, but I'm not able to return those generated points back from the threads to the main thread.

How to tackle this problem ?

Below is the code I have written

import java.util.*;

class MyThread extends Thread{
    int points;
    int insidePoints;

    public MyThread(int insidePoints, int points){
      this.points = points;
      this.insidePoints = insidePoints; 
    }
    
    // simulating monte carlo to generate points and check which are inside the circle
    public void run(){
          int prec = 1000000;
          Random rand = new Random();
          for( int i=0 ; i < points ; ++i){
              double x = (double)rand.nextInt(prec+1)/(double)prec;
              double y = (double)rand.nextInt(prec+1)/(double)prec;

             if( (x*x + y*y) <= 1){
                ++insidePoints;
             }
         }
         System.out.println("Thread " + Thread.currentThread().getId() + " running and Inside points are" + insidePoints);
    }

    public int getCount(){
         return this.insidePoints;
    }
}

public class A{
       
    public static void main(String[] args){

        int totalPoints = 1000000;
            int insidePoints = 0;
            int threadsToBeUsed = 1;

            Scanner scan = new Scanner(System.in);
            System.out.println("Enter the value of Threads : ");
            threadsToBeUsed = scan.nextInt();
            
             try{
                int eachThreadPoints = totalPoints/threadsToBeUsed;
                Thread[] thread = new Thread[threadsToBeUsed];

                for(int i = 0; i < threadsToBeUsed ; ++i){
                    if( i == (threadsToBeUsed-1)){
                       eachThreadPoints += (totalPoints)%eachThreadPoints ;
                    }    
                 MyThread myT = new MyThread(insidePoints, eachThreadPoints);
                 thread[i] = new Thread(myT);
                 thread[i].start();
              }

              for( int  i=0; i < threadsToBeUsed; ++i)
                 thread[i].join();

               System.out.println("Number of inside points :" + insidePoints);
               System.out.println("Pi/4 = " + (double)insidePoints/(double)totalPoints);
               System.out.println("Pi   = " + 4*(double)insidePoints/(double)totalPoints);

               } catch(Exception e){
                   System.out.println(e);
                   System.exit(1);
               }
          }
}

Upvotes: 3

Views: 1301

Answers (1)

dreamcrash
dreamcrash

Reputation: 51443

As an alternative approach, you can use Java Executor Interface, which offers a higher-level abstraction than handling the threads explicitly. Moreover, you might consider the use of the ThreadLocalRandom:

A random number generator isolated to the current thread. Like the global Random generator used by the Math class, a ThreadLocalRandom is initialized with an internally generated seed that may not otherwise be modified. When applicable, use of ThreadLocalRandom rather than shared Random objects in concurrent programs will typically encounter much less overhead and contention. Use of ThreadLocalRandom is particularly appropriate when multiple tasks (for example, each a ForkJoinTask) use random numbers in parallel in thread pools.

Back to your question:

but I'm not able to return those generated points back from thread to main function.

You can take advantage of the Executor interface and submit a task that returns a Java Future:

A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.

Each thread computes the number of points inside and returns that value as a Future to the main thread. The main thread, in turn, sums all the values returned by the other threads.

So code-wise, one would create a pool of threads

ExecutorService pool = Executors.newFixedThreadPool(threadsToBeUsed);

then a list to collect the results

    List<Future<Integer>> results = new ArrayList<>(threadsToBeUsed);

and then submit the work to be done in parallel

   for(int i = 0; i < threadsToBeUsed; i++){
       Future<Integer> insidePointsThr = pool.submit(new task(eachThreadPoints));
       results.add(insidePointsThr);
   }

Make the main thread sum all the results from the other threads:

  int insidePoints = results.stream().mapToInt(f -> f.get()).sum();

Print the results, and shutdown the pool of threads:

pool.shutdown();

A running code example (based on the code that you have provided):

import java.util.*;
import java.util.concurrent.*;

class task implements Callable<Integer> {
    final int points;

    public task(int points){
        this.points = points;
    }

    @Override
    public Integer call() {
        int insidePoints = 0;
        int prec = 1000000;
        for( int i=0 ; i < points ; ++i){
            double x = (double)ThreadLocalRandom.current().nextInt(prec + 1)/(double)prec;
            double y = (double)ThreadLocalRandom.current().nextInt(prec + 1)/(double)prec;
            if( (x*x + y*y) <= 1){
                ++insidePoints;
            }
        }
        System.out.println("Thread " + Thread.currentThread().getId() + " running and Inside points are " + insidePoints);
        return insidePoints;
    }
}

class A{
    public static void main(String[] args){
        int totalPoints = 1000000;
        Scanner scan = new Scanner(System.in);
        System.out.println("Enter the value of Threads : ");
        int threadsToBeUsed = scan.nextInt();

        int eachThreadPoints = totalPoints/threadsToBeUsed;
        ExecutorService pool = Executors.newFixedThreadPool(threadsToBeUsed);
        List<Future<Integer>> results = new ArrayList<>(threadsToBeUsed);
        for(int i = 0; i < threadsToBeUsed; i++){
            Future<Integer> insidePointsThr = pool.submit(new task(eachThreadPoints));
            results.add(insidePointsThr);
        }
        int insidePoints = results.stream().mapToInt(A::getFutureResult).sum();

        System.out.println("Number of inside points :" + insidePoints);
        System.out.println("Pi/4 = " + (double)insidePoints/(double)totalPoints);
        System.out.println("Pi   = " + 4*(double)insidePoints/(double)totalPoints);

        pool.shutdown();
    }

    private static int getFutureResult(Future<Integer> f) {
        try {
            return f.get();
        } catch (InterruptedException | ExecutionException e) {
            // handle the error
        }
        return 0;
    }
}

Output (for 4 threads):

Enter the value of Threads : 
4
Thread 16 running and Inside points are 196447
Thread 17 running and Inside points are 196147
Thread 15 running and Inside points are 196076
Thread 14 running and Inside points are 196795
Number of inside points :785465
Pi/4 = 0.785465
Pi   = 3.14186

Upvotes: 2

Related Questions