web-dev-qa-db-ja.com

Java powerメソッドを使用してn番目のルートを計算する

Math.pow(n, 1.0/3)を使用してJavaで立方根を取得しようとしていましたが、それはdoubleを除算するため、正確な答えを返しません。たとえば、125では、 4.9999999999。これに対する回避策はありますか?3次ルート関数があることは知っていますが、これを修正してより高いルートを計算できるようにします。

次のようなことをして、数値に整数の根があるかどうかを知りたいので、丸めたくありません:Math.pow(n, 1.0 / 3) % ((int) Math.pow(n, 1.0 / 3))

16

doubleで任意精度の計算を行うことはできないため、3つの選択肢があります。

  1. double値が整数であるかどうかを決定する精度を定義します。
  2. doubleの丸められた値が正しい結果であるかどうかをテストします。
  3. BigDecimal オブジェクトで計算を行います。これは、任意精度のdouble値をサポートします。

オプション1

private static boolean isNthRoot(int value, int n, double precision) {
    double a = Math.pow(value, 1.0 / n);
    return Math.abs(a - Math.round(a)) < precision; // if a and round(a) are "close enough" then we're good
}

このアプローチの問題は、「十分に近い」を定義する方法です。これは主観的な質問であり、要件によって異なります。

オプション2

private static boolean isNthRoot(int value, int n) {
    double a = Math.pow(value, 1.0 / n);
    return Math.pow(Math.round(a), n) == value;
}

この方法の利点は、精度を定義する必要がないことです。ただし、別のpow操作を実行する必要があるため、これはパフォーマンスに影響します。

オプション3

BigDecimalの2乗を計算する組み込みメソッドはありません。 この質問 は、それを行う方法についての洞察を提供します。

7
Tunaki

Math.round関数は、doubleに格納できる最も近いlong値に丸めます。 2つの結果を比較して、数値に整数の立方根があるかどうかを確認できます。

double dres = Math.pow(125, 1.0 / 3.0);
double ires = Math.round(dres);
double diff = Math.abs(dres - ires);
if (diff < Math.ulp(10.0)) {
    // has cubic root
}

それが不十分な場合は、 this アルゴリズムを実装してみて、結果が整数と思われない場合は早期に停止することができます。

5

floor(x^(1/n))を計算するためにこのメソッドを書きました。ここで、xは非負のBigIntegerで、nは正の整数です。少し前だったので、なぜそれが機能するのか説明できませんが、私がそれを書いたとき、正しい答えを合理的に迅速に与えることが保証されていることに満足していると合理的に確信しています。

xが正確なn-th power nを累乗した結果が、正確にxに戻るかどうかを確認できます。

public static BigInteger floorOfNthRoot(BigInteger x, int n) {
    int sign = x.signum();
    if (n <= 0 || (sign < 0))
        throw new IllegalArgumentException();
    if (sign == 0)
        return BigInteger.ZERO;
    if (n == 1)
        return x;
    BigInteger a;
    BigInteger bigN = BigInteger.valueOf(n);
    BigInteger bigNMinusOne = BigInteger.valueOf(n - 1);
    BigInteger b = BigInteger.ZERO.setBit(1 + x.bitLength() / n);
    do {
        a = b;
        b = a.multiply(bigNMinusOne).add(x.divide(a.pow(n - 1))).divide(bigN);
    } while (b.compareTo(a) == -1);
    return a;
}

使用するには:

System.out.println(floorOfNthRoot(new BigInteger("125"), 3));

編集上記のコメントを読んで、これがn番目のルートのニュートンラプソン法であることを今思い出します。 Newton-Raphson法には2次収束があります(これは、日常言語では高速です)。数十桁の数字で試してみると、ほんの数秒で答えが得られるはずです。

メソッドを他の数値型で動作するように適合させることはできますが、私の意見ではdoubleBigDecimalはこの種のものには適していません。

1
Paul Boddington

これを行うには、おそらく this メソッドに基づいて、独自の関数を実装します。

1
dimplex

より正確にするために、数学分野からのいくつかのトリックを使用できます。このようにx ^(1/n)= e ^(lnx/n)。

ここで実装を確認してください: https://www.baeldung.com/Java-nth-root

0

それはかなりいハックですが、インデントすることでそれらのいくつかに到達することができます。

System.out.println(Math.sqrt(Math.sqrt(256)));
    System.out.println(Math.pow(4, 4));
    System.out.println(Math.pow(4, 9));
    System.out.println(Math.cbrt(Math.cbrt(262144)));
Result:
4.0
256.0
262144.0 
4.0

これにより、n ^ 3番目ごとのキューブとn ^ 2番目ごとのルートが得られます。

0
John

これは、JavaのMath.pow関数を使用しないソリューションです。それはあなたにほぼn番目のルートを与えます

public class NthRoot {

public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
        int testcases = scanner.nextInt();
        while (testcases-- > 0) {
            int root = scanner.nextInt();
            int number = scanner.nextInt();
            double rootValue = compute(number, root) * 1000.0 / 1000.0;
            System.out.println((int) rootValue);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static double compute(int number, int root) {
    double xPre = Math.random() % 10;
    double error = 0.0000001;
    double delX = 2147483647;
    double current = 0.0;

    while (delX > error) {
        current = ((root - 1.0) * xPre + (double) number / Math.pow(xPre, root - 1)) / (double) root;
        delX = Math.abs(current - xPre);
        xPre = current;
    }
    return current;
}
0
Vpn_talent

これは、このような状況で選択するのに適したオプションです。これに頼ることができます-

   System.out.println("     ");
   System.out.println("     Enter a base and then nth root");
   while(true)
   {
       a=Double.parseDouble(br.readLine());
       b=Double.parseDouble(br.readLine());
       double negodd=-(Math.pow((Math.abs(a)),(1.0/b)));
       double poseve=Math.pow(a,(1.0/b));
       double posodd=Math.pow(a,(1.0/b));
       if(a<0 && b%2==0)
       {
           String io="\u03AF";
           double negeve=Math.pow((Math.abs(a)),(1.0/b));
           System.out.println("     Root is imaginary and value= "+negeve+" "+io);
       }
       else if(a<0 && b%2==1)
       System.out.println("     Value= "+negodd);
       else if(a>0 && b%2==0)
       System.out.println("     Value= "+poseve);
       else if(a>0 && b%2==1)
       System.out.println("     Value= "+posodd);
       System.out.println("     ");
       System.out.print("     Enter '0' to come back or press any number to continue- ");
       con=Integer.parseInt(br.readLine());
       if(con==0)
       break;
       else
       {
           System.out.println("     Enter a base and then nth root");
           continue;
       }
    }
0
Avneesh Singh