FWT也称快速沃尔什变换,是用来求多项式之间位运算的系数的。FWT的思想与FFT有异曲同工之妙,但较FFT来说,FWT比较简单。
之前学习FFT(快速傅里叶变换)的时候,我们知道FFT是用来快速求两个多项式乘积的,即求序列C:
$$C_k=\sum_{i+j=k}A_iB_j$$
而FWT解决的多项式的位运算,即知道两个序列A与B,求:
$$C_k=\sum_{i\&j=k}A_iB_j\;\;(\& 表示位运算"与")$$
$$C_k=\sum_{i|j=k}A_iB_j\;\;(| 表示位运算"或")$$
$$C_k=\sum_{i\land j=k}A_iB_j\;\;(\land 表示位运算"异或")$$
如图FFT的解决方法,在FWT中,我们需要找到一种线性变换$FWT$,使得原序列$A$变成一个新的序列$FWT(A)$,新序列与由原序列线性相关。
注意,由于FWT变换是一种线性变换,所以一定满足
$$FWT(A)+FWT(B)=FWT(A+B)$$
与FFT一样,我么需要把序列用0补成2的幂次方个,然后分割成序列为2的区间,然后更新数值,再合并,再一段段更新,再合并....直到最后合并成一个序列,然后进行最后一次更新即可得到变换后的序列。
FWT_OR
已知两个序列A,B,求新的序列C,其中
$$C=\left\{\sum_{i|j=0}A_iB_j,\sum_{i|j=1}A_iB_j,\sum_{i|j=2}A_iB_j,...\right\}$$
故
$$C_k=\sum_{i|j=k}A_iB_j$$
假设序列为$A$,前一半元素(前$2^{n-1}$个)元素组成的序列为$A_0$,后一半元素(后$2^{n-1}$个)元素组成的序列为$A_1$,故$A=(A_0,A_1)$
若序列A的长度为$2^n$,更新方法:
$$FWT(A)=\begin{cases}A&n=0\\(FWT(A_0),FWT(A_0)+FWT(A_1))&n>0\end{cases}$$
","表示合并前后两个序列。
此时可以证明$$FWT(C)=FWT(A|B)=FWT(A)*FWT(B)$$
借某位大佬的证明方法:
$FWT(A|B)=FWT((A|B)_0,(A|B)_1)$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A_0|B_0,A0|B_1+A_1|B0+A_1|B_1)$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0|B_0),FWT(A_0|B_0+A_0|B_1+A_1|B_0+A_1|B_1))$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0)×FWT(B_0),$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ FWT(A_0)×FWT(B_0)+FWT(A_0)×FWT(B_1)+$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ FWT(A_1)×FWT(B_0)+FWT(A_1)×FWT(B_1))$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0)×FWT(B_0),(FWT(A_0)+FWT(A_1))×$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (FWT(B_0)+FWT(B_1)))$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0),FWT(A_0+A_1))×(FWT(B_0),FWT(B_0+B_1))$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A)×FWT(B)$
然后对FWT(C)序列进行FWT逆变换(UFWT)即可得到C序列。
逆变换的更新方法可以根据正变换的形式得到,为
$$UFWT(A)=(UFWT(A_0),UFWT(A_1)-UFWT(A_0))$$
FWT或变换代码:
typedef long long ll; void FWT_or(ll *a,int n){ for(int i=2;i<=n;i<<=1)//i表示分治的区间 for(int p=i>>1,j=0;j<n;j+=i)//p表示区间的一半,j表示区间开头 for(int k=j;k<j+p;++k)//k来遍历每一个区间的前半部分 a[k+p]+=a[k];//更新 return; }
UFWT或变换代码:
typedef long long ll; void UFWT_or(ll *a,int n){ for(int i=2;i<=n;i<<=1) for(int p=i>>1,j=0;j<n;j+=i) for(int k=j;k<j+p;++k) a[k+p]-=a[k]; return; }
合并代码:
void FWT_or(ll *a,int n,int opt){ for(int i=2;i<=n;i<<=1) for(int p=i>>1,j=0;j<n;j+=i) for(int k=j;k<j+p;++k) a[k+p]+=a[k]*opt; return; }