2017-04-07 143 views
2

我需要計算量評估1 /的tanh(X) - 1/X爲非常小x

1/tanh(x) - 1/x 

x > 0,其中x既可以是非常小和非常大的。

漸近的小x,我們有

1/tanh(x) - 1/x -> x/3 

和大x

1/tanh(x) - 1/x -> 1 

總之,從10^-7小舍入誤差計算表達式時,已經導致表達是精確評估爲0:

import numpy 
import matplotlib.pyplot as plt 


x = numpy.array([2**k for k in range(-30, 30)]) 
y = 1.0/numpy.tanh(x) - 1.0/x 

plt.loglog(x, y) 
plt.show() 

enter image description here

+0

你可以定義自己的版本功能'1/tanh(x) - 1/x'如果參數太小/太大,會評估漸近表達式 – Stelios

+0

編寫您自己的'tanh'? (我沒有看到0直到小於10^-8。) –

回答

3

對於非常小的x,可以使用the Taylor expansion of 1/tanh(x) - 1/x around 0

y = x/3.0 - x**3/45.0 + 2.0/945.0 * x**5 

錯誤是命令O(x**7),所以如果選擇10^-5作爲斷點,則相對誤差和絕對誤差將遠低於機器精度。

import numpy 
import matplotlib.pyplot as plt 


x = numpy.array([2**k for k in range(-50, 30)]) 

y0 = 1.0/numpy.tanh(x) - 1.0/x 
y1 = x/3.0 - x**3/45.0 + 2.0/945.0 * x**5 
y = numpy.where(x > 1.0e-5, y0, y1) 


plt.loglog(x, y) 
plt.show() 

enter image description here

+2

這是正確的答案。爲了避免泰勒展開而轉向任意精度算術增加了大規模的複雜性,依賴性,存儲和計算成本(我認爲這是一個非常喜歡mpmath的人!) –

3

使用任意小數精度Python包mpmath。例如:

import mpmath 
from mpmath import mpf 

mpmath.mp.dps = 100 # set decimal precision 

x = mpf('1e-20') 

print (mpf('1')/mpmath.tanh(x)) - (mpf('1')/x) 
>>> 0.000000000000000000003333333333333333333333333333333333333333311111111111111111111946629156220629025294373160489201095913 

它變得非常精確。請致電mpmath plottingmpmathmatplotlib配合良好,因此您應該可以解決您的問題。


下面是如何mpmath融入你上面寫的代碼示例:

import numpy 
import matplotlib.pyplot as plt 
import mpmath 
from mpmath import mpf 

mpmath.mp.dps = 100 # set decimal precision 

x = numpy.array([mpf('2')**k for k in range(-30, 30)]) 
y = mpf('1.0')/numpy.array([mpmath.tanh(e) for e in x]) - mpf('1.0')/x 

plt.loglog(x, y) 
plt.show() 

enter image description here

0

一個可能更簡單的解決方案來克服這種情況正在改變數據類型下這numpy的是操作:

import numpy as np 
import matplotlib.pyplot as plt 

x = np.arange(-30, 30, dtype=np.longdouble) 
x = 2**x 
y = 1.0/np.tanh(x) - 1.0/x 

plt.loglog(x, y) 
plt.show() 

使用longdouble數據類型並提供正確的解決方案,而舍入錯誤。


我沒悅目修改你的榜樣,你的情況,你需要修改的唯一的事情是:

x = numpy.array([2**k for k in range(-30, 30)]) 

到:

x = numpy.array([2**k for k in range(-30, 30)], dtype=numpy.longdouble) 
+0

我試過這個,發現它稍後才失效,在10^-10。 –