目录
多项式乘法问题
多项式乘法是将两个或多个多项式相乘,得到一个新的多项式。
一个多项式通常写成如下的形式(系数表示): P ( x ) = a n x n + a n − 1 x n − 1 + … + a 1 x + a 0 P(x) = a_nx^n + a_{n-1}x^{n-1} + \ldots + a_1x + a_0 P(x)=anxn+an−1xn−1+…+a1x+a0其中 a i a_{i} ai是多项式的系数, x x x 是变量, n n n 是多项式的次数。
假设我们有两个多项式: P ( x ) = a n x n + a n − 1 x n − 1 + … + a 1 x + a 0 Q ( x ) = b m x m + b m − 1 x m − 1 + … + b 1 x + b 0 P(x) = a_nx^n + a_{n-1}x^{n-1} + \ldots + a_1x + a_0 \\ Q(x) = b_mx^m + b_{m-1}x^{m-1} + \ldots + b_1x + b_0 P(x)=anxn+an−1xn−1+…+a1x+a0Q(x)=bmxm+bm−1xm−1+…+b1x+b0它们的乘积 R ( x ) = P ( x ) ⋅ Q ( x ) R(x)=P(x)⋅Q(x) R(x)=P(x)⋅Q(x) 可以通过将 P ( x ) P(x) P(x) 中的每一项与 Q ( x ) Q(x) Q(x) 中的每一项相乘,然后将结果合并得到: R ( x ) = ∑ i = 0 n ∑ j = 0 m a i b j x i + j R(x) = \sum_{i=0}^{n} \sum_{j=0}^{m} a_ib_jx^{i+j} R(x)=i=0∑nj=0∑maibjxi+j其中, i i i 和 j j j 分别表示 P ( x ) P(x) P(x) 和 Q ( x ) Q(x) Q(x) 中各项的指数, a i a_{i} ai 和 b j b_{j} bj 分别是对应的系数。
暴力求解
对于规模相当的两个多项式相乘,对于 P ( x ) P(x) P(x) 的每个项,都要与 Q ( x ) Q(x) Q(x) 相乘,并进行结果的累加。这个过程的算法复杂度很容易得知为 O ( n 2 ) O(n^{2}) O(n2) 。
#include <iostream>
#include <vector>
using namespace std;
// 结构体表示多项式的一项
struct Term {
int coefficient;
int exponent;
};
// 函数实现多项式相乘
vector<Term> multiplyPolynomials(vector<Term>& poly1, vector<Term>& poly2) {
vector<Term> result;
for (int i = 0; i < poly1.size(); ++i) {
for (int j = 0; j < poly2.size(); ++j) {
int newCoefficient = poly1[i].coefficient * poly2[j].coefficient;
int newExponent = poly1[i].exponent + poly2[j].exponent;
bool termExists = false;
for (int k = 0; k < result.size(); ++k) {
if (result[k].exponent == newExponent) {
result[k].coefficient += newCoefficient;
termExists = true;
break;
}
}
if (!termExists) {
Term newTerm = {newCoefficient, newExponent};
result.push_back(newTerm);
}
}
}
return result;
}
int main() {
vector<Term> poly1 = {{2, 2}, {3, 1}, {1, 0}};
vector<Term> poly2 = {{4, 1}, {1, 0}};
vector<Term> result = multiplyPolynomials(poly1, poly2);
cout << "Result: ";
for (int i = 0; i < result.size(); ++i) {
cout << result[i].coefficient << "x^" << result[i].exponent;
if (i < result.size() - 1) {
cout << " + ";
}
}
cout << endl;
return 0;
}
背景展开
1. 系数表示值计算复杂度
对于给定多项式和值 x x x : A ( x ) = a n − 1 x n − 1 + … + a 1 x + a 0 B ( x ) = b n − 1 x n − 1 + … + b 1 x + b 0 A(x) = a_{n-1}x^{n-1} + \ldots + a_1x + a_0 \\ \\ B(x) = b_{n-1}x^{n-1} + \ldots + b_1x + b_0 A(x)=an−1xn−1+…+a1x+a0B(x)=bn−1xn−1+…+b1x+b0- 加法计算复杂度 O ( n ) O(n) O(n)
A ( x ) + B ( x ) = ( a 0 + b 0 ) + ( a 1 + b 1 ) x + . . . ( a n − 1 + b n − 1 ) x n − 1 A(x)+B(x)=(a_{0}+b_{0})+(a_{1}+b_{1})x+...(a_{n-1}+b_{n-1})x^{n-1} A(x)+B(x)=(a0+b0)+(a1+b1)x+...(an−1+bn−1)xn−1 - 乘法计算复杂度 采用Horner (霍氏法则) O ( n ) O(n) O(n) A ( x ) = a 0 + ( x ( a 1 + x ( a 2 + . . . + x ( a n − 2 + x ( a n − 1 ) ) . . . ) ) A(x) = a_0 + (x(a_1 + x(a_2 + ... + x(a_{n−2} + x(a_{n−1}))...)) A(x)=a0+(x(a1+x(a2+...+x(an−2+x(an−1))...))
2. 点值法表示多项式
当我们用点值法表示多项式时,我们通常是指将多项式表示为一组在特定点
x
x
x 处的函数值。
假设我们有一个多项式 P ( x ) P(x) P(x) ,我们可以在一些特定的点 x 1 , x 2 , … , x n x_1,x_2,…,x_n x1,x2,…,xn 处计算它的值 P ( x 1 ) , P ( x 2 ) , … , P ( x n ) P(x_1),P(x_2),…,P(x_n) P(x1),P(x2),…,P(xn) 。这些点和相应的函数值可以表示为: ( x 1 , P ( x 1 ) ) ( x 2 , P ( x 2 ) ) . . . . . . ( x n , P ( x n ) ) (x_1, P(x_1)) (x_2, P(x_2)) ...... (x_n, P(x_n)) (x1,P(x1))(x2,P(x2))......(xn,P(xn))一个 n n n 次的复系数单变量多项式恰好有 n n n 个复根。
3. 点值表示值计算复杂度
对于给定多项式点值列 x x x : A ( x ) : ( x 0 , y 0 ) , . . . , ( x n − 1 , y n − 1 ) B ( x ) : ( x 0 , z 0 ) , . . . , ( x n − 1 , z n − 1 ) A(x) : (x_0,y_0),...,(x_{n-1},y_{n-1}) \\ \\ B(x) : (x_0,z_0),...,(x_{n-1},z_{n-1}) A(x):(x0,y0),...,(xn−1,yn−1)B(x):(x0,z0),...,(xn−1,zn−1)- 加法计算复杂度 O ( n ) O(n) O(n)
A ( x ) + B ( x ) : ( x 0 , y 0 + z 0 ) , . . . , ( x n − 1 , y n − 1 + z n − 1 ) A(x)+B(x):(x_{0},y_{0}+z_0),...,(x_{n-1},y_{n-1}+z_{n-1}) A(x)+B(x):(x0,y0+z0),...,(xn−1,yn−1+zn−1) - 乘法计算复杂度 O ( n ) O(n) O(n) 但是需要用 2 n 2n 2n 个点表示 A ( x ) A(x) A(x) 和 B ( x ) B(x) B(x) A ( x ) × B ( x ) : ( x 0 , y 0 × z 0 ) , . . . , ( x 2 n − 1 , y 2 n − 1 × z 2 n − 1 ) A(x) \times B(x):(x_0,y_0 \times z_0),...,(x_{2n-1},y_{2n-1} \times z_{2n-1}) A(x)×B(x):(x0,y0×z0),...,(x2n−1,y2n−1×z2n−1)- 计算给定点 s s s ,使用拉格朗日公式 O ( n 2 ) O(n^2) O(n2): A ( s ) = ∑ k = 0 n − 1 y k ∏ j ≠ k ( s − x j ) ∏ j ≠ k ( x k − x j ) A(s)=\sum_{k=0}^{n-1}y_k\frac{\prod _{j\ne k}(s-x_j)}{\prod _{j\ne k}(x_k-x_j)} A(s)=k=0∑n−1yk∏j=k(xk−xj)∏j=k(s−xj)
4. 系数法和点值法比较
表示方法 | 多项式相乘 | 多项式算值 |
---|---|---|
系数法 | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) |
点值法 | O ( n ) O(n) O(n) | O ( n 2 ) O(n^2) O(n2) |
如果能够同时利用 系数法的算值 和 点值法的相乘 的低复杂度计算。那么对于整个多项式相乘就有了一个复杂度比较低的解决方式。其中的关键就是找到两者之间的沟通桥梁。
5. 系数法和点值法的转换
系数法 → \rightarrow → 点值法(通过矩阵向量乘法或 n n n 次霍氏乘法) O ( n 2 ) O(n^2) O(n2):
给定多项式 a ( x ) = a 0 + a 1 x + … + a n − 1 x n − 1 a (x) = a_0 + a_1x +…+ a_{n-1} x_{n - 1} a(x)=a0+a1x+…+an−1xn−1,在 n n n 个不同的点处求值 x 0 , … , x n − 1 x_0,…, x_{n-1} x0,…,xn−1: [ y 0 y 1 y 2 ⋮ y n − 1 ] = [ 1 x 0 x 0 2 … x 0 n − 1 1 x 1 x 1 2 … x 1 n − 1 1 x 2 x 2 2 … x 2 n − 1 ⋮ ⋮ ⋮ ⋱ ⋮ 1 x n − 1 x n − 1 2 … x n − 1 n − 1 ] [ a 0 a 1 a 2 ⋮ a n − 1 ] \begin{bmatrix}y_0\\y_1\\y_2\\\vdots \\y_{n-1}\end{bmatrix} = \begin{bmatrix} 1& x_0& x_0^2 & \dots &x_0^{n-1} \\ 1& x_1& x_1^2 & \dots & x_1^{n-1}\\ 1& x_2& x_2^2 & \dots & x_2^{n-1}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 1& x_{n-1} & x_{n-1}^2 & \dots & x_{n-1}^{n-1} \end{bmatrix}\begin{bmatrix}a_0\\a_1\\a_2\\\vdots\\a_{n-1}\end{bmatrix} y0y1y2⋮yn−1 = 111⋮1x0x1x2⋮xn−1x02x12x22⋮xn−12………⋱…x0n−1x1n−1x2n−1⋮xn−1n−1 a0a1a2⋮an−1
点值法 → \rightarrow → 系数法 (高斯消元 O ( n 3 ) O(n^3) O(n3) / 快速矩阵 O ( n 2.38 ) O(n^{2.38}) O(n2.38)):
给定n个不同的点 x 0 , … , x n − 1 x_0,…, x_{n-1} x0,…,xn−1和值 y 0 , … , y n − 1 y_0,…, y_{n - 1} y0,…,yn−1,求唯一多项式 A ( x ) = a 0 + a 1 x + … + a n − 1 x n − 1 A(x) = a_0 + a_1x +…+ a_{n-1} x_{n-1} A(x)=a0+a1x+…+an−1xn−1: [ a 0 a 1 a 2 ⋮ a n − 1 ] = [ 1 x 0 x 0 2 … x 0 n − 1 1 x 1 x 1 2 … x 1 n − 1 1 x 2 x 2 2 … x 2 n − 1 ⋮ ⋮ ⋮ ⋱ ⋮ 1 x n − 1 x n − 1 2 … x n − 1 n − 1 ] − 1 [ y 0 y 1 y 2 ⋮ y n − 1 ] \begin{bmatrix}a_0\\a_1\\a_2\\\vdots\\a_{n-1}\end{bmatrix} = \begin{bmatrix} 1& x_0& x_0^2 & \dots &x_0^{n-1} \\ 1& x_1& x_1^2 & \dots & x_1^{n-1}\\ 1& x_2& x_2^2 & \dots & x_2^{n-1}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 1& x_{n-1} & x_{n-1}^2 & \dots & x_{n-1}^{n-1} \end{bmatrix}^{-1}\begin{bmatrix}y_0\\y_1\\y_2\\\vdots \\y_{n-1}\end{bmatrix} a0a1a2⋮an−1 = 111⋮1x0x1x2⋮xn−1x02x12x22⋮xn−12………⋱…x0n−1x1n−1x2n−1⋮xn−1n−1 −1 y0y1y2⋮yn−1 其中等式右侧有关 x i x_i xi 的矩阵为范德蒙矩阵,当 x i x_i xi 不同时可逆。
通过直接线性代数的计算,并不能在低于 O ( n 2 ) O(n^2) O(n2) 的复杂度下完成两种表示方式的相互转换,甚至会增加额外的计算代价。这是难以接受的,因此我们需要寻找一种能够高效完成两种表示方式转换的算法,并且复杂度最好能够控制在 O ( n 2 ) O(n^2) O(n2) 以内。
快速傅里叶变换 FFT
可以使用快速傅里叶变换
(FFT)来实现系数表示和点值表示的转换。FFT 可以在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 的时间内将多项式从系数形式转换为点值形式,以及从点值形式转换回系数形式。
1. 多项式乘法的分治
给定多项式如下: A ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 + a 4 x 4 + a 5 x 5 + a 6 x 6 + a 7 x 7 A(x)=a_0+a_1x+a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6+a_7x^7 A(x)=a0+a1x+a2x2+a3x3+a4x4+a5x5+a6x6+a7x7可以有如下两种分治测略:
拆分成低次和高次: A l o w ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 A h i g h ( x ) = a 4 + a 5 x + a 6 x 2 + a 7 x 3 A_{low}(x)=a_0+a_1x+a_2x^2+a_3x^3 \\ A_{high}(x)=a_4+a_5x+a_6x^2+a_7x^3 Alow(x)=a0+a1x+a2x2+a3x3Ahigh(x)=a4+a5x+a6x2+a7x3合并 A ( x ) = A l o w ( x ) + x 4 A h i g h ( x ) A(x)=A_{low}(x)+x^4A_{high}(x) A(x)=Alow(x)+x4Ahigh(x)
拆分成奇偶次: A e v e n ( x ) = a 0 + a 2 x + a 4 x 2 + a 6 x 3 A o d d ( x ) = a 1 + a 3 x + a 5 x 2 + a 7 x 3 A_{even}(x)=a_0+a_2x+a_4x^2+a_6x^3 \\ A_{odd}(x)=a_1+a_3x+a_5x^2+a_7x^3 Aeven(x)=a0+a2x+a4x2+a6x3Aodd(x)=a1+a3x+a5x2+a7x3合并 A ( x ) = A e v e n ( x 2 ) + x A o d d ( x 2 ) A(x)=A_{even}(x^2)+xA_{odd}(x^2) A(x)=Aeven(x2)+xAodd(x2)
2. 取值的直觉
给定多项式
A
(
x
)
=
a
0
+
a
1
x
+
…
+
a
n
−
1
x
n
−
1
A (x) = a_0 + a_1x +…+ a_{n-1} x_{n - 1}
A(x)=a0+a1x+…+an−1xn−1,在
n
n
n 个不同的点处求值
x
0
,
…
,
x
n
−
1
x_0,…, x_{n-1}
x0,…,xn−1
关于
x
i
x_i
xi 的取值怎么取,一般性的直觉会选
0
,
±
1
,
±
2...
0,\pm 1,\pm 2...
0,±1,±2...。这是没问题的,直觉告诉我们选择对称的不同数值可以减少计算量。下面我们来验证这一直觉是否合理:
假如使用奇偶拆分法拆分
A
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
a
3
x
3
+
a
4
x
4
+
a
5
x
5
+
a
6
x
6
+
a
7
x
7
A(x)=a_0+a_1x+a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6+a_7x^7
A(x)=a0+a1x+a2x2+a3x3+a4x4+a5x5+a6x6+a7x7
A
e
v
e
n
(
x
)
=
a
0
+
a
2
x
+
a
4
x
2
+
a
6
x
3
A
o
d
d
(
x
)
=
a
1
+
a
3
x
+
a
5
x
2
+
a
7
x
3
A_{even}(x)=a_0+a_2x+a_4x^2+a_6x^3 \\ A_{odd}(x)=a_1+a_3x+a_5x^2+a_7x^3
Aeven(x)=a0+a2x+a4x2+a6x3Aodd(x)=a1+a3x+a5x2+a7x3
A
(
x
)
=
A
e
v
e
n
(
x
2
)
+
x
A
o
d
d
(
x
2
)
A
(
−
x
)
=
A
e
v
e
n
(
x
2
)
−
x
A
o
d
d
(
x
2
)
A(x)=A_{even}(x^2)+xA_{odd}(x^2) \\ A(-x)=A_{even}(x^2)-xA_{odd}(x^2)
A(x)=Aeven(x2)+xAodd(x2)A(−x)=Aeven(x2)−xAodd(x2)带入
±
1
\pm 1
±1
A
(
1
)
=
A
e
v
e
n
(
1
)
+
x
A
o
d
d
(
1
)
A
(
−
1
)
=
A
e
v
e
n
(
1
)
−
A
o
d
d
(
1
)
A(1)=A_{even}(1)+xA_{odd}(1) \\ A(-1)=A_{even}(1)-A_{odd}(1)
A(1)=Aeven(1)+xAodd(1)A(−1)=Aeven(1)−Aodd(1)可以通过在一点处求 2 个
n
−
1
2
\frac{n-1}{2}
2n−1 次多项式来求 2 个点上
n
−
1
n-1
n−1 次多项式的值。
如果在此基础上再引入复数 i i i 那么你将会看到如下光景: A ( 1 ) = A e v e n ( 1 ) + A o d d ( 1 ) A ( − 1 ) = A e v e n ( 1 ) − A o d d ( 1 ) A ( i ) = A e v e n ( − 1 ) + i A o d d ( − 1 ) A ( − i ) = A e v e n ( − 1 ) − i A o d d ( − 1 ) A(1)=A_{even}(1)+A_{odd}(1) \\ A(-1)=A_{even}(1)-A_{odd}(1) \\ A(i)=A_{even}(-1)+iA_{odd}(-1) \\ A(-i)=A_{even}(-1)-iA_{odd}(-1) A(1)=Aeven(1)+Aodd(1)A(−1)=Aeven(1)−Aodd(1)A(i)=Aeven(−1)+iAodd(−1)A(−i)=Aeven(−1)−iAodd(−1)可以通过在二个点上求 2 个 n − 1 2 \frac{n-1}{2} 2n−1 次多项式来求 4 个点上 n − 1 n-1 n−1 次多项式的值。
利用对称性(相反数),我们可以实现一穿二的计算速度,换句话说,计算速度翻倍。
3. 离散傅里叶 DFT 和单位根
单位根(Roots of Unity)是复数域中的一组特殊复数,它们是复数平面上的单位长度向量,分布在圆周上,且相邻单位根之间的夹角相等,相邻单位根之间的夹角为
2
π
/
n
2π/n
2π/n 弧度。
具体来说,一个 n n n 次单位根是复数 ω \omega ω,满足条件: ω n = 1 \omega^n=1 ωn=1
单位根是使得其 n 次幂等于1的复数。
单位根可以用复数的指数形式表示为: ω k = e 2 π i k n \omega_k = e^{\frac{2πik}{n}} ωk=en2πik
回到下面这个式子:
给定多项式 a ( x ) = a 0 + a 1 x + … + a n − 1 x n − 1 a (x) = a_0 + a_1x +…+ a_{n-1} x_{n - 1} a(x)=a0+a1x+…+an−1xn−1,在 n n n 个不同的点处求值 x 0 , … , x n − 1 x_0,…, x_{n-1} x0,…,xn−1: [ y 0 y 1 y 2 ⋮ y n − 1 ] = [ 1 x 0 x 0 2 … x 0 n − 1 1 x 1 x 1 2 … x 1 n − 1 1 x 2 x 2 2 … x 2 n − 1 ⋮ ⋮ ⋮ ⋱ ⋮ 1 x n − 1 x n − 1 2 … x n − 1 n − 1 ] [ a 0 a 1 a 2 ⋮ a n − 1 ] \begin{bmatrix}y_0\\y_1\\y_2\\\vdots \\y_{n-1}\end{bmatrix} = \begin{bmatrix} 1& x_0& x_0^2 & \dots &x_0^{n-1} \\ 1& x_1& x_1^2 & \dots & x_1^{n-1}\\ 1& x_2& x_2^2 & \dots & x_2^{n-1}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 1& x_{n-1} & x_{n-1}^2 & \dots & x_{n-1}^{n-1} \end{bmatrix}\begin{bmatrix}a_0\\a_1\\a_2\\\vdots\\a_{n-1}\end{bmatrix} y0y1y2⋮yn−1 = 111⋮1x0x1x2⋮xn−1x02x12x22⋮xn−12………⋱…x0n−1x1n−1x2n−1⋮xn−1n−1 a0a1a2⋮an−1
将其中的 x i x_i xi 用单位根 ω \omega ω 进行替换可得:
[ y 0 y 1 y 2 ⋮ y n − 1 ] = [ 1 1 1 … 1 1 ω 1 ω 2 … ω n − 1 1 ω 2 ω 4 … ω 2 ( n − 1 ) ⋮ ⋮ ⋮ ⋱ ⋮ 1 ω n − 1 ω 2 ( n − 1 ) … ω ( n − 1 ) ( n − 1 ) ] [ a 0 a 1 a 2 ⋮ a n − 1 ] \begin{bmatrix}y_0\\y_1\\y_2\\\vdots \\y_{n-1}\end{bmatrix} =\begin{bmatrix} 1& 1& 1 & \dots &1\\ 1& \omega^1& \omega^2 & \dots & \omega^{n-1}\\ 1& \omega^2& \omega^4 & \dots & \omega^{2(n-1)}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 1& \omega^{n-1} & \omega^{2(n-1)} & \dots & \omega^{(n-1)(n-1)} \end{bmatrix}\begin{bmatrix}a_0\\a_1\\a_2\\\vdots\\a_{n-1}\end{bmatrix} y0y1y2⋮yn−1 = 111⋮11ω1ω2⋮ωn−11ω2ω4⋮ω2(n−1)………⋱…1ωn−1ω2(n−1)⋮ω(n−1)(n−1) a0a1a2⋮an−1 其中 y k = A ( ω k ) y_k=A(\omega^k) yk=A(ωk), ω i \omega^i ωi 为傅里叶矩阵。
4. FFT
给定多项式 A ( x ) = a 0 + a 1 x + … + a n − 1 x n − 1 A (x) = a_0 + a_1x +…+ a_{n-1} x_{n - 1} A(x)=a0+a1x+…+an−1xn−1,在 n t h n^{th} nth 单位根处求值 ω 0 , ω 1 , … , ω n − 1 \omega^0,\omega^1,…, \omega^{n-1} ω0,ω1,…,ωn−1
拆解:
A
e
v
e
n
(
x
)
=
a
0
+
a
2
x
+
a
4
x
2
+
.
.
.
+
a
n
−
2
x
n
/
2
−
1
A
o
d
d
(
x
)
=
a
1
+
a
3
x
+
a
5
x
2
+
.
.
.
+
a
n
−
1
x
n
/
2
−
1
A_{even}(x)=a_0+a_2x+a_4x^2+...+a_{n-2}x^{n/2-1} \\ A_{odd}(x)=a_1+a_3x+a_5x^2+...+a_{n-1}x^{n/2-1}
Aeven(x)=a0+a2x+a4x2+...+an−2xn/2−1Aodd(x)=a1+a3x+a5x2+...+an−1xn/2−1
A
(
x
)
=
A
e
v
e
n
(
x
2
)
+
x
A
o
d
d
(
x
2
)
A
(
−
x
)
=
A
e
v
e
n
(
x
2
)
−
x
A
o
d
d
(
x
2
)
A(x)=A_{even}(x^2)+xA_{odd}(x^2) \\ A(-x)=A_{even}(x^2)-xA_{odd}(x^2)
A(x)=Aeven(x2)+xAodd(x2)A(−x)=Aeven(x2)−xAodd(x2)
求解:求
A
e
v
e
n
(
x
)
A_{even}(x)
Aeven(x)和
A
o
d
d
(
x
)
A_{odd}(x)
Aodd(x)和在单位的
n
t
h
2
\frac{n^{th}}{2}
2nth 根处的值:
v
0
,
v
1
,
…
,
v
n
/
2
−
1
v^0,v^1,…,v^{n/ 2-1}
v0,v1,…,vn/2−1
合并:
y
k
=
A
(
ω
k
)
=
A
e
v
e
n
(
v
k
)
+
ω
k
A
o
d
d
(
v
k
)
,
0
≤
k
≤
n
/
2
y
k
+
n
/
2
=
A
(
ω
k
+
n
/
2
)
=
A
e
v
e
n
(
v
k
)
−
ω
k
A
o
d
d
(
v
k
)
,
0
≤
k
≤
n
/
2
y_k=A(\omega^k)=A_{even}(v^k)+\omega^kA_{odd}(v^k),0\le k \le n/2 \\ y_{k+n/2}=A(\omega^{k+n/2})=A_{even}(v^k)-\omega^kA_{odd}(v^k),0\le k \le n/2
yk=A(ωk)=Aeven(vk)+ωkAodd(vk),0≤k≤n/2yk+n/2=A(ωk+n/2)=Aeven(vk)−ωkAodd(vk),0≤k≤n/2其中
v
k
=
(
ω
k
)
2
,
ω
k
+
n
/
2
=
−
ω
k
v^k=(\omega^k)^2,\omega^{k+n/2}=-\omega^k
vk=(ωk)2,ωk+n/2=−ωk
FFT算法在
O
(
n
l
o
g
n
)
O(n log n)
O(nlogn) 个算术运算和
O
(
n
)
O(n)
O(n) 个额外空间中,对每个单位的
n
n
n 次方根计算
n
−
1
n - 1
n−1 次多项式
T
(
n
)
=
{
Θ
(
1
)
if
x
=
1
2
T
(
n
/
2
)
+
Θ
(
n
)
if
x
>
1
T(n)=\left\{\begin{matrix} \Theta (1) & \text{if } x=1\\ 2T(n/2)+\Theta (n) & \text{if } x>1 \end{matrix}\right.
T(n)={Θ(1)2T(n/2)+Θ(n)if x=1if x>1
5. Inverse FFT
给定 n n n 个不同的点 x 0 , … , x n − 1 x_0,…, x_{n-1} x0,…,xn−1 和值 y 0 , … , y n − 1 y_0,…, y_{n - 1} y0,…,yn−1 ,求唯一多项式 a 0 + a 1 x + … + a n − 1 x n − 1 a_0 + a_1x +…+ a_{n-1} x_ {n-1} a0+a1x+…+an−1xn−1 ,在给定的点上有给定的值。 [ y 0 y 1 y 2 ⋮ y n − 1 ] = [ 1 1 1 … 1 1 ω 1 ω 2 … ω n − 1 1 ω 2 ω 4 … ω 2 ( n − 1 ) ⋮ ⋮ ⋮ ⋱ ⋮ 1 ω n − 1 ω 2 ( n − 1 ) … ω ( n − 1 ) ( n − 1 ) ] − 1 [ a 0 a 1 a 2 ⋮ a n − 1 ] \begin{bmatrix}y_0\\y_1\\y_2\\\vdots \\y_{n-1}\end{bmatrix} =\begin{bmatrix} 1& 1& 1 & \dots &1\\ 1& \omega^1& \omega^2 & \dots & \omega^{n-1}\\ 1& \omega^2& \omega^4 & \dots & \omega^{2(n-1)}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 1& \omega^{n-1} & \omega^{2(n-1)} & \dots & \omega^{(n-1)(n-1)} \end{bmatrix}^{-1}\begin{bmatrix}a_0\\a_1\\a_2\\\vdots\\a_{n-1}\end{bmatrix} y0y1y2⋮yn−1 = 111⋮11ω1ω2⋮ωn−11ω2ω4⋮ω2(n−1)………⋱…1ωn−1ω2(n−1)⋮ω(n−1)(n−1) −1 a0a1a2⋮an−1
多项式乘法快速求解
#include <iostream>
#include <vector>
#include <complex>
#include <cmath>
using namespace std;
typedef complex<double> Complex;
// FFT算法
void fft(vector<Complex>& a, bool invert) {
int n = a.size();
if (n <= 1) return;
vector<Complex> a0(n / 2), a1(n / 2);
for (int i = 0, j = 0; i < n; i += 2, j++) {
a0[j] = a[i];
a1[j] = a[i + 1];
}
fft(a0, invert);
fft(a1, invert);
double ang = 2 * M_PI / n * (invert ? -1 : 1);
Complex w(1), wn(cos(ang), sin(ang));
for (int i = 0; i < n / 2; i++) {
Complex t = w * a1[i];
a[i] = a0[i] + t;
a[i + n / 2] = a0[i] - t;
if (invert) {
a[i] /= 2;
a[i + n / 2] /= 2;
}
w *= wn;
}
}
// 多项式乘法
vector<int> multiplyPolynomials(vector<int>& poly1, vector<int>& poly2) {
int n = 1;
while (n < poly1.size() + poly2.size() - 1) n *= 2;
vector<Complex> a(n), b(n);
for (int i = 0; i < poly1.size(); i++) a[i] = poly1[i];
for (int i = 0; i < poly2.size(); i++) b[i] = poly2[i];
fft(a, false);
fft(b, false);
for (int i = 0; i < n; i++) a[i] *= b[i];
fft(a, true);
vector<int> result(n);
for (int i = 0; i < n; i++) result[i] = round(a[i].real());
return result;
}
int main() {
vector<int> poly1 = {1, 2, 3};
vector<int> poly2 = {4, 5, 6};
vector<int> result = multiplyPolynomials(poly1, poly2);
cout << "Result: ";
for (int i = 0; i < result.size(); ++i) {
cout << result[i] << " ";
}
cout << endl;
return 0;
}