定義
FWT是一種快速完成集合卷積運算的算法。
它可以用於求解類似 $C[i]=\sum\limits_{j⊗k=i}A[j]*B[k]$ 的問題。
其中⊗代表位運算中的|,&,^的其中一種。
求解(正變換)
設F(A)是對於A的一種變換。
並且F(A)要求滿足:
$F(A)*F(B)=F(A⊗B)$ ①
$k*F(A)=F(k*A)$ ②
$F(A+B)=F(A)+F(B)$ ③ (A,B長度相同)
鑒於FWT和FFT長得特別像(而且求解的問題也比較類似),我們可以借鑒一下FFT的思路,采用分治的想法。
首先先把多項式的長度用0補到2n,即多項式A為a0+a1x+a2x2+.....+a2n-1x2^n-1。
我們可以將多項式A拆成A0和A1。A0為多項式下標二進制最高位為0的部分,A1即為多項式下標二進制最高位為1的部分。
則A=(A0,A1)。 (ps:此處的括號意為將A0,A1拼起來。)
我們猜測F(A)=(k1*F(A0)+k2*F(A1),k3*F(A0)+k4*F(A1)),其中當A的長度為1時,F(A)=A
對於②式證明如下:
假設A的長度為2n,
由原式得:(k1*F(A0)+k2*F(A1),k3*F(A0)+k4*F(A1))*k=
(k1*F(k*A0)+k2*F(k*A1),k3*F(k*A0)+k4*F(k*A1))
則若要證明k*F(A)=F(k*A),我們需要證明的是F(k*A')=k*F(A'),其中A'的長度為2n-1。按照此方法遞歸直到A的長度為1,因為k*A=k*A,所以k*F(A)=F(k*A)。證畢。
對於③式證明如下:(其實和②式的證明一樣的)
假設A,B的長度為2n。
由原式得:(k1*F(A0+B0)+k2*F(A1+B1),k3*F(A0+B0)+k4*F(A1+B1))=
(k1*F(A0)+k2*F(A1),k3*F(A0)+k4*F(A1))+(k1*F(B0)+k2*F(B1),k3*F(B0)+k4*F(B1))
則若要證明F(A+B)=F(A)+F(B),需要條件F(A'+B')=F(A')+F(B'),其中A'的長度為2n-1。照此方法遞歸,同理可證明。
如今我們證明了②③的正確性,以下計算是為了確保①正確。
(以下計算以異或為例)
由 F(C)=F(A)*F(B)
C=A⊗B→ (A0,A1)⊗(B0,B1)=(A0⊗B0+A1⊗B1,A1⊗B0+A0⊗B1)
可以得出 (以下我們以k1,k2為例)
F(A)的前半部分 F(B)的前半部分 F(C)的前半部分
↓ ↓ ↓
(k1*F(A0)+k2*F(A1))*(k1*F(B0)+k2*F(B1))
=k1*F(A0⊗B0+A1⊗B1)+k2*F(A1⊗B0+A0⊗B1)
所以 k12F(A0⊗B0)+k1k2*F(A0⊗B1)+k1k2*F(A1⊗B0)+k22F(A1⊗B1)
=k1*F(A0⊗B0)+k2*F(A0⊗B1)+k2*F(A1⊗B0)+k1*F(A1⊗B1)
可得k12=k1,k1k2=k2,k22=k1。
解得k1,k2為(0,0)或(1,1)或(1,-1)
由於我們的操作必須可逆,所以排除掉(0,0),並且(k1,k2)(k3,k4)不相等。所以我們可以令k1=k2=k3=1,k4=-1。
則逆變換的時候,k1=k2=k3=1/2,k4=-1/2(這個解一下方程就可以算出來了)。
如果是|或者&運算,將紅色部分修改為:
| :(A0,A1)⊗(B0,B1)=(A0⊗B0,A1⊗B0+A0⊗B1+A1⊗B1)
& : (A0,A1)⊗(B0,B1)=(A0⊗B0+A1⊗B0+A0⊗B1,A1⊗B1)
以下代碼都以異或為例
void fwt(int *a,int len)//xor { for (int i=1;i<len;i<<=1) for (int j=0;j<len;j+=i*2) for (int k=0;k<i;k++) { int u=a[j+k],v=a[j+k+i]; a[j+k]=u+v;a[j+k+i]=u-v+mod; if (a[j+k]>mod) a[j+k]-=mod; if (a[j+k+i]>mod) a[j+k+i]-=mod; // or:a[j+k+i]=u+v; // and:a[j+k]=u+v; } }
FWT逆變換代碼(以異或為例)
void ufwt(int *a,int len) { for (int i=1;i<len;i<<=1) for (int j=0;j<len;j+=i*2) for (int k=0;k<i;k++) { int x=a[j+k],y=a[j+k+i]; a[k+j]=(x+y)*inv2%mod; a[j+k+i]=(x-y+mod)*inv2%mod; } }
其中的inv2為2的逆元。如果題目沒有要求將答案除以某數,也可以寫作:a[k+j]=(x+y)/2,a[k+j+i]=(x-y)/2
一個神神秘秘的問題
學習FWT的時候我比較好奇一個問題。在正變換的時候我們先處理F(A0),F(A1)后處理F(A),那為什么我們在求逆變換的時候不需要先求F(A)的逆變換再處理F(A0),F(A1)的。。。
請大佬不吝賜教。
本篇博客參考hy大佬的博客http://www.cnblogs.com/yoyoball/p/9260176.html。對於其一些我不太理解的地方加了證明和改動。如果有錯誤之處還請多多包涵。