【问题标题】:Newton's method with specified digits of precision具有指定精度位数的牛顿法
【发布时间】:2011-06-26 02:10:48
【问题描述】:

我正在尝试用 Java 编写一个计算数字的 n 次根的函数。我正在为此使用牛顿法。但是,用户应该能够指定他们想要的精度位数。这是我遇到麻烦的部分,因为我的答案通常并不完全正确。相关代码在这里:http://pastebin.com/d3rdpLW8。我该如何修复此代码,以便它始终给出至少 p 位精度的答案? (不做不必要的工作)

import java.util.Random;

public final class Compute {

    private Compute() {
    }

    public static void main(String[] args) {
        Random rand = new Random(1230);
        for (int i = 0; i < 500000; i++) {
            double k = rand.nextDouble()/100;
            int n = (int)(rand.nextDouble() * 20) + 1;
            int p = (int)(rand.nextDouble() * 10) + 1;
            double math = n == 0 ? 1d : Math.pow(k, 1d / n);
            double compute = Compute.root(n, k, p);
            if(!String.format("%."+p+"f", math).equals(String.format("%."+p+"f", compute))) {
                System.out.println(String.format("%."+p+"f", math));
                System.out.println(String.format("%."+p+"f", compute));
                System.out.println(math + " " + compute + " " + p);
            }
        }
    }

    /**
     * Returns the n-th root of a positive double k, accurate to p decimal
     * digits.
     * 
     * @param n
     *            the degree of the root.
     * @param k
     *            the number to be rooted.
     * @param p
     *            the decimal digit precision.
     * @return the n-th root of k
     */
    public static double root(int n, double k, int p) {     
        double epsilon = pow(0.1, p+2);
        double approx = estimate_root(n, k);
        double approx_prev;

        do {
            approx_prev = approx;
            // f(x) / f'(x) = (x^n - k) / (n * x^(n-1)) = (x - k/x^(n-1)) / n
            approx -= (approx - k / pow(approx, n-1)) / n;
        } while (abs(approx - approx_prev) > epsilon);
        return approx;
    }

    private static double pow(double x, int y) {
        if (y == 0)
            return 1d;
        if (y == 1)
            return x;
        double k = pow(x * x, y >> 1);
        return (y & 1) == 0 ? k : k * x;
    }

    private static double abs(double x) {
        return Double.longBitsToDouble((Double.doubleToLongBits(x) << 1) >>> 1);
    }

    private static double estimate_root(int n, double k) {
        // Extract the exponent from k.
        long exp = (Double.doubleToLongBits(k) & 0x7ff0000000000000L);
        // Format the exponent properly.
        int D = (int) ((exp >> 52) - 1023);
        // Calculate and return 2^(D/n).
        return Double.longBitsToDouble((D / n + 1023L) << 52);
    }
}

【问题讨论】:

  • 维基百科告诉我牛顿的方法可能不会收敛到正确的根,也许这就是问题所在? en.wikipedia.org/wiki/…:
  • 编辑:添加到帖子的代码

标签: java math precision newtons-method


【解决方案1】:

您的代码中有错误。您的pow() 方法的最后一行应为
return (y &amp; 1) == 1 ? k : k * x;
而不是
return (y &amp; 1) == 0 ? k : k * x;

【讨论】:

    【解决方案2】:

    让我们回忆一下牛顿方法的误差分析是怎么说的。基本上,它给了我们第 n 次迭代的误差,作为第 n-1 次迭代误差的函数。

    那么,我们如何判断误差是否小于 k?我们不能,除非我们知道 e(0) 处的错误。如果我们知道 e(0) 处的错误,我们就可以用它来找到正确的答案。

    你可以说“e(0)

    您要检查的是误差变化是否小于 k,这是一种完全可以接受的方法。但它不检查误差是否小于 k。正如 Axel 和其他人所指出的,还有许多其他的根近似算法,其中一些会产生更容易的错误分析,如果你真的想要这个,你应该使用其中之一。

    【讨论】:

      【解决方案3】:

      如果您想要 4 位小数的精度,只需迭代直到更新小于 0.0001。

      也就是说,如果您想要 n 的精度,请将您的 epsilon 设置为 Math.pow(10, -n)

      【讨论】:

      • 这正是我在代码中所做的。我什至将我的 epsilon 设置为 pow(0.1, n+2) 以提高精度。但是当调用 root (6, 0.008303709223334907, 1) 时,它返回 0.45000169169604604,其中实际的根是 0.4499994905760778... 将两者舍入到 1 位精度分别得到 0.4 和 0.5。
      • 您的答案似乎过于精确,但这可能与根部正好位于舍入边缘有关。我不确定你能做些什么。
      • 正如您可能理解的那样,您可能需要无限精度才能实际告诉(肯定)第一个小数实际上是哪个。 “正确的小数位”或“有效数字”的含义实际上是您已实现的内容:[ 数字的有效数字......是那些具有有助于其精度的意义的数字。 ](en.wikipedia.org/wiki/Significant_figures)
      • 如果你想要 4 位小数的精度,你必须使用 0.00005 而不是 0.0001。
      • @Axel,在这种特殊情况下无关紧要。
      猜你喜欢
      • 2012-04-10
      • 2013-10-30
      • 1970-01-01
      • 2018-07-29
      • 2023-01-02
      • 1970-01-01
      • 2015-03-04
      • 2013-10-17
      • 2017-05-05
      相关资源
      最近更新 更多