先讲结论
离散傅里叶变换:将系数表示(Coefficient Representation)的多项式转换成点值表示的多项式
快速傅里叶变换(Fast Fourier Transform)是能够在
O
(
n
l
g
n
)
O(nlg n)
O(nlgn)的时间复杂度内完成离散傅里叶变换的一种算法
问题引入
计算一个多项式乘法:
A
(
x
)
=
x
2
+
2
x
+
1
A(x) = x^2 + 2x + 1
A(x)=x2+2x+1
B
(
x
)
=
x
2
−
2
x
+
1
B(x) = x^2 - 2x + 1
B(x)=x2−2x+1
P
(
x
)
=
A
(
x
)
×
B
(
x
)
=
x
4
+
2
x
2
+
1
P(x) = A(x) ×B(x) = x^4 + 2x^2 + 1
P(x)=A(x)×B(x)=x4+2x2+1
值得注意的是在计算机中对多项式的存储一般是系数表示法,如下:
A
(
x
)
A(x)
A(x)在计算机中的表示应该是[2,3,1]
B
(
x
)
B(x)
B(x)在计算机中的表示应该是[0,2,3]
想要计算
P
(
x
)
P(x)
P(x)的表示,需要通过一个二重循环,
由此可见,在计算系数表示的矩阵时需要的时间复杂度应该为
O
(
n
2
)
O(n^2)
O(n2)
点值表示
一个多项式除了用系数表示外,还可以通过多项式函数上的若干个点表示,规范一些的描述就是:
平面上n+1个不同的点可以唯一确定一个n次多项式函数
对于这一点可以用矩阵来证明:
将n+1个点带入到待定系数的n解多项式中可以得到n+1个等式;
写成矩阵的形式,因为各个点都不相同,化简成行阶梯形矩阵可以看到系数矩阵的秩等于增广矩阵的秩;
因此该多项式有唯一解。
事实上这就是拉格朗日插值法
拉格朗日插值法可以找到唯一一个多项式,其恰好在各个观测点上取得观测值,这样的多项式称为拉格朗日插值多项式
插值法:可以将拉格朗日多项式在任意一点的值作为准确值的近似值
当多项式是点值表示的时候再来计算多项式乘法的话:
A
(
x
)
=
x
2
+
2
x
+
1
A(x) = x^2 + 2x + 1
A(x)=x2+2x+1可以表示为[(-2,1),(-1,0),(0,1),(1,4),(2,9)]
B
(
x
)
=
x
2
−
2
x
+
1
B(x) = x^2 - 2x + 1
B(x)=x2−2x+1可以表示为[(-2,9),(-1,4),(0,1),(1,0),(2,1)]
计算
P
(
x
)
P(x)
P(x)的点值表示的话只需要计算对应点上y值的乘积就好了,可以得到
P
(
x
)
P(x)
P(x)的点值表示为[(-2,9),(-1,0),(0,1),(1,0),(2,9)]
不难看出,想要计算
P
(
x
)
P(x)
P(x)的点值表示只需要用到一个循环,也就是说时间复杂度只有
O
(
n
)
O(n)
O(n)
我们可以想到,如果先将计算机存储的系数表示的多项式转换成点值表示,再计算多项式乘法的结果,最后将结果的点值表示转换回系数表示,这样是否可以降低时间复杂度?
如果能够以较低的时间复杂度实现将系数表示的多项式转换成点值表示,就可以极大的降低多项式乘法的时间复杂度,而这个过程就是快速傅里叶变换,这个过程的逆过程就是反傅里叶变换(IFFT)。
求值(Evaluate)
上面我们已经明确了快速傅里叶变换要做的事就是将系数表示的多项式转换成点值表示,这一过程(Coeff -> Value)被称为求值(Evaluate)。
想要用点值表示就需要在多项式函数的图像上找到n+1个不同的点(对于n阶多项式来说):
- 随便选取n个点并计算多项式的值的时间复杂度依旧是 O ( n 2 ) O(n^2) O(n2)
- 利用多项式的奇偶性,选取互为相反数的点可以简化计算
举个例子来表明一下这一过程的效果:
将多项式
P
(
x
)
=
3
x
5
+
2
x
4
+
x
3
+
7
x
2
+
5
x
+
1
P(x) = 3x^5 + 2x^4 + x^3 + 7x^2 + 5x + 1
P(x)=3x5+2x4+x3+7x2+5x+1按照次数分解成奇偶项
P
(
x
)
=
(
2
x
4
+
7
x
2
+
1
)
+
x
(
3
x
4
+
x
2
+
5
)
P(x) = (2x^4 + 7x^2 + 1) + x(3x^4 + x^2 + 5)
P(x)=(2x4+7x2+1)+x(3x4+x2+5)
P
(
x
)
=
P
e
(
x
2
)
+
x
P
o
(
x
2
)
P(x) = P_e(x^2) + xP_o(x^2)
P(x)=Pe(x2)+xPo(x2)由
P
(
x
)
P(x)
P(x)到
P
e
(
x
2
)
,
P
o
(
x
2
)
P_e(x^2),P_o(x^2)
Pe(x2),Po(x2)多项式从5阶降维到2阶
{
P
e
(
x
2
)
=
2
x
4
+
7
x
2
+
1
P
o
(
x
2
)
=
3
x
4
+
x
2
+
5
\left\{ \begin{aligned} P_e(x^2) &= 2x^4 + 7x^2 + 1 \\ P_o(x^2) &= 3x^4 + x^2 + 5 \end{aligned} \right.
{Pe(x2)Po(x2)=2x4+7x2+1=3x4+x2+5用同样的方法递归求解
P
e
(
x
2
)
,
P
o
(
x
2
)
P_e(x^2),P_o(x^2)
Pe(x2),Po(x2)即可,回溯时
{
P
(
x
i
)
=
P
e
(
x
i
2
)
+
x
i
P
o
(
x
i
2
)
P
(
−
x
i
)
=
P
e
(
x
i
2
)
−
x
i
P
o
(
x
i
2
)
\left\{ \begin{aligned} P(x_i) = P_e(x_i^2) + x_iP_o(x_i^2) \\ P(-x_i) = P_e(x_i^2) - x_iP_o(x_i^2) \end{aligned} \right.
{P(xi)=Pe(xi2)+xiPo(xi2)P(−xi)=Pe(xi2)−xiPo(xi2)即可求解多项式的值
总结一下:
- 将多项式按照奇偶项分解成两个低阶多项式
- 选取一组点,计算多项式在该点和对称点上的值
- 迭代
- 回溯
这样做的时间复杂度可以达到 O ( n l g n ) O(nlg n) O(nlgn)比原来的 O ( n 2 ) O(n^2) O(n2)好得多
为了使递归成立,还要解决一个问题就是 x 2 x^2 x2不会为负,因此引入复数
引入复数使递归成立
为了使递归成立,我们需要:
- 使任何一个选取的点可以表示成互为相反数的两个数的平方
- 上述互为相反数的两个点也可以分别表示成两对相反数的平方
为什么说这样就可以实现递归,需要在复平面上解释一下:
1的n次方根可以表示成复平面单位圆上n等分点
通过将原多项式降阶,使我们求解多项式的值得时候更快;将上面的解集平方得到的就是降阶后的多项式的解集,而对应在复平面上
就可以很清楚的看懂递归的过程
将1的n次方根一一带入多项式求值,这一过程就是离散傅里叶变换(DFT)
小结
所以到现在为止,我们就实现了最开始的目标,将一个系数表示的多项式转换成点值表示的,最后FFT的输出就是多项式在n个n次方根的值
整个快速傅里叶变换的伪码和执行流程
参考:
https://www.bilibili.com/video/BV1za411F76U
https://zhuanlan.zhihu.com/p/31584464
未完