【理论】快速沃尔什变换(FWT)学习笔记
FWT 处理的是位运算卷积问题。
其解决的问题是对于长度为 \(2^n\) 的数组 \(A[0\sim2^n-1],B[0\sim2^n-1]\),求出数组 \(C\) 使得其满足:
其中 \(\oplus\) 是一种位运算,一般为 \(\operatorname{And},\operatorname{Or},\operatorname{Xor}\) 中的一种。
FWT 的思想是对数组 \(A,B\) 进行 FWT 变换使得其变成 \(\operatorname{FWTA},\operatorname{FWTB}\),并满足 \(\operatorname{FWTC}\) 等于 \(\operatorname{FWTA,FWTB}\) 对应点值相乘,即有 \(\operatorname{FWTC_i}=\operatorname{FWTA_i}\times\operatorname{FWTB_i}\)。
借此求得了 \(\operatorname{FWTC}\) 之后,我们对其进行 IFWT 变换(即逆 FWT 变换)便可以成功求得 \(C\) 数组。
接下来我们来进行具体讲解:
首先,我们要将 \(A\) 数组转化为 \(\operatorname{FWTA}\),那么我们不妨设变换系数 \(c(i,j)\) 使得:
我们考虑推 \(c(i,j)\) 的一些性质。
先利用性质 \(\operatorname{FWTC_i}=\operatorname{FWTA_i}\times\operatorname{FWTB_i}\)。
代入得对于任意 \(i\) 有:
交换求和顺序:
我们得到了式 \(1\)。
再将 \(C_t=\sum\limits_{t_1\oplus t_2=k}A_{t_1}B_{t_2}\) 乘上变换系数:
将右式改为枚举 \(t_1,t_2\),得:
我们得到了式 \(2\)。
式 \(1\),式 \(2\) 中有共同的 \(\sum\limits_{t}c(i,t)C_t\)(式 \(1\) 中只是名不同,下面联立的结果也是替换变量名后的),故联立可得出:
据此,我们可以得到一个关于变换系数的一个重量级结论: \(c(i,j)c(i,k)=c(i,j\oplus k)\)。
不难发现由于其性质,实际上我们可以按位考虑 \(c(i,j)\),即设 \(i_p\) 为 \(i\) 二进制的第 \(p\) 位,那么实际上有 \(c(i,j)=\prod\limits_pc(i_p,j_p)\),证明非常容易,在此不作过多解释。
通过按位考虑,我们原先要推出的变换系数 \(c(i,j)\) 转化为了推出 \(c(0,0),c(0,1),c(1,0),c(1,1)\) 四个值,这毫无疑问将问题变得轻松了很多。
我们将变换系数求出来了,下一步呢?如何通过变换系数求出一个数组 \(A\) 对应的 \(\operatorname{FWTA}\) 呢?
我们仍然是考虑式子:
将其分为前后各大小为 \(2^{n-1}\) 的两部分:
考虑分为两部分的好处是什么,很显然对于前一部分 \(j\sim[0,2^{n-1}-1]\),最高位 \(j_0=0\),而对于后一部分 \(j\sim[2^{n-1},2^n-1]\),最高位 \(j_0=1\),可以发现,分为两部分使得二进制最高位确定了!那么根据按位思想,我们把 \(i,j\) 的最高位全部抛开变成 \(i',j'\),根据按位显然有 \(c(i,j)=c(i_0,j_0)c(i',j')\),那么原式就变成了:
考虑将下标首位为 \(0\) 的子数组记作 \(A'\),下标首位为 \(1\) 的子数组记作 \(A''\),那么显然:
-
对于 \(i_0=0\),\(\operatorname{FWTA}_i=c(0,0)\operatorname{FWTA'}_i+c(0,1)\operatorname{FWTA''}_i\)。
-
对于 \(i_0=1\),\(\operatorname{FWTA}_i=c(1,0)\operatorname{FWTA'}_i+c(1,1)\operatorname{FWTA''}_i\)。
而 FWT 采用倍增的方法,这就导致了在计算长度为 \(2^n\) 的 FWT 数组已经将长度为 \(2^{n-1}\) 的 \(\operatorname{FWTA'}\) 和 \(\operatorname{FWTA''}\) 预先计算出,自此 FWT 过程成功完成。
那么 IFWT 呢?
观察到对于 \(i_0=0\),\(\operatorname{FWTA}_i\) 的计算实际上可以表为矩阵乘法的形式:
那么不难发现 IFWT 只需要该矩阵求逆后再做一遍 FWT 即可。
同时这也对 \(c(i,j)\) 的构造增加了要求,即对应矩阵的逆必须存在(如果这条不要求显然可以构造 \(c(i,j)=0\))。
我们回忆一下线性代数的知识,若二阶方阵的逆存在那么便等价于 \(c(0,0)c(1,1)\ne c(0,1)c(1,0)\),这变成了我们构造的限制。
好的那么 FWT 以及 IFWT 的过程就结束了,我们现在要对 \(\operatorname{And},\operatorname{Or},\operatorname{Xor}\) 推导出 \(c(i,j)\)。
(另外一个利用的性质是 \(c(i,j)c(i,k)=c(i,j\oplus k)\))
\(\operatorname{Or}\):
显然 \(c(i,j)=c^2(i,j)\),故 \(c(i,j)\in\{0,1\}\)。
同时有 \(c(i,1)=c(i,0)c(i,1)\),故 \(c(i,0)=0,c(i,1)=1\) 不可能。
因为矩阵对角线乘积不等,所以 \(c(i,0)=c(i,1)=0\) 不可能,可得 \(c(i,0)\) 必须为 \(1\)。
\(c(0,0)=c(1,0)=1\),为了满足矩阵对角线成绩不等,显然 \(c(0,1)\) 和 \(c(1,1)\) 中恰有一个为 \(1\)。
不妨令 \(c(1,1)=1\)。
我们得到矩阵为:
其逆矩阵为:
观察到此时 \(c(i,j)=[i\&j=j]\),即 \(c(i,j)\) 为 \(1\) 当且仅当 \(j\) 是 \(i\) 的子集。
(逆矩阵可手动高斯消元得到,详见【模板】矩阵求逆 )
\(\operatorname{And}\):
和 \(\operatorname{Or}\) 几乎完全相同的推法。
显然 \(c(i,j)=c^2(i,j)\),故 \(c(i,j)\in\{0,1\}\)。
同时有 \(c(i,0)=c(i,0)c(i,1)\),故 \(c(i,0)=1,c(i,1)=0\) 不可能。
因为矩阵对角线乘积不等,所以 \(c(i,0)=c(i,1)=0\) 不可能,可得 \(c(i,1)\) 必须为 \(1\)。
\(c(0,1)=c(1,1)=1\),为了满足矩阵对角线成绩不等,显然 \(c(0,0)\) 和 \(c(1,0)\) 中恰有一个为 \(1\)。
不妨令 \(c(0,0)=1\)。
我们得到矩阵为:
其逆矩阵为:
观察到此时 \(c(i,j)=[i\&j=i]\),即 \(c(i,j)\) 为 \(1\) 当且仅当 \(i\) 是 \(j\) 的子集。
\(\operatorname{Xor}\):
首先 \(c(i,0)=c^2(i,0)\),得到 \(c(i,0)=0\operatorname{or}1\)。
同时,对于任意 \(j\),有 \(c(i,0)=c^2(i,j)\),由于 \(c(i,j)\) 不可能全为 \(0\)(这样就满足不了对角线成绩不等),故 \(c(i,0)=1,c(i,j)=1\operatorname{or}-1\)。
即 \(c(0,0)=c(1,0)=1\)。
因为矩阵对角线乘积不等,所以 \(c(0,1),c(1,1)\) 中必须恰一个为 \(1\) 一个为 \(-1\)。
不妨令 \(c(0,1)=1\)。
我们得到矩阵为:
其逆矩阵为:
观察到此时 \(c(i,j)=(-1)^{|i\&j|}\),即若 \(i\) 与 \(j\) 交集的大小为奇数,\(c(i,j)=-1\),反之则有 \(c(i,j)=1\)。
代码
void FWT(ll *f){
for(int len=1;len<=(1<<(n-1));len<<=1){
for(int i=0;i<(1<<n);i+=(len<<1)){
rep(j,i,i+len-1){
ll tmp=f[j];
f[j]=c[0][0]*f[j]+c[0][1]*f[j+len];
f[j+len]=c[1][0]*tmp+c[1][1]*f[j+len];
f[j]%=mod;
f[j+len]%=mod;
}
}
}
return;
}
void calc(ll tp){
rep(i,0,(1<<n)-1){
a[i]=A[i];
b[i]=B[i];
}
if(tp==1){
c[0][0]=c[1][1]=c[1][0]=1;
c[0][1]=0;
}
if(tp==2){
c[0][0]=c[0][1]=c[1][1]=1;
c[1][0]=0;
}
if(tp==3){
c[0][0]=c[0][1]=c[1][0]=1;
c[1][1]=-1;
}
FWT(a);
FWT(b);
rep(i,0,(1<<n)-1) C[i]=a[i]*b[i]%mod;
if(tp==1){
c[0][0]=c[1][1]=1;
c[0][1]=0;
c[1][0]=-1;
}
if(tp==2){
c[0][0]=c[1][1]=1;
c[0][1]=-1;
c[1][0]=0;
}
if(tp==3){
c[0][0]=c[0][1]=c[1][0]=inv(2);
c[1][1]=-c[0][0];
}
FWT(C);
rep(i,0,(1<<n)-1) C[i]=(C[i]+mod)%mod;
}
FWT 的性质:
FWT 是一种线性变换,所以其满足性质 \(\operatorname{FMT}(A+B)=\operatorname{FMT}(A)+\operatorname{FMT}(B)\)。
由此可得,数乘其同样满足 \(\operatorname{FMT}(kA)=k\operatorname{FMT}(A)\)。