Calculating nth root in Java using power method

后端 未结 9 2649
礼貌的吻别
礼貌的吻别 2021-02-18 22:08

I was trying to get a cubic root in java using Math.pow(n, 1.0/3) but because it divides doubles, it doesn\'t return the exact answer. For example, with 125, this g

相关标签:
9条回答
  • 2021-02-18 22:38

    Since it is not possible to have arbitrary-precision calculus with double, you have three choices:

    1. Define a precision for which you decide whether a double value is an integer or not.
    2. Test whether the rounded value of the double you have is a correct result.
    3. Do calculus on a BigDecimal object, which supports arbitrary-precision double values.

    Option 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
    }
    

    The problem with this approach is how to define "close enough". This is a subjective question and it depends on your requirements.

    Option 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;
    }
    

    The advantage of this method is that there is no need to define a precision. However, we need to perform another pow operation so this will affect performance.

    Option 3

    There is no built-in method to calculate a double power of a BigDecimal. This question will give you insight on how to do it.

    0 讨论(0)
  • 2021-02-18 22:42

    I wrote this method to compute floor(x^(1/n)) where x is a non-negative BigInteger and n is a positive integer. It was a while ago now so I can't explain why it works, but I'm reasonably confident that when I wrote it I was happy that it's guaranteed to give the correct answer reasonably quickly.

    To see if x is an exact n-th power you can check if the result raised to the power n gives you exactly x back again.

    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;
    }
    

    To use it:

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

    Edit Having read the comments above I now remember that this is the Newton-Raphson method for n-th roots. The Newton-Raphson method has quadratic convergence (which in everyday language means it's fast). You can try it on numbers which have dozens of digits and you should get the answer in a fraction of a second.

    You can adapt the method to work with other number types, but double and BigDecimal are in my view not suited for this kind of thing.

    0 讨论(0)
  • 2021-02-18 22:43

    I'd go for implementing my own function to do this, possibly based on this method.

    0 讨论(0)
提交回复
热议问题