第一章 FFT
0x00 记号说明
全部采用 F [ i ] F[i] F[i] 表示多项式第 i i i 次项系数。(数组可能是下标)
统一使用 ∗ * ∗ 作为卷积。
0x10 前置知识
来自初三的 SMT:本章节写于初一升初二暑假。部分内容垃圾就原谅我吧。
我觉得啊,初一下应该就要熟练任意模数 NTT ,不然怎么做初二初三的多项式(狗头)——STJqwq,初一
我 cnm 老子没说过这句话——SmallTualatin,初三
FFT 的思想是多项式世界大门的钥匙,有了它,毒瘤 新的世界将对你们敞开。
前置知识:
- 函数和多项式,初中的定义
- 一点点复数的知识,知道 i = − 1 i=\sqrt{-1} i=−1 这种东西 ,FFT要用
- 一些奇怪的同余知识(至少要会求逆元),NTT要用,任意模数NTT也要用
有手,有脑子
复数类的具体实现:
定义一个复数为 a + b i a+bi a+bi 。 i i i 为 − 1 \sqrt{-1} −1。
加法: a + b i + c + d i = ( a + c ) + ( b + d ) i a+bi+c+di=(a+c)+(b+d)i a+bi+c+di=(a+c)+(b+d)i
减法:同上,系数改成 − 1 -1 −1
乘法: ( a + b i ) ( c + d i ) = a c + a d i + b c i + b d i 2 = ( a c − b d ) + ( a d + b c ) i (a+bi)(c+di)=ac+adi+bci+bdi^2=(ac-bd)+(ad+bc)i (a+bi)(c+di)=ac+adi+bci+bdi2=(ac−bd)+(ad+bc)i
除法:其实是可以的,但是这里不需要。
下列实现了一个比较简洁的复数类。
struct cp{
double x,y;
friend cp operator +(cp a,cp b){
return {
a.x+b.x,a.y+b.y};
}friend cp operator -(cp a,cp b){
return {
a.x-b.x,a.y-b.y};
}friend cp operator *(cp a,cp b){
return {
a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}
};
0x11 算法流程
(1)两个系数序列点值化->(2)每个点值乘起来->(3)点值序列系数化
考虑如何 点值化 系数序列 和 如何 系数化 点值序列 。
给个样例手模一下,1 2
和 2 3 1
,
然后我们取 − 1 -1 −1 , 0 0 0 , 1 1 1 点上的答案:
f ( − 1 ) = − 1 , f ( 0 ) = 1 , f ( 1 ) = 3 f(-1)=-1,f(0)=1,f(1)=3 f(−1)=−1,f(0)=1,f(1)=3 而 g ( − 1 ) = 0 , g ( 0 ) = 2 , g ( 1 ) = 6 g(-1)=0,g(0)=2,g(1)=6 g(−1)=0,g(0)=2,g(1)=6
所以设 h ( x ) = f ( x ) × g ( x ) h(x)=f(x)\times g(x) h(x)=f(x)×g(x),那么 h ( − 1 ) = − 1 × 0 = 0 , h ( 0 ) = 1 × 2 = 2 , h ( 1 ) = 3 × 6 = 18 h(-1)=-1\times 0=0,h(0)=1\times 2=2,h(1)=3\times 6=18 h(−1)=−1×0=0,h(0)=1×2=2,h(1)=3×6=18
发现函数图像是对的!
手模样例,最后的结果为 2 7 7 2
。
证明:点值表示 f ( x ) = ∑ i = 0 n f [ i ] x i f(x)=\sum\limits_{i=0}^{n} f[i]x^i f(x)=i=0∑nf[i]xi , g ( x ) = ∑ i = 0 n g [ i ] x i g(x)=\sum\limits_{i=0}^{n} g[i]x^i g(x)=i=0∑ng[i]xi ,
h ( x ) h(x) h(x) 相当于每一项都枚举乘起来,那么 f ( x ) × g ( x ) = ∑ i = 0 n f [ i ] x i × ∑ j = 0 n g [ j ] x j f(x)\times g(x)=\sum\limits_{i=0}^{n} f[i]x^i \times \sum\limits_{j=0}^{n} g[j]x^j f(x)×g(x)=i=0∑nf[i]xi×j=0∑ng[j]xj 。
即 g ( x ) = ∑ i = 0 n f [ i ] ∑ j = 0 n g [ j ] x i + j g(x)=\sum\limits_{i=0}^{n} f[i]\sum\limits_{j=0}^{n} g[j]x^{i+j} g(x)=i=0∑nf[i]j=0∑ng[j]xi+j ,就是多项式乘法的定义。
DFT 就是求出这个点值转系数和系数转点值。然而暴力太慢,于是考虑从 O ( n 2 ) O(n^2) O(n2) 优化成 O ( n log n ) O(n\log n) O(nlogn)。
0x12 DFT的分治优化
FFT 的分治流程:先把这个序列通过补 0 0 0 扩充到 2 2 2 的整数幂次方。即 n 为 2 m − 1 2^m-1 2m−1 的形式,其中 m m m 为最小的 2 m > n 2^m>n 2m>n 的数。
然后
把序列按照奇偶分治。
f ( x ) = f [ 0 ] + f [ 1 ] x + f [ 2 ] x 2 + . . . + f [ n ] x n f(x)=f[0]+f[1]x+f[2]x^2+...+f[n]x^n f(x)=f[0]+f[1]x+f[2]x2+...+f[n]xn
那么拆成两半, f ( x ) = ( f [ 0 ] + f [ 2 ] x 2 + . . . ) + ( f [ 1 ] x + f [ 3 ] x 3 + . . . ) f(x)=(f[0]+f[2]x^2+...)+(f[1]x+f[3]x^3+...) f(x)=(f[0]+f[2]x2+...)+(f[1]x+f[3]x3+...)
于是右边柿子提出一个 x x x, f ( x ) = ( f [ 0 ] + f [ 2 ] x 2 + . . . ) + x ( f [ 1 ] + f [ 3 ] x 2 + . . . ) f(x)=(f[0]+f[2]x^2+...)+x(f[1]+f[3]x^2+...) f(x)=(f[0]+f[2]x2+...)+x(f[1]+f[3]x2+...)
然后发现两边的柿子中带入的数是 x 2 x^2 x2 ,于是继续递归下去
即 F ( x ) = F L ( x 2 ) + F R ( x 2 ) x F(x)=FL(x^2)+FR(x^2)x F(x)=FL(x2)+FR(x2)x 。
0x13 带入什么 x x x 合适
因为代入普通的 x x x 没有什么特殊性质,所以考虑所有 x n = 1 x^n=1 xn=1 的 x x x 。
大眼观察法,我们似乎只有 − 1 -1 −1 和 1 1 1 ,但是引入复数系之后,我们可以找到 n n n 个。
把复数域作为向量,搞到平面直角坐标系上。 ( x , y ) (x,y) (x,y) 点就代表 x + y i x+yi x+yi 这样一个复数。
如果对于 a 4 = 1 a^4=1 a4=1 的解 a a a ,那么就有 1 , − 1 , i , − i 1,-1,i,-i 1,−1,i,−i 都可以满足这个答案。
于是得出这么一个结论:单位圆上 n n n 等份,每个被切割的点即为 a n = 1 a^n=1 an=1 的一个解。这个 n n n 等份可以使用 三角函数 求出。
一号单位根的位置: ( c o s ( 2 π n ) , s i n ( 2 π n ) ) (cos(\dfrac{2\pi}{n}),sin(\dfrac{2\pi}{n})) (cos(n2π),sin(n2π)) 。它被定义成 ω n 1 \omega_{n}^{1} ωn1 。
然后有一个性质: ω n i = ( ω n 1 ) i \omega_{n}^i=(\omega_{n}^1)^i ωni=(ωn1)i 。这个显而易见,计算单位复根,弧度就是它的多少倍,所以是多少次方。
还有一个性质, ω 2 n 2 i = ω n i \omega_{2n}^{2i}=\omega_{n}^i ω2n2i=ωni 。这个也显然。
还有一个性质, ω n 0 = 1 , ω n i = ω n i % n \omega_{n}^0=1,\omega_{n}^{i}=\omega_{n}^{i\% n} ωn0=1,ωni=ωni%n 。因为一个圆上所有点的排列是循环的。
一个性质: ω 2 n k + n = − ω 2 n k \omega_{2n}^{k+n}=-\omega_{2n}^{k} ω2nk+n=−ω2nk ,这个按照图形推导即可,就是圆上与它相对的点嘛。
0x14 FFT 最后的证明
我们接着上面的证明:
F ( x ) = F L ( x 2 ) + F R ( x 2 ) x F(x)=FL(x^2)+FR(x^2)x F(x)=FL(x2)+FR(x2)x
考虑代入单位根,(每个 k ≤ n / 2 k\le n/2 k≤n/2)
D F T ( F ( ω n k ) ) = D F T ( F L ( ( ω n k ) 2 ) ) + ω n k D F T ( F R ( ( ω n k ) 2 ) ) D F T ( F ( ω n k ) ) = D F T ( F L ( ω n 2 k ) ) + ω n k D F T ( F R ( ω n 2 k ) ) D F T ( F ( ω n k ) ) = D F T ( F L ( ω n / 2 k ) ) + ω n k D F T ( F R ( ω n / 2 k ) ) DFT(F(\omega_{n}^{k}))=DFT(FL((\omega_{n}^{k})^2))+\omega_{n}^{k}DFT(FR((\omega_{n}^{k})^2))\\ DFT(F(\omega_{n}^{k}))=DFT(FL(\omega_{n}^{2k}))+\omega_{n}^{k}DFT(FR(\omega_{n}^{2k}))\\ DFT(F(\omega_{n}^{k}))=DFT(FL(\omega_{n/2}^{k}))+\omega_{n}^{k}DFT(FR(\omega_{n/2}^{k})) DFT(F(ωnk))=DFT(FL((ωnk)2))+ωnkDFT(FR((ωnk)2))DFT(F(ωnk))=DFT(FL(ωn2k))+ωnkDFT(FR(ωn2k))DFT(F(ωnk))=DFT(FL(ωn/2k))+ωnkDFT(FR(ωn/2