【问题标题】:Calculating nth root in Java using power method使用幂方法计算Java中的第n个根
【发布时间】:2015-09-13 18:29:48
【问题描述】:

我试图在 java 中使用 Math.pow(n, 1.0/3) 获得三次方根,但因为它除以双精度数,它不会返回确切的答案。例如,对于 125,这给出 4.9999999999。有解决办法吗?我知道有一个三次根函数,但我想解决这个问题,以便计算更高的根。

我不想四舍五入,因为我想通过执行以下操作来知道一个数字是否具有整数根:Math.pow(n, 1.0 / 3) % ((int) Math.pow(n, 1.0 / 3))

【问题讨论】:

  • 使用 BigDecimal 类,它是任意精度的实数的十进制表示。
  • BigDecimal 类中当然没有计算第 n 个根的方法。所以你需要自己实现它。我会给牛顿拉夫森方法一个机会。见here

标签: java math double decimal nth-root


【解决方案1】:

由于不可能使用double 进行任意精度的微积分,因此您有三个选择:

  1. 定义一个精度,您可以根据该精度决定 double 值是否为整数。
  2. 测试你所拥有的double的四舍五入值是否是正确的结果。
  3. 对支持任意精度双精度值的 BigDecimal 对象进行演算。

选项 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 的双倍幂的内置方法。 This question 会告诉你如何去做。

【讨论】:

    【解决方案2】:

    Math.round 函数将四舍五入到可以存储为双精度的最接近的 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 算法,如果结果似乎不是整数,请提前停止。

    【讨论】:

    • 我知道这是正确的,但我不能这样做,因为我需要确保数字是整数根,我只是编辑了问题以包含这个。
    • 这是已经提到的问题。他没有得到准确的结果,也不想四舍五入。
    • @RamanShrivastava 我根据编辑的问题编辑了答案
    • @ManosNikolaidis 谢谢!
    【解决方案3】:

    我会去实现我自己的功能来做到这一点,可能基于this 方法。

    【讨论】:

    • 这个问题更多地与为double 定义精度的方式有关,而不是使用的方法。
    【解决方案4】:

    我编写了这个方法来计算floor(x^(1/n)),其中x 是一个非负的BigIntegern 是一个正整数。这是不久前的事了,所以我无法解释它为什么会起作用,但我有理由相信,当我写它时,我很高兴它可以保证相当快地给出正确的答案。

    要查看x 是否是精确的n-th 幂,您可以检查提升到幂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));
    

    编辑 阅读了上面的 cmets,我现在记得这是用于 n 次根的 Newton-Raphson 方法。 Newton-Raphson 方法具有二次收敛性(在日常语言中意味着它很快)。您可以在具有数十位数字的数字上尝试它,您应该会在几分之一秒内得到答案。

    您可以调整该方法以使用其他数字类型,但我认为doubleBigDecimal 不适合这种事情。

    【讨论】:

      【解决方案5】:

      您可以使用一些来自数学领域的技巧,以获得更高的准确性。 比如这个 x^(1/n) = e^(lnx/n)。

      在此处检查实现: https://www.baeldung.com/java-nth-root

      【讨论】:

        【解决方案6】:

        这是不使用 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;
        }
        

        【讨论】:

        • 您已经发布了两个答案。哪个更有用?
        【解决方案7】:

        在这种情况下,这是一个不错的选择。 你可以依赖这个-

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

        【讨论】:

          【解决方案8】:

          这是一个非常丑陋的 hack,但你可以通过缩进来访问其中的一些。

          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 个根。

          【讨论】:

            【解决方案9】:

            使用二分法查找第 n 个根。 这是根据您的要求以任意精度查找第 n 个根的方法。

            import java.util.Scanner;
            
            public class FindRoot {
            
                public static void main(String[] args) {
                    try (Scanner scanner = new Scanner(System.in)) {
                        int testCase = scanner.nextInt();
                        while (testCase-- > 0) {
                            double number = scanner.nextDouble();
                            int root = scanner.nextInt();
                            double precision = scanner.nextDouble();
                            double result = findRoot(number, root, precision);
                            System.out.println(result);
                        }
                    }
                }
            
                private static double findRoot(double number, int root, double precision) {
                    double start = 0;
                    double end = number / 2;
                    double mid = end;
                    while (true) {
                        if (precision >= diff(number, mid, root)) {
                            return mid;
                        }
                        if (pow(mid, root) > number) {
                            end = mid;
                        } else {
                            start = mid;
                        }
                        mid = (start + end) / 2;
                    }
                }
            
                private static double diff(double number, double mid, int n) {
                    double power = pow(mid, n);
                    return number > power ? number - power : power - number;
                }
            
                private static double pow(double number, int pow) {
                    double result = number;
                    while (pow-- > 1) {
                        result *= number;
                    }
                    return result;
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-06-29
              • 2012-02-08
              • 2014-03-24
              • 2016-08-30
              • 1970-01-01
              • 2014-02-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多