【learning】快速沃爾什變換FWT


## 問題描述

  已知\(A(x)\)\(B(x)\)\(C[i]=\sum\limits_{j\otimes k=i}A[j]*B[k]\),求\(C\)

  其中\(\otimes\)是三種位運算的其中一種

具體求解

  說在前面:接下來的一些符號的話我們統一用\(\otimes\)代表某種位運算(選定的),如果這個符號出現在兩個多項式之間(如:\(A\otimes B\)),那么是表示按照最上面那條式子卷積,如果出現在兩個數之間(如\(j\otimes k\)),那么是表示這兩個數進行這種位運算;然后我們用$\ \cdot$這個符號表示點積(也就是對應數位相乘)

​  這里我們用與運算(\(\otimes=\&\))舉例說明好了,另外兩個差不多的

  首先來個不太靠譜的聯想,這個東西的名字長得跟FFT那么像那是不是可以借用FFT的形式啊(Portal -->FFT)

  然后我們考慮構造一種變換\(F(A)\)(額其實就是FWT),其中\(A\)是一個多項式,這個變換滿足:

\[\begin{aligned} &F(A)\cdot F(B)=F(A\otimes B)\\ &F(k* A)=k*F(A)\\ &F(A+B)=F(A)+F(B)\\ \end{aligned} \]

  這樣我們就可以像FFT一樣,先用某種方法求出\(F(A)\)\(F(B)\)然后對應位相乘,再把得到的結果逆變換一下就可以得到\(C(x)\)

​  

  那么現在的問題是我們怎么構造\(F\)

  參考FFT的求解我們還是嘗試用分治的思想來解決這個問題

​  與FFT按照下標的奇偶性分類不同,因為位運算是跟二進制有關的,所以我們可以考慮按照下標的二進制最高位為\(0\)還是為\(1\)分成兩類(因為算的時候長度是補成\(2^k\)這樣的,所以其實也就是嚴格的前半部分和后半部分)

​  對於一個多項式\(A\),下標二進制最高位為\(0\)的那部分記為\(A_0\),為\(1\)的那部分記為\(A_1\)\(A=(A_0,A_1)\)

​  

​  我們可以猜測一下這個\(F(A)=(k_1A_0+k_2A_1,k_3A_0+k_4A_1)\)

  其中\(k_1,k_2,k_3,k_4\)都是常數

  然后現在我們要做的就是求出這四個常數

  

​  我們將\(A\otimes B\)寫成\((A_0,A_1)\otimes (B_0,B_1)\),然后看一下這個式子等於什么:

​  同樣的我們還是將結果寫成\((C_0,C_1)\)的形式,那么根據與運算的特點我們可以的到:

\[\begin{aligned} (A_0,A_1)\otimes (B_0,B_1)=(A_0\otimes B_0+A_0\otimes B_1+A_1\otimes B_0,A_1\otimes B_1)\\ \end{aligned} \]

​  (具體一點的話就是\(0\&0=0,0\&1=0,1\&0=0\)所以都是\(C_0\)的組成部分,只有\(1\&1=1\)\(C_1\)的組成部分)

  

  然后我們根據最上面的\(F\)這個變換滿足的性質和我們猜測的\(F(A)=(k_1A_0+k_2A_1,k_3A_0+k_4A_1)\)來處理一下這個式子

​  (因為\(k_1,k_2\)\(k_3,k_4\)滿足的條件其實是一樣的所以在下面化的時候我們可以只考慮前半部分的求解(也就是就解\(k_1,k_2\),所有的變換什么的只寫前半部分),然后求得多組解最后取其中兩個可行解分別作為\(k_1,k_2,k_3,k_4\)即可)

​  (因為太長了。。所以一個等式我就換行來寫了qwq)

\[\begin{aligned} &(k_1A_0+k_2A_1)\cdot (k_1B_0+k_2B_1)\\ =&k_1(A_0\otimes B_0+A_0\otimes B_1+A_1\otimes B_0)+ k_2(A_1\otimes B_1)\\ \\ &\Updownarrow\\ \\ &k_1^2(A_0\otimes B_0)+k_1k_2(A_0\otimes B_1)+k_1k_2(A_1\otimes B_0)+k_2^2(A_1\otimes B_1)\\ =&k_1(A_0\otimes B_0)+k_1(A_0\otimes B_1)+k_1(A_1\otimes B_0)+k_2(A_1\otimes B_1) \end{aligned} \]

  然后我們就可以直接得到一個比較好看的方程組了:

\[\begin{cases} k_1^2=k_1\\ k_1k_2=k_1\\ k_2^2=k_2\\ \end{cases} \]

  然后我們解出來有三組解:\((0,0),(1,1),(0,1)\)

  接下來要做的就是從這三組解里面挑兩組出來作為\((k_1,k_2)\)\((k_3,k_4)\)

  但這個並不是隨便挑的,考慮到我們的這個變換必須要有一個逆變換,所以我們挑出來的兩組不能是有一組是\((0,0)\)或者有兩組相等(不然就不可逆了嘛),實際上,我們可以將這四個系數寫成一個矩陣,然后逆變換的系數的話就是對這個矩陣求個逆就好了

  所以,這里我們只能選\((1,1)\)\((0,1)\),我們令\((k_1,k_2)=(1,1)\)\((k_3,k_4)=(0,1)\)

\[\begin{bmatrix} k_1&k_2\\ k_3&k_4\\ \end{bmatrix}= \begin{bmatrix} 1&1\\ 0&1\\ \end{bmatrix} \]

​  然后我們把這個東西求一下逆,得到逆矩陣:

\[\begin{bmatrix} 1&-1\\ 0&1\\ \end{bmatrix} \]

​  (具體求解的話。。當然可以直接用線代的那套但是qwq這里那么小那直接設數代一下就好了,設四個數然后矩乘一下等於單位矩陣)

​  然后我們按照這個系數去算FWT(也就是變換\(F\))和逆FWT就好啦

  然后如果是求異或或者是或運算的話,那直接改一下

\[(A_0,A_1)\otimes (B_0,B_1)=(A_0\otimes B_0+A_0\otimes B_1+A_1\otimes B_0,A_1\otimes B_1) \]

​  這個式子的左邊然后再用同樣的方法算一下就好了

  xor的話就是:\((A_0\otimes B_0+A_1\otimes B_1,A_0\otimes B_1+A_1\otimes B_0)\)

  or的話就是:\((A_0\otimes B_0,A_0\otimes B_1+A_1\otimes B_0+A_1\otimes B_1)\)

​  

  代碼什么的長得跟FFT其實差不多。。。哦不對。。會短很多ovo

​  大概是長這個樣子:

#define OR 0
#define AND 1
#define XOR 2

void fwt(int *a,int op,int type,int len){//op=1為FWT,否則為逆FWT
	int v,u;
	for (int step=2;step<=len;step<<=1)
		for (int st=0;st<len;st+=step)
			for (int i=0;i<step>>1;++i){
				u=a[st+i]; v=a[st+i+(step>>1)];//u=A0,v=A1
				if (op==1){
					if (type==XOR)
						a[st+i]=(u+v)%MOD,a[st+i+(step>>1)]=(u+MOD-v)%MOD;
					else if (type==AND)
						a[st+i]=(u+v)%MOD;
					else
						a[st+i+(step>>1)]=(u+v)%MOD;

				}
				else{
					if (type==XOR)
						a[st+i]=1LL*(u+v)*inv2%MOD,a[st+i+(step>>1)]=1LL*(1LL*u+MOD-v)*inv2%MOD;
					else if (type==AND)
						a[st+i]=(1LL*u-v+MOD)%MOD;
					else
						a[st+i+(step>>1)]=(1LL*v-u+MOD)%MOD;//v-u!!
				}
			}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM