另一种卷积
多项式乘法的卷积是这样的:
然而还有种神奇的卷积( ⊕ 是位运算):
FWT可以快速求这种卷积。
快速沃尔什变换
因为FFT用了系数 → 点值,用点值快速求卷积,再点值 → 系数的方法,所以我们也想让沃尔什变换具有这样的特点,于是开始构造。
离散沃尔什变换
定义
DWT(A)
是向量
A
经过离散沃尔什变换得到的向量,令变换系数为
这样没法求 f(i,j) 啊,我们来推一下:
也就是说 f(i,j)f(i,k)=f(i,j⊕k) 。
然后就可以对不同的位运算求出不同的变换系数,直接上结论:
and:f(i,j)=[i and j=i]or:f(i,j)=[i and j=j]xor:f(i,j)=(−1)count(i and j)
其中
count(i)
表示
i
在二进制下
并不知道是怎么来的,但是容易验证是对的。
这里就说明一下比较神奇的 xor ( and 和 or 很容易验证):
f(i,j)f(j,k)=(−1)count(i and j)+count(i and k)f(i,j xor k)=(−1)count(i and (j xor k))
count(i and j)+count(i and k)
求的是
i
和
f(i,j)
有一个很重要性质:可以二进制拆分。用
ik
表示
i
二进制下的第
f(i,j)=f(i0,j0)f(i1,j1)f(i2,j2)⋯f(ilen−1,jlen−1)
接下来开始考虑离散沃尔什变换(
n
补到
喜闻乐见的规模减半了,于是( i∈[0,n2) ):
和FFT很像啊,只不过FFT是奇偶拆半,而FWT是左右拆半。正因为FWT是左右拆半,直接迭代就可以了,不需要和FFT一样二进制翻转。
逆沃尔什变换
根据离散沃尔什变换得到的递归式,我们发现只需要解一下二元一次方程就可以了。
模板
inline void FWT(int *a,int n,int f){
for (int k=1;k<n;k<<=1)
for (int i=0;i<n;i+=(k<<1))
for (int j=0;j<k;j++)
if (f==1){
int x=a[i+j],y=a[i+j+k];
//and:a[i+j]+=a[i+j+k];
//or :a[i+j+k]+=a[i+j];
//xor:a[i+j]=x+y;a[i+j+k]=x-y;
} else{
int x=a[i+j],y=a[i+j+k];
//and:a[i+j]-=a[i+j+k];
//or :a[i+j+k]-=a[i+j];
//xor:a[i+j]=(x+y)/2;a[i+j+k]=(x-y)/2;
}
}