一个麦克劳林级数运用欧拉的思想,使用适当的多项式来近似计算函数的值。不过,这些多项式显然会与像cos(x)这样的函数发散,因为它们在某些点上都趋向于无穷大,而cos函数则不是。一个100阶多项式最多可以在零点两侧近似50个周期的函数。由于50 * 2pi << 1000,你的多项式不能近似cos(1000)。
为了得到一个合理的解,多项式的阶数至少必须是x/pi。你可以尝试计算300+阶的多项式,但由于浮点数的有限精度和阶乘的巨大性,你很可能会遇到一些主要的数值问题。
相反,利用cos(x)的周期性,在函数的第一行添加以下内容:
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
时停止迭代:
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|
的限制。由于以下项会切换符号,因此它还是结果总精度的上限。
round(cos(1000), 8)
" 这个函数返回的是一个浮点数,它的大小不是十进制表示法、科学计数法或其他任何表示法。因此,它并不会返回科学计数法的数字1.2194074101485173e+225
。实际上,科学计数法只是一种表示方法。这个结果是由你如何打印这个值决定的。 - Alexander