提问人:Corwin of Amber 提问时间:2/21/2021 更新时间:10/14/2022 访问量:1755
如何在 Python 3.7 中避免 f(x) = (1-cos(x))/x**2 中小数的灾难性取消?
How do I avoid catastrophic cancellation for small numbers in f(x) = (1-cos(x))/x**2 in Python 3.7?
问:
如何避免因小数字而灾难性取消 Python 3.7 中的 f(x) = (1-cos(x))/x**2?
这就是我到目前为止尝试过的(我知道,关键是一些三角恒等式,它使您能够避免取消,而且我也知道,在使用了 L'Hopital 规则后,f(x) 的极限→0 是 0.5,所以正确的程序输出非常接近 0.5,例如,如果您使用 x = 1.2e-4,这就是您得到的, 但是你会用较小的数字(如 1.2e-8)取消,我需要这样做,这样就不会发生这种情况)。
from math import *
def f(x): #these are all the same function using different identities
a = (1-(sin(x)/tan(x)))/(x**2)
b = (1-(sin(2*x)/(2*sin(x))))/(x**2)
c = (1-((1-((tan(x/2))**2))/(1+(tan(x/2))**2)))/(x**2)
d = (sin(x)**2+cos(x)**2-cos(x))/(x**2)
e = (sin(x)**2+cos(x)**2-(sin(2*x)/(2*sin(x))))/(x**2)
return a, b, c, d, e
print(k(1.2e-8))
#Output: (0.0, 0.7709882115452477, 0.0, 0.0, 0.0) - whereas need 0.5000...
答:
喜欢这个:
sin(x)/x * tan(x/2)/x
它一直完成工作到最后,仍然没问题。x = 1e-308
不幸的是,我无法提供太多关于为什么它运行良好的见解。
评论
sin
cosine
sin(1e-100)=1e-100
cos(1e-100)=1.0
tan(1e-100)=1e-100
sin(x)=x
x
sin(x)/x
x
问题在于浮点和双精度的有限。您必须使用更精确的算术,例如 mpfr。它可以在 Python 中通过绑定使用,例如
https://pypi.org/project/gmpy2/
下面是一个示例,我通过一个名为 Sagemath 的更高级的环境使用它: 我使用的是 100 位精度:
sage: R = RealField(100) // 100 bits of precision
sage: def f(x): #these are all the same function using different identities
....: a = (1-(sin(x)/tan(x)))/(x**2)
....: b = (1-(sin(2*x)/(2*sin(x))))/(x**2)
....: c = (1-((1-((tan(x/2))**2))/(1+(tan(x/2))**2)))/(x**2)
....: d = (sin(x)**2+cos(x)**2-cos(x))/(x**2)
....: e = (sin(x)**2+cos(x)**2-(sin(2*x)/(2*sin(x))))/(x**2)
....: return a, b, c, d, e
....:
sage: f(R(1.2e-8))
(0.50000000000000264647624775223,
0.49999999999999716827551705076,
0.49999999999999716827551705076,
0.49999999999999716827551705076,
0.49999999999999169007478634929)
评论
请改用。 是数据分析和更复杂数学的标准。可以在终端中使用以下命令安装它。numpy.float128
numpy
pip install numpy
from numpy import *
def f(x): #these are all the same function using different identities
a = (1-(sin(x)/tan(x)))/(x**2)
b = (1-(sin(2*x)/(2*sin(x))))/(x**2)
c = (1-((1-((tan(x/2))**2))/(1+(tan(x/2))**2)))/(x**2)
d = (sin(x)**2+cos(x)**2-cos(x))/(x**2)
e = (sin(x)**2+cos(x)**2-(sin(2*x)/(2*sin(x))))/(x**2)
return a, b, c, d, e
print(f(float128(1.2e-8)))
这将打印
(0.5003141275115400736, 0.49956120933620291774, 0.49993766842387149567, 0.49993766842387149567, 0.49956120933620291774)
评论
numpy.float128
float128
numpy
您可以有条件地返回小 x 的限制,如下所示:
import matplotlib.pyplot as plt
from numpy import *
epsilon=1e-8
def f(x):
if x<epsilon
return 0.5
return (1-sin(x)/tan(x))/x**2
#note: same as (1-cos(x))/x**2
x=arange(0,6,0.01)
y=vectorize(f)
plt.plot(x,y(x))
plt.show()
绘制的曲线看起来很平滑
注意:我更喜欢numpy而不是math。矢量化使得使用数组调用函数成为可能(效率不高,但易于使用)。
如何在 Python 3.7 中避免 f(x) = (1-cos(x))/x**2 中小数的灾难性取消?
您可以使用半角度标识:
1 − cos(x) = 2 sin²(x/2)。
(历史上的三角函数会将其识别为正弦函数,或者是哈弗正弦函数的两倍,这很有用,并为此目的制成表格!
如果将 x 计算为 0 附近 x 的 cos(x) 的近似值,则减法 1 中的灾难性抵消可能会极大地放大该近似值中的任何误差——如果四舍五入为 1,则可能会给出一个无意义的答案,因此当它应该接近 1/2 时四舍五入为 0。cos(x)
cos(x)
cos(x)
(1 - cos(x))/x**2
相反,如果你把它改写成
(1 − cos(x))/x² = 2 sin²(x/2)/x²
使用半角恒等式,然后计算接近零的 x,您将在条件良好的域上组合函数。
在二进制浮点中,除以 2 和乘以 2 总是精确的,除非出现溢出或下溢的情况,并且 sin 和平方都处于接近零的良好条件,因此即使在数学库的过程中进行中间舍入和误差,相对误差也相当小。2*(sin(x/2)/x)**2
sin
也可以使用另一个半角度标识,
1 − cos(x) = sin(x) tan(x/2),
正如另一个答案所暗示的那样——SIN 和 Tan 都处于接近零的良好条件,因此它也会给出一个很好的近似值。 但是计算 sin 和 tan 的成本比仅仅计算 sin 要高,而且我认为至少对于接近零的输入,误差没有明显的优势。
要在没有条件的情况下处理 x = 0,如果您有一个近似于函数 sinc(x) = sin(x)/x 的过程,并且极限为 sinc(0) = 0,您也可以将其重写为:sinc
(1 − cos(x))/x² = 2 sin²(x/2)/x² = (1/2) sin²(x/2)/(x/2)² = (1/2) sinc²(x/2)。
下一个:如何计算输入的小数点?[复制]
评论
numpy
float64