欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

并行任务求解:数豆子求PI(3.1415926...)

程序员文章站 2022-06-22 16:20:17
...

问题描述:给定一个边长2r的正方形,有这样一个圆:以正方形对角线交点为圆点,以r为半径。现在在正方形区域内随机撒豆子,落在圆内的豆子数为k个,落在正方形内的豆子数为n个,根据以上给定的信息编程求PI,要求用并发(多线程)提高解题效率。

解:初步分析:

由已知信息可以得出:

        并行任务求解:数豆子求PI(3.1415926...)   (Sc表示圆的面积、Ss表示正方形的面积

所以:

        并行任务求解:数豆子求PI(3.1415926...)

得到:

        并行任务求解:数豆子求PI(3.1415926...)

现在的问题已经被简化了,我们只需要知道k和n的比值,设n为1000000,也就是说知道了k的值,就能算出PI

一般编程思路:

首先的问题是需要一个函数来判断豆子在不在圆内,定义方法boolean inCirCle(),那这个方法需要形式参数吗?其实不需要,这个方法需要的两个条件都可以拟定:

①:豆子的坐标:(x, y);

②:圆的位置。

由于PI的求解不需要考虑r,所以设r = 1,圆心的坐标为(0, 0),而豆子的落点是随机的,所以可以用两个随机数x,y来表示豆子位置,x,y的取值范围应该是-1~1,此时解决了inCircle的条件,剩下的都好解决了,判断豆子在不在圆内其实就是判断:

并行任务求解:数豆子求PI(3.1415926...) 的值是不是真,若为真则在圆内,若不为真则在圆外、正方形之内,这样就可以给出inCircle()方法了:

public boolean inCircle(Random random){
    //random是产生随机数的对象
    double x = random.nextDouble();    //产生0-1的随机数,因为x*x,所以正负无所谓
    double y = random.nextDouble();

    if ((x*x + y*y) < 1){
        return true;
    }
    return false;
}

现在就是得到k的值了,循环n次数豆子在不在圆内,若在k++,若不在continue

public int getK(){
    for (int i=0; i<1000000; i++){
        if (inCircle())
            k++;
    }
    return k;
}

最后套公式算出PI的值。

存在问题:浪费时间,效率太差,占用cpu,基于以上问题我们提出并行求解PI。

并行求解思路:

将原本一个主线程做的所有相同的工作分给多个线程共同完成来提高效率,如下图:

并行任务求解:数豆子求PI(3.1415926...)

那么如何实现?

定义一个BeanThread

/**
 * 数豆子线程
 * 每个线程数1000000/10 = 100000个豆子
 */
class BeanThread extends Thread {
    private int k = 0;  //落在圆里的豆子个数
    private Random random = new Random();
    private double x;   //(x,y)是豆子落下的坐标
    private double y;

    /**
     * 数豆子线程主要执行的程序
     */
    public void run(){
        for (int i=0; i<100000; i++){
            if (inCircle()){
                k++;
            }
        }
    }

    /**
     * 判断豆子在不在圆内
     * @return
     */
    public boolean inCircle(){
        double x = random.nextDouble(); //0-1(负的无所谓)
        double y = random.nextDouble();

        if ((x * x + y * y) <= 1){
            return true;
        }
        return false;
    }

    public int getK() {
        return k;
    }
}

可以看出一个副线程做的工作和主线程一般无二,那么此时可以用主线程开始创建副线程调用它们的start()方法了,但这里存在一个线程的同步问题:如何保证在每个副线程都执行完毕后主线程才开始对各个副线程的k求和然后求PI?如果主线程在有副线程还没执行完的前提下就开始执行,那sum_k的值肯定不准确。其实这里可以用到各个线程的join()方法,它的作用是等待这个线程执行完毕(死亡),那么就可以在开始各个线程的工作后再由主线程调用各个线程的join方法,具体理解我画图给出:

并行任务求解:数豆子求PI(3.1415926...)

根据以上分析我给出主线程的代码:

public class 并行任务求解_数豆子求PI {
    /**
     * 主线程
     * 假设我们要数的豆子有10000,分给10个线程完成
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        BeanThread[] beanThreads = new BeanThread[10];
        int k_sum = 0;  //各个数豆子线程得到的k的和

        //创建10个线程
        for (int i=0; i<10; i++){
            beanThreads[i] = new BeanThread();
        }

        //启动线程
        for (int i=0; i<10; i++){
            beanThreads[i].start();
        }

        //同步线程
        for (int i=0; i<10; i++){
            beanThreads[i].join();  //等待这个线程死亡(结束)
        }

        //获得各个线程的k并求和
        for (int i=0; i<10; i++){
            k_sum += beanThreads[i].getK();
        }

        //求PI
        double PI = (4.0 * k_sum) / 1000000.0;
        System.out.println(PI);
    }
}

到此,所有问题都解决了,感谢观看!0.0

相关标签: Java基础