有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用Java计算PI。错误值

我试图使用Gregory-Leibniz算法来计算PI,但我总是只使用多线程来得到错误的值。单线程工作正常

我认为问题在于共享的k值,计算混乱

错误的PI值:

Select an option: 4 How many points? 100000 How many threads? 32 Concurrent Gregory-Leibniz estimated PI value : 2.7663972054374577. Executed in 121.578657 ms.

需要帮忙吗

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * The Class GregoryLeibniz.
 */
public class GregoryLeibniz {

    /** The in circle. */
    private double factor;

    /** The sum. */
    private double sum;

    /** The points. */
    private long points;

    /** The n processors. */
    private int nProcessors;

    private long k;


    /**
     * Instantiates a new gregory leibniz.
     *
     * @param points the points
     * @param nProcessors the n processors
     */
    public GregoryLeibniz(long points, int nProcessors) {
        super();
        this.points = points;
        this.nProcessors = nProcessors;
    }


    /**
     * The Class GregoryLeibnizImpl.
     */
    public class GregoryLeibnizImpl implements Runnable {   

        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            if(k % 2 == 0) factor = 1.0;
            else factor = -1.0;

            sum += factor / (2*k +1);
        }
    }

    /**
     * Calculate PI.
     *
     * @return the double
     */
    public double calculatePI() {
        ExecutorService executor = Executors.newWorkStealingPool(nProcessors);

        for (k = 0; k < points; k++) {
            Runnable worker = new GregoryLeibnizImpl();
            executor.execute(worker);
        }

        executor.shutdown();

        while(!executor.isTerminated()) { }

        return 4.0 * sum;
    }
}

共 (3) 个答案

  1. # 1 楼答案

    尝试同步运行方法。如果你让k没有互斥,一个线程将检查k的模,设置因子,然后它将进入睡眠状态,下一个线程将检查模集(相同)因子,执行sum+=填充,然后进入睡眠状态。在那之后,第一个线程还将使用旧因子进行sum+=运算

  2. # 2 楼答案

    你需要给你的员工一份k。除此之外,让员工只做一步,并不能真正赢得很多。创建的工作线程不应超过可用的CPU(核心),并为每个工作线程分配一部分可计算的总数

    还请注意,这仍然会给您留下一个错误的sum,因为如果两个工作人员同时对其进行读写,其中一个结果将丢失

    让每个工作人员维护一个私有sum,然后在完成所有操作后,在主线程中将它们的私有总和相加

    比如:

    public class GregoryLeibnizImpl implements Runnable {
        private int start;
        private int end;
        private int sum;
    
        public GregoryLeibnizImpl(int start, int end) {
            this.start = start;
            this.end = end;
        }
    
        @Override
        public void run() {
            // loop from start to end using your sum algorithm
        }
    }
    

    然后将循环更改为以下内容:

        for (int k = 0; k < points; k += points / numthreads) {
            Runnable worker = new GregoryLeibnizImpl(k, k + points / numthreads);
            executor.execute(worker);
        }
    

    您需要将这些worker实例保存在某个位置,例如一个列表,以便在完成后收集它们的结果

  3. # 3 楼答案

    GregoryLibNizimpl的每个实例都需要独立运行。或者你需要一个互斥。或者两者兼而有之

    1. GregoryLibNizimpl需要将“k”作为构造函数参数,并将其存储为成员变量

    2. sum周围需要一个互斥/保护。否则,需要在calculatePI函数末尾“汇总”工作线程对象的所有结果

    这一行:

    while(!executor.isTerminated()) { }
    

    会烧掉整个内核并破坏代码的性能。改用awaitTermination方法

    更新

    我需要一些实践,所以我重组了你的代码,以获得一个有价值的解决方案。也许我能帮忙

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * The Class GregoryLeibniz.
     */
    public  class GregoryLeibniz {
    
        /** The n processors. */
        private int nProcessors;
        private long points;
        private long itemsPerThread;
        private long itemsInFirstThread;
    
        /**
         * Instantiates a new gregory leibniz.
         *
         * @param points the points
         * @param nProcessors the n processors
         */
        public GregoryLeibniz(long points, int nProcessors) {
            this.points = points;
            this.nProcessors = nProcessors;
            this.itemsPerThread = this.points / this.nProcessors;
            this.itemsInFirstThread += this.itemsPerThread + this.points - this.itemsPerThread * this.nProcessors;
        }
    
    
        /**
         * The Class GregoryLeibnizImpl.
         */
        public class GregoryLeibnizImpl implements Runnable {
    
            /* (non-Javadoc)
             * @see java.lang.Runnable#run()
             */
    
            long start;
            long end;
            public double result;
    
            public GregoryLeibnizImpl(long start, long end)
            {
                this.start = start;
                this.end = end;
            }
    
            @Override
            public void run() {
                int factor = ((start % 2)!=0) ? -1 : 1;
                for (long i = start; i <= end; i++) {
    
                    result += factor / (double)(i*2+1);
                    factor *= -1;
                }
            }
        }
    
        /**
         * Calculate PI.
         *
         * @return the double
         */
        public double calculatePI() {
            ExecutorService executor = Executors.newWorkStealingPool(nProcessors);
    
            long start = 1;
            long end = itemsInFirstThread;
    
            GregoryLeibnizImpl [] workers = new GregoryLeibnizImpl[this.nProcessors];
    
            for (int t = 0; t < this.nProcessors; t++) {
                GregoryLeibnizImpl worker = new GregoryLeibnizImpl(start, end);
                workers[t] = worker;
                executor.execute(worker);
                start += this.itemsPerThread;
                end += this.itemsPerThread;
            }
    
            executor.shutdown();
    
            while (executor.isTerminated() == false) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
    
            double result = 0;
            for (int t = 0; t < this.nProcessors; t++) {
                result += workers[t].result;
            }
    
            result += 1;
            result *= 4;
    
            return result;
    
        }
    
        public static void main(String [] args) {
    
            var gl = new GregoryLeibniz(1000000, 4);
            double d = gl.calculatePI();
            System.out.println(d);
        }
    
    }