深入理解 FFT

理论前置

知道啥是多项式(即 f(x)=i=0n1fixi 这一类东西)。

知道啥是多项式的卷积(即 (f×g)(x)=h(x),其中 hi=k=0ifkgik)。

知道啥是复数(即 a+bi 这种,其中 i2=1)。

知道啥是单位根(即 xn=1 的所有复数解,记作 ωn)。

引入

随着 OI 中毒瘤出题人和毒瘤数数题的变多,我们有时候会需要求两个多项式的卷积,根据定义暴力计算,我们有一个 O(n2)优秀做法。

显然毒瘤出题人不会止步于此,于是我们需要更快的算法。

这时就需要引入多项式的另外一种表示方法了。

点值表示法

对于 f(x),我们不仅可以通过系数表示法(f(x)=i=0n1fixi)来表示它,还可以通过点值表示法来表示它。

具体的,对于一个 n1 次的多项式 f(x),我们可以通过 n 个互不相同的点唯一确定这个多项式,即 f(x)=[(x0,y0),(x1,y1),,(xn1,yn1)],其中 yi=f(xi)

这种表示法对我们来说是很好的,因为对于两个多项式 f(x),g(x),只要把点值表示中 yi 两两相乘即可求出 h(x) 的点值表达(注意:由于 h(x)n+m2 次多项式,所以需要 n+m1 个点才能唯一确定 h(x))。这样,我们只需要 O(n) 的时间复杂度即可求出两个多项式的卷积。

现在让我们整理一下算法流程:

  1. 输入两个多项式 f(x),g(x)
  2. f(x),g(x) 各转成点值表达法(需要 n+m1 个点)
  3. 把点值表达法的 yi 两两相乘
  4. 把点值表达法转成 h(x) 的系数表达法
  5. 输出多项式 h(x)

现在为止一切都很好,但是我们会发现还存在一个问题:系数表达法和点值表达法的转换依旧是 O(n2) 的,所以这个算法除了增大常数之外没有任何好处。

你先别急,先看完下面再说

系数表达法 -> 点值表达法

让我们先从简单的多项式入手,比如 f(x)=x2,我们如何在较快的时间内求出它的点值表达呢?

很简单,我们可以代入一系列的正负值 {±x0,±x1,,±xn/21},因为我们有 f(x)=f(x),所以我们只需要求出 n2 个点的点值表达了。

稍难一点,设 f(x)=x3,由于 f(x)=f(x),所以我们依旧可以参考上面的流程。

一般的,对于多项式 f(x)=i=0n1fixi,我们可以把它分为两半,即 f0(x)=i=0n/21f2ixif1(x)=i=0n/21f2i+1xi,那么我们有

f(xi)=f0(xi2)+xif1(xi2)f(xi)=f0(xi2)xif1(xi2)

可以发现对于 f0f1,这等价于原问题:多点求值,因此我们可以分治下去求解。

看上去非常不错,但事实上这种做法暗藏着一个陷阱:经过一轮迭代后 xi2 均为正值,因此不能再套用这种做法。

怎么解决呢?

可以发现我们想要的是一系列值 x0,x1,,xn1,使得 x2i=x2i+1x4i2=x4i+22x8i4=x8i+44

这时就需要引入复数了。

不妨设 x0=1,则显然 x1=1,那么有 1=x22,由于 xi 互不相等,故 x2,x3=i,i,以此类推。我们会发现 xi 实际上就是 xn=1 的全部复数解,或者说 ωn

怎么计算 ωn 呢?可以发现单位根其实就是把单位圆进行了 n 等分,即 ωnk=ei(2kπ/n)=cos(2kπ/n)+isin(2kπ/n)

至此,我们就可以通过分治将此算法做到 O(nlogn) 了。

点值表达法 -> 系数表达法

考虑系数表示法变成点值表示法的另外一种方式:线性代数。

我们有

{f0+x0f1+x02f2++x0n1fn1=f(x0)f0+x1f1+x12f2++x1n1fn1=f(x1)f0+xn1f1+xn12f2++xn1n1fn1=f(xn1)

使用矩阵表示即为

[f(x0)f(x1)f(xn1)]=[1x0x02x03x0n11x1x12x13x1n11x2x22x23x2n11xn1xn12xn13xn1n1][f0f1fn1]

代入 xk=ωnk 可得

[f(x0)f(x1)f(xn1)]=[111111ωnωn2ωn3ωnn11ωn2ωn4ωn6ωn2(n1)1ωnn1ωn2(n1)ωn3(n1)ωn(n1)(n1)][f0f1fn1]

那么如果我们想要进行 IFFT,本质上就是对上面这个大矩阵求逆。

由于这是一个有一些很好的性质的矩阵,所以它的逆也非常的漂亮:

[111111ωnωn2ωn3ωnn11ωn2ωn4ωn6ωn2(n1)1ωnn1ωn2(n1)ωn3(n1)ωn(n1)(n1)]1=1n[111111ωn1ωn2ωn3ωn(n1)1ωn2ωn4ωn6ωn2(n1)1ωn(n1)ωn2(n1)ωn3(n1)ωn(n1)(n1)]

不难发现逆矩阵和原矩阵几乎一模一样,也可以使用 FFT 的方式计算。

至此,我们可以在 O(nlogn) 的时间内计算两个多项式的乘积。

参考视频

posted @   bykem  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示