快速傅里叶变换(FFT)和逆快速傅里叶变换(IFFT)

多项式表示法与卷积

多项式有两种表示方法

  • 系数表示法
  • 点值表示法

系数表示法
就是最普通的表示方法,如
f(x)=a0x0+a1x1+a2x2+......+an1xn1 f(x) = a_0x^0 + a_1x^1 + a_2x^2 + ...... + a_{n-1}x^{n-1} 则表示为 f(x)={a0,a1,a2,......,an1}f(x) = \{a_0, a_1, a_2, ......, a_{n-1}\}


点值表示法
平面坐标系上的来描述多项式的一种表示方法
对于一个多项式来说有 n 项,则可以通过 n 对 {x,y}\{x, y\} 点对来进行系数求解,即对于上述式子来说还有
f(x)={(x0,f(x0)),(x1,f(x1)),(x2,f(x2)),......,(xn1,f(xn1))} f(x) = \{(x_0, f(x_0)), (x_1, f(x_1)), (x_2, f(x_2)), ......, (x_{n-1}, f(x_{n-1}))\} 这种表达方式


卷积
就是多项式相乘,对于2种不同的多项式表示法来说,做卷积的时间复杂度会不同

  • 系数表示法,由于每2个系数之间都需要做一次处理,所以时间复杂度需要 O(n2)
  • 点值表示法,只要对应的两个点的 f(xi)f(x_i) 相乘即可,所以时间复杂度只有 O(n)

但是将系数表示法转换成点值表示法,如果用朴素的转换方法,也需要 O(n2) 的时间复杂度,所以采取DFT通过分治法来做

 

离散傅里叶变换(DFT)

傅里叶变换中所有的 n 都为 2 的整数次幂
我们的目标是将系数表示法转换成点值表示法,如果随机选取 nn 个点,再计算出它对应的 f(x)f(x),时间复杂度为 O(n2),因为对每个点都需要求 nnxi,i[0,n1]x^i, i∈[0, n - 1]

傅里叶选取了一些点,这些点满足它们的若干次方 = 1,这样可以使得带入之后不需要做这么多运算(但是时间复杂度仍是O(n2))

这些点都在复数平面直角坐标系里
快速傅里叶变换(FFT)和逆快速傅里叶变换(IFFT)
圆上所有的点都满足傅里叶的要求
我们假设将其 n=8n = 8 等分,则会得到:
快速傅里叶变换(FFT)和逆快速傅里叶变换(IFFT)
(1,0)(1, 0) 按照逆时针进行编号,对于编号为 k 的点,记为 wnkw_n^k,称为 nn 次单位根,且
wnk=coskn2π+isinkn2π w_n^k = \cos{\frac{k}{n}2\pi} + i\sin{\frac{k}{n}2\pi}


单位根的性质

  • wnk=w2n2kw_n^k = w_{2n}^{2k},2可以替换为任意整数
  • wnk+n2w_n^{k+\frac{n}{2}} = wnk-w_n^k
  • wn0=wnnw_n^0 = w_n^n
  • 对任意 k[0,n]k∈[0, n],有 Σj=0n1(wnk)j=0\Sigma_{j=0}^{n-1}(w_n^k)^j = 0

        

快速傅里叶变换(FFT)


简介

虽然通过DFT选出了便于计算的点,但是时间复杂度仍为 O(n2),但是通过分析多项式可以得到用分治法处理这些点的方式。
假设有
A(x)=Σj=0n1aixi=a0+a1x1+a2x2+......+an1xn1 A(x) = \Sigma_{j=0}^{n-1}a_ix^i = a_0 + a_1x^1 + a_2x^2 + ...... + a_{n-1}x^{n-1} 现在将 A(x)A(x)xx 的下标奇偶性将 A(x)A(x) 分成两部分
A(x)=(a0+a2x2+a4x4+......+an2xn2)+x(a1+a3x2+......+an1xn2) A(x) = (a_0 + a_2x^2 + a_4x^4 + ...... + a_{n-2}x^{n-2}) + x(a_1 + a_3x^2 + ...... + a_{n-1}x^{n-2}) 所以有
A(x)=A1(x2)+xA2(x2) A(x) = A_1(x^2) + xA_2(x^2)
k<n2k < \frac{n}{2} 的时候,将wnkw_n^k 作为 xx 带入 A(x)A(x)
A(wnk)=A1((wnk)2)+xA2((wnk)2)=A1(wn2k)+xA2(wn2k)=A1(wn2k)+wnkA2(wn2k) \begin {aligned} A(w_n^k) &= A_1((w_n^k)^2) + xA_2((w_n^k)^2) \\ &= A_1(w_n^{2k}) + xA_2(w_n^{2k}) \\ &=A_1(w_{\frac{n}{2}}^k) + w_n^kA_2(w_{\frac{n}{2}}^k) \end {aligned}
所以当取后半部分的时候,有x=wnk+n2x = w_n^{k+\frac{n}{2}}
A(wnk+n2)=A1((wnk+n2)2)+xA2((wnk+n2)2)=A1(wn2kwnn)+wnk+n2A2(wn2kwnn)=A1(wn2k)wnkA2(wn2k)=A1(wn2k)wnkA2(wn2k) \begin {aligned} A(w_n^{k+\frac{n}{2}}) &= A_1((w_n^{k+\frac{n}{2}})^2) + xA_2((w_n^{k+\frac{n}{2}})^2) \\ &= A_1(w_n^{2k}w_n^n) + w_n^{k+\frac{n}{2}}A_2(w_n^{2k}w_n^n) \\ &= A_1(w_n^{2k}) - w_n^kA_2(w_n^{2k}) \\ &=A_1(w_{\frac{n}{2}}^k) - w_n^kA_2(w_{\frac{n}{2}}^k) \end {aligned}
可以看到对于 k=n2k = \frac{n}{2} 的中介线来说,A(wnk)A(w_n^k)A(wnk+n2)A(w_n^{k+\frac{n}{2}}) 都可以通过 wn2kw_{\frac{n}{2}}^k 进行计算

这样时间复杂度就降为了 O(nlog2n)O(nlog_2n)
        


蝴蝶变换 Cooley-Turkey

刚提到对于当前层确定的位置 ii,可以通过下一层的两个值对当前值进行更新

开始肯定想着用递归的方式进行操作,但是有更为便捷的二进制反转方法

  • 以下 kk 均为 aka_k 中的 kk
kk 0 1 2 3 4 5 6 7
二进制 000 001 010 011 100 101 110 111

一次变换之后,将偶数下标放在一起、奇数下标放在一起,重新变化成以下数列(前4个为一组,后4个为一组)

kk 0 2 4 6 1 3 5 7

二次变换之后,将两组的奇次项、偶次项再分开,变成以下数列(2、2、2、2分组)

kk 0 4 2 6 1 5 3 7

三次变换之后,已经变成单独的一个一个的了,再观察他们的二进制,和原始序列k作比较

原始 kk 0 1 2 3 4 5 6 7
二进制 000 001 010 011 100 101 110 111
kk 0 4 2 6 1 5 3 7
二进制 000 100 010 110 001 101 011 111

这样的对比可以很容易看出,对于原始序列和变换之后的序列来说,他们就是对二进制做了一个反转操作,即 1 (001) 变成了 4 (100)

那如果我们在最开始的时候,就将序列做了二进制反转操作,就可以从前往后依次顺序运算,而不需要使用递归的方式

以刚刚的序列为例子

反转后的 kk 0 4 2 6 1 5 3 7
计算内容 A(w10)=a0w10A(w_1^0) = a_0w_1^0 A(w14)A(w_1^4) A(w12)A(w_1^2) A(w16)A(w_1^6) A(w11)A(w_1^1) A(w15)A(w_1^5) A(w13)A(w_1^3) A(w17)=a7w17A(w_1^7) = a_7w_1^7

第一次合并

kk 0 4 2 6 1 5 3 7
计算内容 A(w20)=A1(w10)+w20A2(w14)A(w_2^0) = A_1(w_1^0) + w_2^0A_2(w_1^4) A(w24)=A1(w10)w20A2(w14)A(w_2^4) = A_1(w_1^0) - w_2^0A_2(w_1^4) A(w22)A(w_2^2) A(w26)A(w_2^6) A(w21)A(w_2^1) A(w25)A(w_2^5) A(w23)A(w_2^3) A(w27)A(w_2^7)

这里的 A1(w10)A_1(w_1^0) 就对应了第一个表中的 A(w10)A(w_1^0)
k=4k = 4 的时候取值为 w20w_2^0 是因为在 k>n2k > \frac{n}{2} 的时候,xx 取的是 wnk+n2w_n^{k + \frac{n}{2}},所以 kk 也要相应的减去 n2\frac{n}{2}

第二次合并

kk 0 2 4 6 1 3 5 7
计算内容 A(w40)=A1(w20)+w40A2(w24)A(w_4^0) = A_1(w_2^0) + w_4^0A_2(w_2^4) A(w42)=A1(w20)w40A2(w24)A(w_4^2) = A_1(w_2^0) - w_4^0A_2(w_2^4) A(w44)A(w_4^4) A(w46)A(w_4^6) A(w41)A(w_4^1) A(w43)A(w_4^3) A(w45)A(w_4^5) A(w47)A(w_4^7)

这里的 A1(w20)A_1(w_2^0) 就对应了第二个表中的 A(w20)A(w_2^0)

第三次合并

kk 0 1 2 3 4 5 6 7
计算内容 A(w80)=A1(w40)+w80A2(w42)A(w_8^0) = A_1(w_4^0) + w_8^0A_2(w_4^2) A(w81)=A1(w40)w80A2(w42)A(w_8^1) = A_1(w_4^0) - w_8^0A_2(w_4^2) A(w82)A(w_8^2) A(w83)A(w_8^3) A(w84)A(w_8^4) A(w85)A(w_8^5) A(w86)A(w_8^6) A(w87)A(w_8^7)

所以在最后一次合并之后就得到了所有的 A(x)A(x)
成功的从系数表示法转换成点值表示法!
        

逆快速傅里叶变换(IFFT)

当然我们从系数表示法转换成点值表示法只是为了计算卷积的时候减小时间复杂度,但最后对我们有帮助、便于分析的仍然是系数表示法

所以在对点值表示法的多项式进行卷积之后,仍需要将其再次转换回系数表示法

这种转换方式称为逆快速傅里叶变换

那等于说我们给出了 nn 个线性方程组,然后需要对其进行求解
a0(wn0)0+a1(wn0)1+......+an1(wn0)n1=A(wn0)a0(wn1)0+a1(wn1)1+......+an1(wn1)n1=A(wn1)......a0(wnn1)0+a1(wnn1)1+......+an1(wnn1)n1=A(wnn1) \begin{aligned} a_0(w_n^0)^0 + a_1(w_n^0)^1 + ...... + a_{n-1}(w_n^0)^{n-1} &= A(w_n^0) \\ a_0(w_n^1)^0 + a_1(w_n^1)^1 + ...... + a_{n-1}(w_n^1)^{n-1} &= A(w_n^1) \\ ......\\ a_0(w_n^{n-1})^0 + a_1(w_n^{n-1})^1 + ...... + a_{n-1}(w_n^{n-1})^{n-1} &= A(w_n^{n-1}) \\ \end{aligned}
将其写成矩阵的形式有
[(wn0)0(wn0)1...(wn0)n1(wn1)0(wn1)1...(wn0)n1......(wnn1)0(wnn1)1...(wnn1)n1][a0a1...an1]=[A(wn0)A(wn1)...A(wnn1)] \left[ \begin{matrix} (w_n^0)^0 & (w_n^0)^1 & ... & (w_n^0)^{n-1} \\ (w_n^1)^0 & (w_n^1)^1 & ... & (w_n^0)^{n-1} \\ ...... \\ (w_n^{n-1})^0 & (w_n^{n-1})^1 & ... & (w_n^{n-1})^{n-1} \end{matrix} \right] \left[ \begin{matrix} a_0 & \\ a_1 & \\ ...\\ a_{n-1} & \end{matrix} \right] = \left[ \begin{matrix} A(w_n^0) \\ A(w_n^1) \\ ...\\ A(w_n^{n-1}) \end{matrix} \right]
定义第一个矩阵为D、第二个矩阵为V、第三个矩阵为E
按照矩阵乘法,有
eij=Σk=0n1dikvkj=Σk=0n1wnikwnkj=Σk=0n1wnk(ji) \begin{aligned} e_{ij} & = \Sigma_{k=0}^{n-1}d_{ik}v_{kj} \\ &= \Sigma_{k=0}^{n-1}w_n^{-ik}w_n^{kj} \\ &=\Sigma_{k=0}^{n-1}w_n^{k(j-i)} \end{aligned}
所以有
eij={ni=j0ij e_{ij} = \begin{cases} & n & & i = j \\ & 0 & & i ≠ j \\ \end{cases}
iji≠j 的时候可以根据等比序列求和公式得到和为0

由此可知,In=1nEI_n = \frac{1}{n}EInI_n是一个 n×n 的单位矩阵

所以有 1nD=V1\frac{1}{n}D = V^{-1},∵ EE 和单位矩阵扯上联系了嘛

那么其实IFFT就是一个变换了一点点的FFT
如果说FFT用公式来表示是
X(k)=Σn=0N1x(n)wNkn X(k) = \Sigma_{n=0}^{N-1}x(n)w_N^{kn} 则IFFT用公式来表示就是
x(n)=1NΣn=0N1X(k)wnkn x(n) = \frac{1}{N}\Sigma_{n=0}^{N-1}X(k)w_n^{-kn}
单位根取个负然后乘个 1N\frac{1}{N}

那总结一下FFT和IFFT的作用就是减少卷积运算的时间复杂度www
        

其他

  • 其实是为了学习NTT
  • 然而NTT还没看懂
  • 先总结出一份FFT和IFFT的来
  • NTT的尽快出吧QuQ