1

我目前正在编写一个计算器应用程序。我正在尝试将导数估计器写入其中。下面的公式是一种简单的方法。通常在纸上,您会使用尽可能小的 h 来获得最准确的估计。问题是双打无法处理将非常小的数字添加到相对较大的数字。例如 4+1E-200 只会产生 4.0。即使 h 只是 1E-16,实际上 4+1E16 也会给你正确的值,但是做数学计算它是不准确的,因为第 16 位之后的任何东西都会丢失并且舍入不能正确发生。我听说双打的一般经验法则是 1E-8 或 1E-7。这个问题是大数字不起作用,因为 2E231+1E-8 将只是 2E23,1E-8 会因为尺寸问题而丢失。

f'(x)=(f(x+h)-f(x))/h as x approaches 0

当我在点 4 测试 f(x)=x^2 所以 f'(4) 时,它应该正好是 8 现在我知道我可能永远不会得到正好 8。但我最准确的似乎是 1E 左右7 或 1E8 有趣的是 1E-9 到 1E-11 都给出了相同的答案。这是 h 的列表和结果f(x)=x^2 at x=4

1E-7 8.000000129015916
1E-8 7.999999951380232
1E-9 8.000000661922968
1E-10 8.000000661922968
1E-11 8.000000661922968
1E-12 8.000711204658728

以下是我的问题:

  1. 选择 h 的最佳方法是什么,显然 1E-8 或 1E-7 是有意义的,但是我如何根据 x 选择 h,以便即使 x 是 3.14E203 或 2E-231,它也适用于任何大小的数字.
  2. 我应该考虑多少位小数。
  3. 你知道德州仪器是如何做到的吗,TI 83、84 和 Inspire 可以用数字计算出小数点后 12 位或精度的导数,而且几乎总是正确的,但是它们的数字的最大精度是 12 位,而那些计算器是非 CAS,所以他们实际上并没有派生任何东西
  4. 从逻辑上讲,有一个介于 1E-7 和 1E-8 之间的数字会给我一个更精确的结果,有没有办法找到这个数字,或者至少接近它。

已回答

非常感谢鲍勃。该应用程序目前计划为 2 种形式,一个命令行 PC 应用程序。还有一个安卓应用。在“关于”页面的部分内容中将特别感谢您。如果您愿意,它将是开源的,但在我解决一些非常非常大的错误之前,我不会发布项目站点的链接。目前我一直称它为 Mathulator,但名称可能会更改,因为它已经拥有版权,而且听起来很愚蠢。我不知道发布候选者何时会运行,目前我不知道它何时运行会稳定。但如果我也能实现我想要的一切,那将是非常强大的。再次感谢。快乐编程。

4

6 回答 6

4

有一本书可以回答这个问题(还有其他人喜欢它):

C 中的数字食谱,第 2 版,出版社、Vetterling、Teukolsky 和 ​​Flannery。本书还提供 C++、Fortran 和 BASIC 版本。遗憾的是,不存在 Java 版本。此外,我相信这本书已经绝版,但可以在线购买某些口味的二手版本(至少通过 bn.com。)

第 5.7 节,“数值导数”,p。186 准确地解释了您在数值导数中看到的问题以及它发生背后的数学原理,以及如何正确计算数值导数的函数(在 C 中,但应该很容易转换为 Java)。此处介绍了它们的简单近似值的摘要:

1)在数值上,你​​最好计算对称版本:

f'(x) = (f(x + h) - f(x - h)) / 2h

2) h 应约为 (sigma_f)^(1/3) * x_c

在哪里

sigma_f =~简单函数计算 f(x) 的小数精度

x_c =~ x,除非 x 等于 0。

但是,这不会产生最优导数,因为误差是 ~ (sigma_f)^(2/3)。更好的解决方案是 Ridders 算法,该算法在书中以 C 程序的形式呈现(参考 Ridders, CJF 1982, Advances in Engineering Software, vol. 4, no. 2, pp 75-76。)

于 2011-04-04T20:39:10.447 回答
2

阅读题为“每个程序员应该了解的浮点知识”的论文(谷歌)。然后您会看到大多数浮点值在计算机硬件中近似表示。

要进行没有此缺点的计算,请使用符号计算。但这不如使用浮点数那么有效。

为了使浮点结果一致,使用四舍五入到最近的 10 次幂,例如 0.1、0.01 等。要了解何时应该停止近似,请在近似步骤期间使用某种阈值来观察。例如,如果执行下一个近似步骤只对已经计算的值产生 0.001% 的变化,那么继续近似是没有意义的。

更新我很久以前就有我的数值计算课程,但我隐约记得减去接近的数字是非常糟糕的,因为如果数字非常接近,最可靠的数字会被抵消,并且你有不可靠的数字。这正是你减少时发生的事情h。在这些情况下,建议用一些其他操作代替减法。例如,您可以切换到您的 `f(x) 扩展的某种系列。

我不太明白你的第二个问题,因为答案取决于你的要求——“尽可能多”。

顺便说一句,在 math.stackexchange.com 上找到问题的答案可能会更好。

此外,访问链接提供thrashgod数值微分

于 2011-04-04T20:19:31.520 回答
2

1.浮点数(浮点数和双精度数)的精度取决于数字的绝对值。双打的精度约为 15 位,因此您可以添加1 + 1e-15,但10 + 1e-15很可能再次为 10 位,因此您必须添加10 + 1e-14。为了获得有意义的结果,我建议您将 1e-8 乘以原始数字的绝对值,这将为您提供大约 7 个正确的导数数字。就像是:

double h = x * 1e-8;
double derivative = (f(x+h) - f(x)) / h;

无论如何,这是一个近似值,例如,如果您尝试计算 sin(x) 在 x=1e9 处的导数,您将得到 h=10,结果将全部错误。但是对于“有趣”部分接近于零的“常规”函数,这将很好地工作。

2.“h”越少,对导数进行采样的点越精确,但得到的导数的正确位数越少。我无法证明这一点,但我的直觉是,h = x * 1e-8你得到7 = 15 - 8正确的数字,其中 15 是double' 的精度。

此外,使用“更对称”的公式是个好主意,它在二阶多项式上给出了绝对正确的答案:

double derivative = (f(x+h) - f(x-h)) / (2*h);
于 2011-04-04T20:43:03.220 回答
1

我的问题是什么是最合适的h,以及如何将其缩放到任何大小。

数值微分中所述, h的合适选择是sqrt(ɛ) * x,其中ɛ机器 epsilon

于 2011-04-04T20:49:03.950 回答
0

我会使用BigDecimal类进行此类计算,尽管它不能回答您的问题,但它确实会提高浮点运算的精度。

于 2011-04-04T20:22:08.073 回答
0

根据 Javadoc,11 位代表指数,52 位代表有效数字。忽略指数,您似乎有 52 位可供使用。所以如果你选择 h = x * 2^-40,你在这里使用了 40 位,你会看到的精度是 2^-12。根据您的需要调整此比率。

于 2011-04-04T20:44:39.087 回答