Почему java дает странные результаты при попытке узнать значение pi с помощью java.math.BigInteger?

Я пытаюсь вычислить значение пи, подсчитав количество точек, которые могут быть на окружности, используя уравнение:

Если точка окружности равна (cx,cy), а радиус равен r:
, тогда r2 = cx2+cy2

Но когда я реализую это в java, используя следующий код:

package ani.pi.gen;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 *
 * @author ani
 */
public class Engine {
private BigInteger x,pimag;
private final BigInteger radius;
public Engine(int digits){
    radius=new java.math.BigInteger("10").pow(digits);
    pimag=new BigInteger("0");
    x=new BigInteger("-"+radius);
    System.out.println(x+" "+radius);
}
public int percent=0;
private int lp=0;
public void start(){
    Thread t=new Thread(() -> {
        for(;x.compareTo(radius)<=0;x=x.add(new BigInteger("1"))){
            System.out.println(x.pow(2));
            for(BigInteger y=new BigInteger(""+radius);y.compareTo(radius)>=0;y=y.subtract(new BigInteger("1"))){
                if(x.pow(2).add(y.pow(2)).equals(radius.pow(2))){
                    pimag=pimag.add(new BigInteger("1"));
                    System.out.println(pimag+" "+x+" "+y);
                }
            }
            percent=(x.add(radius).divide(radius.multiply(new BigInteger("2")))).multiply(new BigInteger("100")).intValueExact();
            if(percent!=lp){
                System.out.println(percent+"%");
                lp=percent;

            }

        }
        System.out.println("Pi -> "+new BigDecimal(pimag.toString()).divide(new BigDecimal(radius+"")));
    });
    t.start();

}
}

Когда я запускаю программу, используя код:

new Engine(2).start();

Я получаю следующий вывод:

бежать:

-100 100 10000 9801 9604 9409 9216 9025 8836 8649 8464 8281 8100 7921 7744 7569 7396 7225 7056 6889 6724 6561 6400 6241 6084 5929 5776 5625 5476 5329 5184 5041 4900 4761 4624 4489 4356 4225 4096 3969 3844 3721 3600 3481 3364 3249 3136 3025 2916 2809 2704 2601 2500 2401 2304 2209 2116 2025 1936 1849 1764 1681 1600 1521. 9 4 1 0 1 0 100 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784. 1936 2025 2116 2209 2304 2401 2500 2601 2704 2809 2916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4356 4489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6241 6400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8464 8649 8836 9025 9216 9409 9604 9801 10000 100% Pi -> 0,01 УСПЕШНОЕ ПОСТРОЕНИЕ (всего время: 0 секунд)

В котором указано, что значение числа пи равно 0,01.

В чем может быть проблема и как ее решить?


person Aniruddha Sarkar    schedule 17.12.2015    source источник
comment
Я не вижу, чтобы переменная pimag когда-либо использовалась таким образом, чтобы в вашем коде для нее было установлено значение Pi.   -  person Tim Biegeleisen    schedule 17.12.2015
comment
В самом деле, вы, кажется, допустили большой скачок между опубликованным вами уравнением и вычислением числа пи. Если вы считаете, что BigInteger ведет себя странно, возможно, вы имеете в виду конкретное вычисление?   -  person Jon Skeet    schedule 17.12.2015
comment
(Кроме того, я бы также предложил использовать пробелы и константы для часто используемых значений BigInteger, чтобы значительно повысить удобочитаемость. И я не вижу причин использовать потоки только для демонстрации этой проблемы.)   -  person Jon Skeet    schedule 17.12.2015
comment
pimag увеличивается каждый раз, когда выполняется уравнение r^2 = cx^2+cy^2.   -  person Aniruddha Sarkar    schedule 17.12.2015
comment
так какое другое уравнение я могу использовать для вычисления числа пи? кажется, что это уравнение не может быть реализовано с помощью BigInteger, или я не знаю, как это сделать.   -  person Aniruddha Sarkar    schedule 17.12.2015
comment
Ваш подход не сработает. Представьте, что у вас есть лист миллиметровой бумаги, и вы рисуете на нем круг. Если я правильно понимаю ваш алгоритм, он подсчитает количество точек, в которых окружность точно проходит через точку пересечения вертикальной и горизонтальной линий. И их будет не так много. Вы подсчитываете количество целочисленных решений уравнения x^2+y^2=r^2, где задано r, и количество решений не будет иметь никакого отношения к длине окружности. (Пример: если r=5, то есть 8 решений, если r=6, то только 4.)   -  person ajb    schedule 17.12.2015
comment
@aniruddha.sarkar вам нужно точное число пи? Ищите формулы на странице en.wikipedia.org/wiki/Approximations_of_%CF%80   -  person Developer Marius Žilėnas    schedule 17.12.2015
comment
На самом деле, я никогда не слышал, чтобы кто-нибудь пытался вычислить число пи, подсчитав количество точек на окружности (что в любом случае не имеет смысла, поскольку количество точек бесконечно). Я не думаю, что есть способ заставить это работать.   -  person ajb    schedule 17.12.2015
comment
Если r=5, то решений 12, а не 8. Моя ошибка.   -  person ajb    schedule 17.12.2015
comment
@aniruddha.sarkar Ты почти понял. Посмотрите на второй, он должен быть for (BigInteger y = new BigInteger("" + radius); y.compareTo(new BigInteger("-"+radius)) >= 0; y = y.subtract(new BigInteger("1"))). Посмотрите на строку if(x.pow(2).add(y.pow(2)).equals(radius.pow(2))){ она должна содержать compareTo так как надо брать те которые лежат внутри круга, а не только на круге. Должно быть if (x.pow(2).add(y.pow(2)).compareTo(radius.pow(2)) <= 0). А площадь рассчитывается из System.out.println("Pi -> " + new BigDecimal(pimag.toString()).divide(new BigDecimal(radius.pow(2) + "")));. :)   -  person Developer Marius Žilėnas    schedule 17.12.2015


Ответы (1)


Ты почти понял. Посмотрите на второй for он должен быть

for (BigInteger y = new BigInteger("" + radius); 
     y.compareTo(new BigInteger("-"+radius)) >= 0; 
     y = y.subtract(new BigInteger("1")))

Посмотрите на строку

if(x.pow(2).add(y.pow(2)).equals(radius.pow(2))){

он должен содержать compareTo так как надо брать те что лежат внутри круга, а не только на круге. Должно быть

if (x.pow(2).add(y.pow(2)).compareTo(radius.pow(2)) <= 0). 

Площадь должна быть рассчитана с помощью radius.pow(2)

System.out.println("Pi -> " + new BigDecimal(
                                 pimag.toString()).divide(
                                 new BigDecimal(radius.pow(2) + "")));

Я подправил ваше решение. :)

package javaapplication38;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

public class JavaApplication38
{

    public static void main(String[] args)
    {        
        new Engine(BigInteger.TEN.pow(2)).start();
    }

    public static class Engine
    {        
        final BigInteger radius    ;
        final BigInteger negRadius ;
        final BigInteger r2        ; //The force awakens on December 18, 2015.

        public static final BigInteger TWO = new BigInteger("2");    

        public Engine(BigInteger radius)
        {
            this.radius = radius;
            negRadius   = radius.negate();
            r2          = this.radius.pow(2);
        }

        public Engine(int radius)
        {
            this(new BigInteger("" + radius));
        }      

        public void start()
        {
            Runnable r = new Runnable()
            {
                public void run()
                {                    
                    BigInteger area = BigInteger.ZERO; //area of the circle

                    //calculate circle's area
                    //let's walk through all the points (x;y)
                    for (BigInteger x = new BigInteger(negRadius.toString());
                            x.compareTo(radius) <= 0; 
                            x = x.add(BigInteger.ONE))
                    {                        
                        for (BigInteger y = new BigInteger(negRadius.toString());
                                y.compareTo(radius) <= 0;
                                y = y.add(BigInteger.ONE))
                        {
                            //System.out.format("Point %s, %s%n", x.toString(), y.toString());
                            //if square is in the circle add square's area to the area of the circle
                            if (x.pow(2).add(y.pow(2)).compareTo(r2) <= 0)
                            {
                                area = area.add(BigInteger.ONE);
                            }                            
                        }                        
                    }                    
                    System.out.println("Radius " + radius.toString());
                    System.out.println("Area " + area.toString());                    
                    System.out.println("PI   " + new BigDecimal(area).divide(new BigDecimal(r2)));                                                             
                }
            };
            new Thread(r).start();
        }
    }
}

Выход:

Area 3141549
PI 3.141549
person Developer Marius Žilėnas    schedule 17.12.2015