FFT

FFT 流程

  • P1919 【模板】A*B Problem 升级版(FFT 快速傅里叶变换) 为例

  • 一个数字可以看成 ai×10i ,那么把两个数字转成多项式的形式,求最后每一项的系数

  • 我们知道一个多项式可以通过 n 个系数,或者 n+1 个点值来确定,那么我们考虑快速的让两个多项式转换成点值表示,然后点值的值是可以直接乘起来的,然后再通过得到的 (fg)n+1 个点值转回系数

  • FFT 要求长度为 2n ,所以不够的补 0

系数转点值

  • 对于函数 f(x)=a0+a1x+a2x2+...

  • f(x)=(a0+a2x2+a4x4+...)+x(a1+a3x2+a5x4+...)

  • G(x)=a0+a2x+a4x2...,H(x)=a1+a3x+a5x2...

  • 那么 f(x)=G(x2)+xH(x2)

  • 注意到这个 x 我们取单位根 wnk,0kn ,之所以这个取是因为单位根是 xn=1 在复数域下的解,幂次最后得出是 1 会方便我们计算

  • 单位根有一些性质:

    • wnn=1
    • w2n2k=wnk
    • w2nn+k=w2nk
  • 那么最后就会得到

    • 0<x<n2,f(wnx)=G(wn/2k)+wnkH(wn/2k)
    • n2x<n,f(wnx)=G(wn/2k)wnkH(wn/2k)
  • 可以发现中间的符号不同,其他都是一样的,那么我们只要递归求 G(x)H(x) 就可以了

  • 最后得到的函数的每一位是相应的点值

  • 复杂度为 O(nlogn) ,递归写法

蝴蝶变换

  • 观察发现编号为 x(2) 的下标,最后会变到 rev(x(2)) 的下标

  • 这个可以预处理出来,将每个数变换到对应的地方,那么就可以直接递推处理

  • 具体的变换可以参照 rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));

点值转系数

  • 实际上这个时候一般的高斯消元可以列出一个矩阵,具体可见 OI-Wiki

  • 我们惊喜的发现这个系数矩阵是可逆的,并且逆矩阵的每个位置就是原来的数倒一下再除以 n

  • 这个除以 n 可以放在最后再一起除

  • 然后此时已经求出的点值乘上逆矩阵就等于原系数

  • 我们将点值看成系数,系数看成新的点值,那么就转换成了之前的 快速系数转点值

  • 因为只是倒一下,所以符号上有一点不同,其他的都是一样的

参考代码

8 次MTT

  • FFT 的精度损失是一方面,另一方面无法像 NTT 那样做大值域,因为没办法取模

  • 于是就有了拆系数 FFT ,也叫 MTT

  • P4245 【模板】任意模数多项式乘法 为例

  • 一种方法是直接将一个函数 f(x) 拆成 f(x)/Mf(x)%M

  • f(x)=M×(f(x)/M)+(f(x)%M)=Mf1(x)+f2(x)

  • 那么 f(x)g(x)=M2f1g1+f2g2+Mf1g2+Mf2g1 ,那么就要做 8 次 FFT

  • M215 最好,开 long double 信仰的跑,值域是解决了,但是常数巨大

4 次 MTT

  • 首先还是像上面一样拆式子,那么现在有 A0,A1,B0,B1 四个系数式,我们希望通过 2 次 DFT 得到它们各自的点值

  • 构造系数式 P=A0+iB0,Q=A0iB0

  • 注意到 P,Q 的每一项都共轭,那么它们的点值也共轭,那么我们求出 P 的点值,然后推出 Q 的点值,然后类似于解一元二次方程来解出 A0,B0

  • 那么分成两组,一共就是 2 次 DFT

  • 现在得到了 A0,A1,B1,B0 的点值

  • 更抽象的,现在有 4 个点值式 A,B,C,D ,我们希望通过 2 次 IDFT 得到它们两两乘积的系数式

  • 构造 P=A+iB,Q=C+iD,Q=CiD

  • 那么 PQ=AC+iBC+iDABD,PQ=AC+iBCiDA+BD

  • 通过 2 次 IDFT 可以得到上述式子

  • 因为最终的答案不存在虚部,那么就可以通过虚部和实部分别解出 AC,BC,BD,DA ,正好符合我们的要求

  • 那么总的就是要进行 4 次 FFT

posted @   Kzos_017  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示