Рассчитать PI в Java. Неверное значение - PullRequest
1 голос
/ 17 июня 2019

Я пытаюсь вычислить PI, используя алгоритм Gregory-Leibniz, но я всегда получаю неправильные значения, только используя многопоточность. Одиночная нить работает нормально.

Я думаю, что проблема заключается в общем значении k, и вычисление не работает.

Неверное значение PI:

Выберите вариант: 4 Сколько очков? 100000 Сколько потоков? 32 Одновременно Григорий-Лейбниц оценил значение PI: 2,7663972054374577. Выполнено в 121.578657 мс.

Любая помощь, пожалуйста?

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 ]

2 голосов
/ 17 июня 2019

Каждый экземпляр GregoryLeibnizImpl должен работать независимо.Или вам нужен мьютекс.Или оба.

  1. GregoryLeibnizImpl должен принять «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);
    }

}
0 голосов
/ 17 июня 2019

Вы должны дать своему работнику копию k. Кроме того, вы не очень много выигрываете, позволяя своим работникам делать только один шаг в сумме. Вы должны создать не больше рабочих, чем у вас есть доступные процессоры (ядра), и дать каждому часть суммы для расчета.

Обратите также внимание, что это все еще оставляет вас с ошибкой 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);
    }

Вам необходимо хранить эти рабочие экземпляры где-нибудь, например, список, чтобы вы могли собрать их результаты, как только это будет сделано.

0 голосов
/ 17 июня 2019

Попробуйте синхронизировать метод запуска. Если вы разрешите k без мьютекса, один поток проверит модуль k, задает коэффициент, а затем заснет, а следующий поток проверит установленный (такой же) коэффициент модуля, выполнит sum + = вещи и уснет. После этого первый поток также выполнит sum + = вещи со старым фактором.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...