使用便利课程#
多项式包提供的便利类有:
姓名
提供
多项式
动力系列
切比雪夫
切比雪夫系列
勒让德
勒让德系列
拉盖尔
拉盖尔系列
隐士
隐士系列
埃尔米特E
HermiteE系列
本文中的级数是相应多项式基函数乘以系数的有限和。例如,幂级数看起来像
并且有系数\([1, 2, 3]\)。具有相同系数的切比雪夫级数看起来像
更一般地说
在这种情况下\(T_n\)是度数的切比雪夫函数\(n\),但也可以很容易地成为任何其他类的基本函数。所有类的约定是系数\(c[i]\)与 i 次的基函数一致。
所有类都是不可变的并且具有相同的方法,特别是它们实现了 Python 数字运算符 +、-、*、//、%、divmod、**、== 和 !=。由于浮点舍入误差,最后两个可能有点问题。现在我们使用 NumPy 1.7.0 版本快速演示各种操作。
基本#
首先,我们需要一个多项式类和一个多项式实例来使用。这些类可以直接从多项式包或相关类型的模块导入。这里我们从包中导入并使用传统的 Polynomial 类,因为它很熟悉:
>>> from numpy.polynomial import Polynomial as P
>>> p = P([1,2,3])
>>> p
Polynomial([1., 2., 3.], domain=[-1, 1], window=[-1, 1], symbol='x')
请注意,长版本的打印输出分为三个部分。第一个是系数,第二个是域,第三个是窗口:
>>> p.coef
array([1., 2., 3.])
>>> p.domain
array([-1, 1])
>>> p.window
array([-1, 1])
打印多项式会以更熟悉的格式生成多项式表达式:
>>> print(p)
1.0 + 2.0·x + 3.0·x²
请注意,多项式的字符串表示形式默认使用 Unicode 字符(Windows 除外)来表示幂和下标。还可以使用基于 ASCII 的表示形式(Windows 上默认)。可以使用以下函数在包级别切换多项式字符串格式
set_default_printstyle
:
>>> np.polynomial.set_default_printstyle('ascii')
>>> print(p)
1.0 + 2.0 x + 3.0 x**2
或通过字符串格式控制单个多项式实例:
>>> print(f"{p:unicode}")
1.0 + 2.0·x + 3.0·x²
当我们开始拟合时,我们将处理域和窗口,目前我们忽略它们并运行基本的代数和算术运算。
加减:
>>> p + p
Polynomial([2., 4., 6.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> p - p
Polynomial([0.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
乘法:
>>> p * p
Polynomial([ 1., 4., 10., 12., 9.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
权力:
>>> p**2
Polynomial([ 1., 4., 10., 12., 9.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
分配:
楼层除法“//”是多项式类的除法运算符,在这方面多项式被视为整数。对于 < 3.x 的 Python 版本,“/”运算符映射到“//”,与 Python 一样,对于更高版本,“/”仅适用于标量除法。在某些时候它将被弃用:
>>> p // P([-1, 1])
Polynomial([5., 3.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
余:
>>> p % P([-1, 1])
Polynomial([6.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
分区模式:
>>> quo, rem = divmod(p, P([-1, 1]))
>>> quo
Polynomial([5., 3.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> rem
Polynomial([6.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
评估:
>>> x = np.arange(5)
>>> p(x)
array([ 1., 6., 17., 34., 57.])
>>> x = np.arange(6).reshape(3,2)
>>> p(x)
array([[ 1., 6.],
[17., 34.],
[57., 86.]])
代换:
将 x 替换为多项式并展开结果。这里我们代入 p 本身,得到展开后的一个新的 4 次多项式。如果将多项式视为函数,则这是函数的复合:
>>> p(p)
Polynomial([ 6., 16., 36., 36., 27.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
根源:
>>> p.roots()
array([-0.33333333-0.47140452j, -0.33333333+0.47140452j])
显式使用 Polynomial 实例并不总是很方便,因此元组、列表、数组和标量会在算术运算中自动转换:
>>> p + [1, 2, 3]
Polynomial([2., 4., 6.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> [1, 2, 3] * p
Polynomial([ 1., 4., 10., 12., 9.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> p / 2
Polynomial([0.5, 1. , 1.5], domain=[-1., 1.], window=[-1., 1.], symbol='x')
域、窗口或类不同的多项式不能在算术中混合:
>>> from numpy.polynomial import Chebyshev as T
>>> p + P([1], domain=[0,1])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 213, in __add__
TypeError: Domains differ
>>> p + P([1], window=[0,1])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 215, in __add__
TypeError: Windows differ
>>> p + T([1])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 211, in __add__
TypeError: Polynomial types differ
但可以用不同的类型来替代。事实上,对于类型、域和窗口转换,多项式类之间的转换是这样完成的:
>>> p(T([0, 1]))
Chebyshev([2.5, 2. , 1.5], domain=[-1., 1.], window=[-1., 1.], symbol='x')
给出切比雪夫形式的多项式p 。这有效是因为 \(T_1(x) = x\)并替换\(x\)为了\(x\)不改变原始多项式。然而,所有乘法和除法都将使用切比雪夫级数完成,因此结果的类型也是如此。
所有多项式实例都是不可变的,因此增强运算(+=
、-=
等)以及任何其他违反多项式实例不可变性的功能都故意未实现。
微积分#
多项式实例可以积分和微分:
>>> from numpy.polynomial import Polynomial as P
>>> p = P([2, 6])
>>> p.integ()
Polynomial([0., 2., 3.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> p.integ(2)
Polynomial([0., 0., 1., 1.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
第一个示例对p进行了一次积分,第二个示例对 p 进行了两次积分。默认情况下,积分下限和积分常数均为 0,但两者都可以指定:
>>> p.integ(lbnd=-1)
Polynomial([-1., 2., 3.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> p.integ(lbnd=-1, k=1)
Polynomial([0., 2., 3.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
在第一种情况下,积分下限设置为 -1,积分常数为 0。在第二种情况下,积分常数也设置为 1。微分更简单,因为唯一的选择是多项式微分的次数:
>>> p = P([1, 2, 3])
>>> p.deriv(1)
Polynomial([2., 6.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> p.deriv(2)
Polynomial([6.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
其他多项式构造函数#
通过指定系数构造多项式只是获取多项式实例的一种方法,还可以通过指定根、从其他多项式类型转换以及通过最小二乘拟合来创建多项式。拟合在其自己的部分中讨论,其他方法如下所示:
>>> from numpy.polynomial import Polynomial as P
>>> from numpy.polynomial import Chebyshev as T
>>> p = P.fromroots([1, 2, 3])
>>> p
Polynomial([-6., 11., -6., 1.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> p.convert(kind=T)
Chebyshev([-9. , 11.75, -3. , 0.25], domain=[-1., 1.], window=[-1., 1.], symbol='x')
Convert方法还可以转换域和窗口:
>>> p.convert(kind=T, domain=[0, 1])
Chebyshev([-2.4375 , 2.96875, -0.5625 , 0.03125], domain=[0., 1.], window=[-1., 1.], symbol='x')
>>> p.convert(kind=P, domain=[0, 1])
Polynomial([-1.875, 2.875, -1.125, 0.125], domain=[0., 1.], window=[-1., 1.], symbol='x')
在 numpy 版本 >= 1.7.0 中,基础方法和强制转换类方法也可用。 cast 方法的工作方式与 Convert 方法类似,而 basic 方法返回给定次数的基多项式:
>>> P.basis(3)
Polynomial([0., 0., 0., 1.], domain=[-1., 1.], window=[-1., 1.], symbol='x')
>>> T.cast(p)
Chebyshev([-9. , 11.75, -3. , 0.25], domain=[-1., 1.], window=[-1., 1.], symbol='x')
类型之间的转换可能很有用,但不建议日常使用。从 50 次切比雪夫级数传递到相同次数的多项式级数时数值精度的损失可能会使数值评估的结果本质上是随机的。
配件#
拟合是域和窗口属性成为便利类一部分的原因。为了说明该问题,下图绘制了 5 次以下切比雪夫多项式的值。
>>> import matplotlib.pyplot as plt
>>> from numpy.polynomial import Chebyshev as T
>>> x = np.linspace(-1, 1, 100)
>>> for i in range(6):
... ax = plt.plot(x, T.basis(i)(x), lw=2, label=f"$T_{i}$")
...
>>> plt.legend(loc="upper left")
>>> plt.show()
在 -1 <= x <= 1范围内,它们是很好的等波纹函数,位于 +/- 1 之间。在 -2 <= x <= 2 范围内的相同图看起来非常不同:
>>> import matplotlib.pyplot as plt
>>> from numpy.polynomial import Chebyshev as T
>>> x = np.linspace(-2, 2, 100)
>>> for i in range(6):
... ax = plt.plot(x, T.basis(i)(x), lw=2, label=f"$T_{i}$")
...
>>> plt.legend(loc="lower right")
>>> plt.show()
可以看出,“好的”部分已经变得微不足道。在使用切比雪夫多项式进行拟合时,我们希望使用x介于 -1 和 1 之间的区域,这就是窗口指定的区域。然而,要拟合的数据不太可能所有数据点都在该区间内,因此我们使用域来指定数据点所在的区间。拟合完成后,首先通过线性变换将域映射到窗口,然后使用映射的数据点完成通常的最小二乘拟合。拟合的窗口和域是返回序列的一部分,在计算值、导数等时自动使用。如果在调用中未指定它们,则拟合例程将使用默认窗口和包含所有数据点的最小域。下图显示了对噪声正弦曲线的拟合。
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from numpy.polynomial import Chebyshev as T
>>> np.random.seed(11)
>>> x = np.linspace(0, 2*np.pi, 20)
>>> y = np.sin(x) + np.random.normal(scale=.1, size=x.shape)
>>> p = T.fit(x, y, 5)
>>> plt.plot(x, y, 'o')
>>> xx, yy = p.linspace()
>>> plt.plot(xx, yy, lw=2)
>>> p.domain
array([0. , 6.28318531])
>>> p.window
array([-1., 1.])
>>> plt.show()