簡體   English   中英

使用泰勒級數逼近 cos

[英]Approximating cos using the Taylor series

我正在使用泰勒級數來計算一個數字的 cos,對於小數字,function 返回准確的結果,例如cos(5)給出0.28366218546322663 但是對於較大的數字,它會返回不准確的結果,例如cos(1000)給出1.2194074101485173e+225

def factorial(n):
    c = n
    for i in range(n-1, 0, -1):
        c *= i
    return c

def cos(x, i=100):
    c = 2
    n = 0
    for i in range(i):
        if i % 2 == 0:
            n += ((x**c) / factorial(c))
        else:
            n -= ((x**c) / factorial(c))
        c += 2
    return 1 - n

我嘗試使用round(cos(1000), 8)它仍然返回一個用科學計數法寫的數字1.2194074101485173e+225和 e+ 部分。 math.cos(1000)給出0.5623790762907029 ,如何四舍五入我的數字,使其與 math.cos 方法相同?

McLaurin 級數使用 Euler 的思想來使用適當的多項式來近似 function 的值。 多項式顯然與像cos(x)這樣的 function 不同,因為它們在某些時候都趨向無窮大,而cos則不然。 100 階多項式在零的每一側最多可以近似 function 的 50 個周期。 由於 50 * 2pi << 1000,您的多項式不能近似cos(1000)

為了更接近合理的解決方案,多項式的階必須至少為x / pi 您可以嘗試計算 300+ 階的多項式,但由於浮點數的有限精度和階乘的巨大,您很可能會遇到一些主要的數值問題。

相反,使用cos(x)的周期性並將以下內容添加為 function 的第一行:

x %= 2.0 * math.pi

您還需要限制多項式的階數,以避免因因子太大而無法放入浮點數的問題。 此外,您可以並且應該通過增加先前結果而不是在每次迭代時從頭開始來計算您的階乘。 這是一個具體的例子:

import math

def cos(x, i=30):
    x %= 2 * math.pi
    c = 2
    n = 0
    f = 2
    for i in range(i):
        if i % 2 == 0:
            n += x**c / f
        else:
            n -= x**c / f
        c += 2
        f *= c * (c - 1)
    return 1 - n
>>> print(cos(5), math.cos(5))
0.28366218546322663 0.28366218546322625

>>> print(cos(1000), math.cos(1000))
0.5623790762906707 0.5623790762907029

>>> print(cos(1000, i=86))
...
OverflowError: int too large to convert to float

通過注意到增量乘積是x**2 / (c * (c - 1)) ,您可以進一步擺脫數值瓶頸。 對於比直接階乘所能支持的更大的i ,這將保持良好的界限:

import math

def cos(x, i=30):
    x %= 2 * math.pi
    n = 0
    dn = x**2 / 2
    for c in range(2, 2 * i + 2, 2):
        n += dn
        dn *= -x**2 / ((c + 1) * (c + 2))
    return 1 - n
>>> print(cos(5), math.cos(5))
0.28366218546322675 0.28366218546322625
>>> print(cos(1000), math.cos(1000))
0.5623790762906709 0.5623790762907029
>>> print(cos(1000, i=86), math.cos(1000))
0.5623790762906709 0.5623790762907029
>>> print(cos(1000, i=1000), math.cos(1000))
0.5623790762906709 0.5623790762907029

請注意,超過某個點,無論您執行多少次循環,結果都不會改變。 這是因為現在dn按照歐拉的意圖收斂到零。

您可以使用此信息進一步改進您的循環。 由於浮點數的精度有限(具體而言,尾數為 53 位),因此您可以在|dn / n| < 2**-53時停止迭代。 |dn / n| < 2**-53 :

import math

def cos(x, conv=2**-53):
    x %= 2 * math.pi
    c = 2
    n = 1.0
    dn = -x**2 / 2.0
    while abs(n / dn) > conv:
        n += dn
        c += 2
        dn *= -x**2 / (c * (c - 1))
    return n
>>> print(cos2(5), math.cos(5))
0.28366218546322675 0.28366218546322625
>>> print(cos(1000), math.cos(1000))
0.5623790762906709 0.5623790762907029
>>> print(cos(1000, 1e-6), math.cos(1000))
0.5623792855306163 0.5623790762907029
>>> print(cos2(1000, 1e-100), math.cos(1000))
0.5623790762906709 0.5623790762907029

參數conv不僅僅是|dn/n|上的界限 . 由於以下術語切換符號,它也是結果整體精度的上限。

返回的數字只是一個數字; 在您打印之前,它沒有符號意義。 如果您希望控制值的打印方式,假設您正在像這樣打印

print(cos(1000))

然后我們可以使用格式字符串來控制output

print("{:f}".format(cos(1000)))

如果您使用的是 Python 3.6 或更高版本,我們甚至可以將其直接插入到字符串文字中。

print(f"{cos(1000):f}")

您可以閱讀上面的鏈接以查看有關格式迷你語言的更多詳細信息(兩種功能之間的語言相同)。 例如,如果您想打印特定的小數位數,您也可以提出請求。 我們可以精確打印三個小數位,如下所示

print("{:.3f}".format(cos(1000)))
print(f"{cos(1000):.3f}")

但是,正如 Mad Physicist 指出的那樣,您的代碼也存在更多數學問題,因此我強烈建議您也閱讀他的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM