Calculating nth root in Java using power method

2019-04-04 06:51发布

问题:

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 gives 4.9999999999. Is there a work-around for this? I know there is a cubic root function but I'd like to fix this so I can calculate higher roots.

I would not like to round because I want to know whether a number has an integer root by doing something like this: Math.pow(n, 1.0 / 3) % ((int) Math.pow(n, 1.0 / 3)).

回答1:

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.



回答2:

The Math.round function will round to the nearest long value that can be stored to a double. You could compare the 2 results to see if the number has an integer cubic root.

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
}

If that's inadequate you can try implementing this algorithm and stop early if the result doesn't seem to be an integer.



回答3:

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



回答4:

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.



回答5:

Well this is a good option to choose in this situation. You can rely on this-

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


回答6:

It's a pretty ugly hack, but you could reach a few of them through indenting.

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

Which will give you every n^3th cube and every n^2th root.



回答7:

You can use some tricks come from mathematics field, to havemore accuracy. Like this one x^(1/n) = e^(lnx/n).

Check the implementation here: https://www.baeldung.com/java-nth-root



回答8:

Here is the solution without using Java's Math.pow function. It will give you nearly nth root

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