多项式简介
对于数域F\mathbb FF,若有∀i∈{1,2,3,⋯ ,n}\forall i\in\{1,2,3,\cdots,n \}∀i∈{1,2,3,⋯,n},则
f(x)=a0+a1x+a2x2+⋯+anx=∑i=1naixif(x)=a_0+a_1x+a_2x^2+\cdots+a_nx=\sum_{i=1}^na_ix^if(x)=a0+a1x+a2x2+⋯+anx=i=1∑naixi
为数域F\mathbb FF上的一个多项式.
多项式的度数
-
多项式最高项的次数称作多项式的次数
-
f(x)=∑i=1naixif(x)=\sum\limits_{i=1}^na_ix^if(x)=i=1∑naixi,其中an≠0a_n≠0an̸=0
-
记作deg f(x)=n\text{deg}\ f(x)=ndeg f(x)=n
多项式的系数表示
对于上述多项式f(x)f(x)f(x),有系数向量a⃗\vec{a}a与之映射
f(x)=∑i=1naixi⇔a⃗=(a1,a2,a3,⋯ ,an)f(x)=\sum_{i=1}^na_ix^i⇔\vec{a}=(a_1,a_2,a_3,\cdots,a_n)f(x)=i=1∑naixi⇔a=(a1,a2,a3,⋯,an)
我们称a⃗\vec{a}a为f(x)f(x)f(x)的系数表示
多项式卷积
首先定义
f(x)=∑i=1naixif(x)=\sum\limits_{i=1}^na_ix^if(x)=i=1∑naixi
g(x)=∑i=1nbixig(x)=\sum\limits_{i=1}^nb_ix^ig(x)=i=1∑nbixi
于是他们的卷积形式为
h(x)=(f⋅g)(x)=(∑i=1naixi)(∑i=1nbixi)h(x)=(f\cdot g)(x)=\Bigg(\sum\limits_{i=1}^na_ix^i\Bigg)\Bigg(\sum_{i=1}^nb_ix^i\Bigg)h(x)=(f⋅g)(x)=(i=1∑naixi)(i=1∑nbixi)
可得deg h(x)=2n\text{deg}\ h(x)=2ndeg h(x)=2n,于是令
h(x)=∑i=1ncixih(x)=\sum_{i=1}^nc_ix^ih(x)=i=1∑ncixi
cic_ici满足以下运算律
ci=∑j+k=iajbk=∑j=inajbi−jc_i=\sum_{j+k=i}a_jb_k=\sum_{j=i}^na_jb_{i-j}ci=j+k=i∑ajbk=j=i∑najbi−j
系数向量ccc为aaa与bbb的卷积,记作
c=a∗bc=a*bc=a∗b
多项式点值表示
设deg f(x)=n\text{deg}\ f(x)=ndeg f(x)=n,∀xi∈F (i=(1,2,3,⋯ ,n))\forall x_i\in\mathbb F\ (i=(1,2,3,\cdots,n))∀xi∈F (i=(1,2,3,⋯,n))
yi=f(xi)y_i=f(x_i)yi=f(xi)
则我们可以在数域F\mathbb FF的坐标系中绘制出nnn个点
(x1,y1),(x2,y2),(x3,y3),⋯ ,(xn,yn)(x_1,y_1),(x_2,y_2),(x_3,y_3),\cdots,(x_n,y_n)(x1,y1),(x2,y2),(x3,y3),⋯,(xn,yn)
同样与f(x)f(x)f(x)函数有映射关系。
不妨将这nnn个点带入矩阵运算
[1 x1 x12 ⋯ x1n1 x2 x22 ⋯ x2n1 x3 x32 ⋯ x3n⋮ ⋮ ⋮ ⋱ ⋮1 xn xn2 ⋯ xnn][a1a2a3⋮an]=[y1y2y3⋮yn]\begin{bmatrix}1\ \ x_1\ \ x_1^2\ \ \ \ \cdots\ \ x_1^n\\\\1\ \ x_2\ \ x_2^2\ \ \ \ \cdots\ \ x_2^n\\\\1\ \ x_3\ \ x_3^2\ \ \ \ \cdots\ \ x_3^n\\\\\vdots\ \ \ \ \vdots\ \ \ \ \vdots\ \ \ \ \ddots\ \ \vdots\\\\1\ \ x_n\ \ x_n^2\ \ \ \ \cdots\ \ x_n^n\end{bmatrix}\begin{bmatrix}a_1\\\\a_2\\\\a_3\\\\\vdots\\\\a_n\end{bmatrix}=\begin{bmatrix}y_1\\\\y_2\\\\y_3\\\\\vdots\\\\y_n\end{bmatrix}⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡1 x1 x12 ⋯ x1n1 x2 x22 ⋯ x2n1 x3 x32 ⋯ x3n⋮ ⋮ ⋮ ⋱ ⋮1 xn xn2 ⋯ xnn⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡a1a2a3⋮an⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡y1y2y3⋮yn⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
我们称最左边的矩阵为范德蒙矩阵.
多项式插值
对于多项式点值表示(x1,y1),(x2,y2),(x3,y3),⋯ ,(xn,yn)(x_1,y_1),(x_2,y_2),(x_3,y_3),\cdots,(x_n,y_n)(x1,y1),(x2,y2),(x3,y3),⋯,(xn,yn)
求它的系数向量表达式a⃗=(a1,a2,a3,⋯ ,an)\vec{a}=(a_1,a_2,a_3,\cdots,a_n)a=(a1,a2,a3,⋯,an)
这个过程叫做插值.
朴素的插值法可以利用范德蒙矩阵进行高斯消元,但是时间复杂度是无法承受的.
我们可以用拉格朗日插值求解
f(x)=∑i=1nyi∏i≠jx−xjxi−xjf(x)=\sum_{i=1}^ny_i\prod_{i≠j}\frac{x-x_j}{x_i-x_j}f(x)=i=1∑nyii̸=j∏xi−xjx−xj
复杂度优化到了n2n^2n2
点值求解多项式乘法
-
选取nnn个xix_ixi,带入计算点值,这一步是O(n2)O(n^2)O(n2)
-
计算点值的卷积,这一步是O(n)O(n)O(n)
-
插值计算系数向量,这一步是O(n2)O(n^2)O(n2)
所以最后的时间复杂度应该为O(n2)O(n^2)O(n2),朴素的点值求解与暴力计算一致。
接下来我们会学习以下知识点优化多项式卷积
-
复数单位根
-
原根
复数单位根
考虑方程
zn=1 (z∈C)z^n=1\ \ (z\in\mathbb C)zn=1 (z∈C)
我们按照极角排序,命第kkk个复数解为ωnk\omega_n^kωnk
由初等三角函数知识,我们解得
ωnk=coskn2π+isinkn2π (k∈Z)\omega_n^k=\cos\frac{k}{n}2\pi+i\sin\frac{k}{n}2\pi\ \ \ (k\in\mathbb Z)ωnk=cosnk2π+isinnk2π (k∈Z)
由欧拉公式可得
ωnk=e2πkin\omega_n^k=e^{\frac{2\pi ki}{n}}ωnk=en2πki
可以O(1)O(1)O(1)求解。
由三角函数的周期性(τ=2π)(\tau=2\pi)(τ=2π),不难考虑ωnk\omega_n^kωnk的周期性
-
ωnk=ωnk(modn)\omega_n^k=\omega_n^{k\pmod{n}}ωnk=ωnk(modn)
-
ωnn=ωn0=1\omega_n^n=\omega_n^0=1ωnn=ωn0=1
我们取k∈{0,2,3,⋯ ,n−1}k\in\{0,2,3,\cdots,n-1\}k∈{0,2,3,⋯,n−1}
有以下定理
相消定理
ωdndk=ωnk\omega_{dn}^{dk}=\omega_n^kωdndk=ωnk
充分证明
ωdndk=cosdkdn2π+isindkdn2π\omega_{dn}^{dk}=\cos\frac{dk}{dn}2\pi+i\sin\frac{dk}{dn}2\piωdndk=cosdndk2π+isindndk2π
=coskn2π+isinkn2π=ωnk\ \ \ \ \ \ \ \ \ \ \ \ =\cos\frac{k}{n}2\pi+i\sin\frac{k}{n}2\pi=\omega_n^k =cosnk2π+isinnk2π=ωnk
证毕.
此外补充几种常见形式
ω2nn=ω21=−1\omega_{2n}^n=\omega_2^1=-1ω2nn=ω21=−1
ω2nn+k=−ω2nk\omega_{2n}^{n+k}=-\omega_{2n}^kω2nn+k=−ω2nk
折半定理
nnn次单位根的平方的集合等于n2\dfrac{n}{2}2n次单位根的集合
A={(ωnk)2∣k∈Z}A=\{\left(\omega_n^k\right)^2|k\in \mathbb Z\}A={(ωnk)2∣k∈Z}
B={ωn2k∣k∈Z}B=\{\omega_{\frac{n}{2}}^k|k\in\mathbb Z\}B={ω2nk∣k∈Z}
A⊆B,B⊆A⇔A=BA⊆B,B⊆A⇔A=BA⊆B,B⊆A⇔A=B
(ωnk+n2)2=ωnn+2k=ωn2k=(ωnk)2\left(\omega_n^{k+\frac{n}{2}}\right)^2=\omega_n^{n+2k}=\omega_n^{2k}=\left(\omega_n^k\right)^2(ωnk+2n)2=ωnn+2k=ωn2k=(ωnk)2
求和引理
∑i=0n−1(ωnk)i=0 (k≠n)\sum_{i=0}^{n-1}\left(\omega_n^k\right)^i=0\ \ \ (k≠n)i=0∑n−1(ωnk)i=0 (k̸=n)
由等比数列的求和公式可得
∑i=0n−1(ωnk)i=1−(ωnk)n1−ωnk=1−11−ωnk=0\sum_{i=0}^{n-1}\left(\omega_n^k\right)^i=\frac{1-\left(\omega_n^k\right)^n}{1-\omega_n^k}=\frac{1-1}{1-\omega_n^k}=0i=0∑n−1(ωnk)i=1−ωnk1−(ωnk)n=1−ωnk1−1=0
对于(k=n)(k=n)(k=n)
∑k=0n−1(ωnk)i=n\sum_{k=0}^{n-1}\left(\omega_n^k\right)^i=nk=0∑n−1(ωnk)i=n
单位根的带入可以优化多项式卷积.
注:以下出现的nnn均为222的整数幂.
离散傅里叶变换
对于多项式f(x)=(a0,a2,a3,⋯ ,an−1)f(x)=(a_0,a_2,a_3,\cdots,a_{n-1})f(x)=(a0,a2,a3,⋯,an−1)
求解∀xi=ωnk (i=(0,1,2,⋯ ,n−1))\forall x_i=\omega_n^k\ \ \ (i=(0,1,2,\cdots,n-1))∀xi=ωnk (i=(0,1,2,⋯,n−1))处的点值.
可以令
yk=f(xk)=f(ωnk)y_k=f(x_k)=f(\omega_n^k)yk=f(xk)=f(ωnk)
此过程读作长度为nnn的离散傅里叶变换,记作
yk=DFTn(f)y_k=\text{DFT}_n(f)yk=DFTn(f)
快速傅里叶变换
考虑分治
f0(x)=(a0,a2,a4,⋯ ,an−2)f_0(x)=(a_0,a_2,a_4,\cdots,a_{n-2})f0(x)=(a0,a2,a4,⋯,an−2)
f1(x)=(a1,a3,a5,⋯ ,an−1)f_1(x)=(a_1,a_3,a_5,\cdots,a_{n-1})f1(x)=(a1,a3,a5,⋯,an−1)
于是有
f(x)=f0(x2)+xf1(x2)f(x)=f_0(x^2)+xf_1(x^2)f(x)=f0(x2)+xf1(x2)
对于k<n2k<\dfrac{n}{2}k<2n,我们有
f(ωnk)=f0(ωn2k)+ωnkf1(ωn2k)=f0(ωn2k)+ωnkf1(ωn2k)f(\omega_n^k)=f_0(\omega_n^{2k})+\omega_n^kf_1(\omega_n^{2k})=f_0(\omega_{\frac{n}{2}}^k)+\omega_n^kf_1(\omega_{\frac{n}{2}}^k)f(ωnk)=f0(ωn2k)+ωnkf1(ωn2k)=f0(ω2nk)+ωnkf1(ω2nk)
对于k>n2k>\dfrac{n}{2}k>2n,替换成k+n2k+\dfrac{n}{2}k+2n
f(ωnk+n2)=f0(ωn2k+n)+ωnk+n2f1(ωn2k+n)=f0(ωn2k)−ωnkf1(ωn2k)f\left(\omega_n^{k+\frac{n}{2}}\right)=f_0\left(\omega_{n}^{2k+n}\right)+\omega_{n}^{k+\frac{n}{2}}f_1(\omega_n^{2k+n})=f_0(\omega_{\frac{n}{2}}^k)-\omega_n^kf_1(\omega_{\frac{n}{2}}^k)f(ωnk+2n)=f0(ωn2k+n)+ωnk+2nf1(ωn2k+n)=f0(ω2nk)−ωnkf1(ω2nk)
令
yk=f(ωnk) yk[0]=f0(ωn2k) yk[1]=f1(ωn2k)y_k=f(\omega_n^k)\ \ \ y_k^{[0]}=f_0(\omega_{\frac{n}{2}}^k)\ \ \ y_k^{[1]}=f_1(\omega_{\frac{n}{2}}^k)yk=f(ωnk) yk[0]=f0(ω2nk) yk[1]=f1(ω2nk)
由上述结论,我们有
yk=yk[0]+ωnkyk[1]y_k=y_k^{[0]}+\omega_n^ky_k^{[1]}yk=yk[0]+ωnkyk[1]
yk+n2=yk[0]−ωnkyk[1]y_{k+\frac{n}{2}}=y_k^{[0]}-\omega_n^ky_k^{[1]}yk+2n=yk[0]−ωnkyk[1]
上述操作为蝴蝶操作,其中ωnk\omega_n^kωnk为旋转因子.
快速傅里叶变换时间复杂度分析
令T(n)T(n)T(n)为长度为nnn的快速傅里叶变换的时间复杂度
递归求得yk[0],yk[1]y_k^{[0]},y_k^{[1]}yk[0],yk[1],规模均为n2\dfrac{n}{2}2n
即
T(n)=2T(n2)+O(n)=O(nlog2n)T(n)=2T\left(\frac{n}{2}\right)+O(n)=O(n\log_2n)T(n)=2T(2n)+O(n)=O(nlog2n)
快速傅里叶逆变换
设有向量(c0,c1,c2,⋯ ,cn−1)(c_0,c_1,c_2,\cdots,c_{n-1})(c0,c1,c2,⋯,cn−1),满足
ck=∑i=0n−1yi(ωn−k)ic_k=\sum_{i=0}^{n-1}y_i(\omega_n^{-k})^ick=i=0∑n−1yi(ωn−k)i
展开yiy_iyi,我们有
ck=∑i=0n−1∑j=0n−1aj(ωni)j(wn−k)ic_k=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(\omega_n^i)^j(w_n^{-k})^ick=i=0∑n−1j=0∑n−1aj(ωni)j(wn−k)i
=∑i=0n−1∑j=0n−1aj(ωnj)i(ωn−k)i\ \ \ \ =\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(\omega_n^j)^i(\omega_n^{-k})^i =i=0∑n−1j=0∑n−1aj(ωnj)i(ωn−k)i
=∑i=0n−1∑j=0n−1aj(wnj−k)i =\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(w_n^{j-k})^i\ \ \ =i=0∑n−1j=0∑n−1aj(wnj−k)i
=∑j=0n−1aj∑i=0n−1(wnj−k)i =\sum_{j=0}^{n-1}a_j\sum_{i=0}^{n-1}(w_n^{j-k})^i\ \ \ =j=0∑n−1aji=0∑n−1(wnj−k)i
考虑到求和定理
∑i=1n−1(ωnk)i=0 (k≠n)\sum_{i=1}^{n-1}(\omega_n^k)^i=0\ \ \ (k≠n)i=1∑n−1(ωnk)i=0 (k̸=n)
取j=kj=kj=k
ck=nykc_k=ny_kck=nyk
求解以下nnn的逆元就可以了
yk=1ncky_k=\frac{1}{n}c_kyk=n1ck
于是快速傅里叶逆变换只需要取共轭复数就行了。
迭代求解
我们需要这样求解

对于n=2kn=2^kn=2k,考虑第iii次分治
二进制第iii位为000,放入左侧,最终位置的二进制的k−ik-ik−i位为000
二进制第iii位为111,放入右侧,最终位置的二进制的k−ik-ik−i位为111
知识点讲完了,代码如下
#include <cstdio>
#include <complex>
using namespace std;
const int N = 1e+7 + 1;
const double Pi = acos(-1);
int n, m, rev[N];
complex<double> F[N], G[N], H[N];
int invert(int n) {
int bit = 1;
while((1 << bit) < n) bit++;
return (1 << bit);
}
int getint() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
return x * f;
}
void FFT(complex<double> *A, int n, int inv) {
int bit = 1;
while((1 << bit) < n) bit++;
for(int i = 0; i < n; i++) {
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
if(i < rev[i]) swap(A[i], A[rev[i]]);
}
for(int mid = 1; mid < n; mid <<= 1) {
complex<double> temp(cos(Pi / mid), inv * sin(Pi / mid));
for(int i = 0; i < n; i += mid << 1) {
complex<double> omega(1, 0);
for(int j = 0; j < mid; j++, omega *= temp) {
complex<double> x = A[i + j], y = omega * A[i + j + mid];
A[i + j] = x + y;
A[i + j + mid] = x - y;
}
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 0; i <= n; i++) F[i].real(getint());
for(int i = 0; i <= m; i++) G[i].real(getint());
FFT(F, invert(n + m), 1);
FFT(G, invert(n + m), 1);
for(int i = 0; i <= invert(n + m); i++) H[i] = F[i] * G[i];
FFT(H, invert(n + m), -1);
for(int i = 0; i <= n + m; i++)
printf("%d ", (int)(H[i].real() / invert(n + m) + 0.5));
return 0;
}

本文介绍了多项式的基本概念,包括度数、系数表示和卷积,并详细探讨了点值表示、插值以及如何通过复数单位根优化多项式乘法。重点讲解了离散傅里叶变换(DFT)、快速傅里叶变换(FFT)及其时间复杂度分析,以及FFT的逆变换。最后提到了迭代求解的方法。
2050

被折叠的 条评论
为什么被折叠?



