「算法」FWT(快速沃尔什变换)

「算法」FWT(快速沃尔什变换)本文详细介绍了快速沃尔什变换 FWT 如何用于解决位运算卷积问题 包括与 或 异或 同或四种运算 并提供了相应的算法实现

大家好,欢迎来到IT知识分享网。

用途

F W T FWT FWT 用来在 O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n) 的时间内解决形如 C k = ∑ i ⊕ j = k A i B j C_k=\sum_{i\oplus j=k}A_iB_j Ck=ij=kAiBj 的位运算卷积问题。

其中 ⊕ \oplus 表示 and ⁡ , or ⁡ , xor ⁡ , nxor ⁡ \operatorname{and},\operatorname{or},\operatorname{xor},\operatorname{nxor} and,or,xor,nxor 等位运算中的一种。

思路和 F F T FFT FFT 类似,都是先把多项式 A , B A,B A,B 写成类似 点值表示法 的形式,分别记为 F W T ( A ) FWT(A) FWT(A) F W T ( B ) FWT(B) FWT(B) ,使得 F W T ( C ) i = F W T ( A ) i ⋅ F W T ( B ) i FWT(C)_i=FWT(A)_i\cdot FWT(B)_i FWT(C)i=FWT(A)iFWT(B)i

现在按位运算进行分类讨论。

一些符号

这里作一些规定。

  1. 每一个序列的长度都是二的整数幂的;
  2. 下文中默认位运算的优先级在 + + + 之前,即会先计算位运算,再计算加法;
  3. ( A , B ) (A,B) (A,B) 表示把 A , B A,B A,B A A A 在前, B B B 在后的顺序拼接起来组成一个长度为 ∣ A ∣ + ∣ B ∣ |A|+|B| A+B 的序列;
  4. A + B A+B A+B 表示把两个长度相等的序列 A , B A,B A,B 的每一个对应下标上的数相加,即 ( A + B ) i = A i + B i (A+B)_i=A_i +B_i (A+B)i=Ai+Bi ,形成的序列长度为 ∣ A ∣ |A| A
  5. A − B A-B AB 表示把两个长度相等的序列 A , B A,B A,B 的每一个对应下标上的数相减,即 ( A − B ) i = A i − B i (A-B)_i=A_i -B_i (AB)i=AiBi ,形成的序列长度为 ∣ A ∣ |A| A

不同位运算下的形式

与运算

先给出一个结论: F W T ( A ) i = ∑ i and ⁡ j = i A j FWT(A)_i=\sum_{i\operatorname{and} j=i} A_j FWT(A)i=iandj=iAj

证明:

∀ i ∈ [ 0 , ∣ A ∣ ) ∪ Z , 都有 F W T ( A ) i × F W T ( B ) i = F W T ( C ) i    ⟸    ( ∑ j and ⁡ i = i A j ) ( ∑ k and ⁡ i = i B k ) = ∑ j and ⁡ k and ⁡ i = i A j B k    ⟸    { j and ⁡ i = i , k and ⁡ i = i 等价于 j and ⁡ k and ⁡ i = i \begin{aligned} &\forall i\in[0,|A|)\cup Z,\text{都有}\\ &\uad\uad FWT(A)_i\times FWT(B)_i=FWT(C)_i\\ &\impliedby\left(\sum_{j\operatorname{and} i=i}A_j\right)\left(\sum_{k\operatorname{and} i=i}B_k \right) =\sum_{j\operatorname{and} k\operatorname{and} i=i}A_jB_k\\ &\impliedby\begin{cases} j\operatorname{and} i&=i,\\ k\operatorname{and} i&=i \end{cases} \quad\text{等价于}\quad j\operatorname{and} k\operatorname{and} i=i \end{aligned} i[0,A)Z,都有FWT(A)i×FWT(B)i=FWT(C)i
jandi=iAj
(kandi=iBk)=jandkandi=iAjBk
{
jandikandi=i,=i
等价于jandkandi=i

得证。

根据这个结论直接做是 O ( n 2 ) O\left(n^2\right) O(n2) 的。

考虑像 F F T FFT FFT 那样,把序列 A A A 二分为序列 A 0 , A 1 A_0,A_1 A0,A1 ,再根据 F W T ( A 0 ) , F W T ( A 1 ) FWT(A_0),FWT(A_1) FWT(A0),FWT(A1) 算出 F W T ( A ) FWT(A) FWT(A) ,这样就可以做到 O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n) 了。

发现如果把序列 A A A 的下标用 二进制 表示, A 0 A_0 A0 是由下标中第 1 + log ⁡ 2 ∣ A ∣ 1+\log_2 |A| 1+log2A 位(也就是当前的最高位)为 0 0 0 的元素组成的, A 1 A_1 A1 中的元素的最高位则为 1 1 1

因为 and ⁡ \operatorname{and} and 运算有这样的性质:除了 1 and ⁡ 1 = 1 1\operatorname{and} 1=1 1and1=1 ,其它 0 and ⁡ 1 , 0 and ⁡ 0 , 1 and ⁡ 0 0\operatorname{and} 1,0\operatorname{and} 0,1\operatorname{and} 0 0and1,0and0,1and0 的值都为 0 0 0 。即 A 1 A_1 A1 会贡献到 A 0 A_0 A0 ,因此可以得到:

F W T ( A ) = ( F W T ( A 0 ) + F W T ( A 1 ) , F W T ( A 1 ) ) FWT(A)=(FWT(A_0)+FWT(A_1),FWT(A_1)) FWT(A)=(FWT(A0)+FWT(A1),FWT(A1))

那反演就容易了:

U F W T ( A ) = ( U F W T ( A 0 ) − U F W T ( A 1 ) , U F W T ( A 1 ) ) UFWT(A)=(UFWT(A_0)-UFWT(A_1),UFWT(A_1)) UFWT(A)=(UFWT(A0)UFWT(A1),UFWT(A1))

代码

inline void fwt_and(int f[],int opt) { 
    for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) f[k]+=f[k+j]*opt; } 

或运算

同样的,先给出结论: F W T ( A ) i = ∑ i or ⁡ j = i A j FWT(A)_i=\sum_{i\operatorname{or} j=i} A_j FWT(A)i=iorj=iAj

证明是类似的,就不写了。结论也是类似的:

F W T ( A ) = ( F W T ( A 0 ) , F W T ( A 0 ) + F W T ( A 1 ) ) U F W T ( A ) = ( U F W T ( A 0 ) , U F W T ( A 1 ) − U F W T ( A 0 ) ) \begin{aligned} FWT(A)&=(FWT(A_0),FWT(A_0)+FWT(A_1))\\ UFWT(A)&=(UFWT(A_0),UFWT(A_1)-UFWT(A_0)) \end{aligned} FWT(A)UFWT(A)=(FWT(A0),FWT(A0)+FWT(A1))=(UFWT(A0),UFWT(A1)UFWT(A0))

代码

inline void fwt_or(int f[],int opt) { 
    for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) f[k+j]+=f[k]*opt; } 

异或运算

注意这个结论长得不一样( popcount ⁡ ( x ) \operatorname{popcount}(x) popcount(x) 表示把 x x x 写成二进制形式有多少位上的数为 1 1 1 ):

F W T ( A ) i = ∑ 2 ∣ popcount ⁡ ( i and ⁡ j ) A j − ∑ 2 ∤ popcount ⁡ ( i and ⁡ j ) A j FWT(A)_i=\sum_{2\mid\operatorname{popcount}(i\operatorname{and} j)} A_j -\sum_{2\nmid\operatorname{popcount}(i\operatorname{and} j)} A_j FWT(A)i=2popcount(iandj)Aj2popcount(iandj)Aj

注意不是直接仿照 与运算或运算 的形式写成

F W T ( A ) i = ∑ i xor ⁡ j = i A j FWT(A)_i=\sum_{i\operatorname{xor} j=i} A_j FWT(A)i=ixorj=iAj

为了方便起见,定义运算符 ⊗ \otimes x ⊗ y = popcount ⁡ ( x and ⁡ y ) m o d    2 x\otimes y=\operatorname{popcount}(x\operatorname{and} y)\mod{2} xy=popcount(xandy)mod2

那么这个运算符是满足以下性质的:

( x ⊗ y ) xor ⁡ ( x ⊗ z ) = x ⊗ ( y xor ⁡ z ) (x\otimes y)\operatorname{xor}(x\otimes z)=x\otimes (y\operatorname{xor} z) (xy)xor(xz)=x(yxorz)

证明:

a = popcount ⁡ ( x and ⁡ y and ⁡ z ) , b = popcount ⁡ ( x xor ⁡ y ) − a , c = popcount ⁡ ( y xor ⁡ z ) − a a=\operatorname{popcount}(x\operatorname{and}y\operatorname{and}z),b=\operatorname{popcount}(x\operatorname{xor}y)-a,c=\operatorname{popcount}(y\operatorname{xor}z)-a a=popcount(xandyandz),b=popcount(xxory)a,c=popcount(yxorz)a

那么

   popcount ⁡ ( x and ⁡ ( y xor ⁡ z ) ) = b + c = popcount ⁡ ( x xor ⁡ y ) + popcount ⁡ ( x xor ⁡ z ) − 2 a ∴ x ⊗ ( y xor ⁡ z ) = ( x ⊗ y + x ⊗ z ) m o d    2 = ( x ⊗ y ) xor ⁡ ( x ⊗ z ) \begin{aligned} &\quad\;\operatorname{popcount}(x\operatorname{and}(y\operatorname{xor}z))\\&=b+c\\ &=\operatorname{popcount}(x\operatorname{xor}y)+\operatorname{popcount}(x\operatorname{xor}z)-2a\\ &\therefore x\otimes(y\operatorname{xor}z)\\ &=(x\otimes y+x\otimes z)\mod{2}\\ &=(x\otimes y)\operatorname{xor}(x\otimes z) \end{aligned} popcount(xand(yxorz))=b+c=popcount(xxory)+popcount(xxorz)2ax(yxorz)=(xy+xz)mod2=(xy)xor(xz)

得证。

然后现在来证明结论:

证明:

F W T ( A ) i ⋅ F W T ( B ) i = ( ∑ j ⊗ i = 0 A j − ∑ j ⊗ i = 1 A j ) ( ∑ k ⊗ i = 0 B k − ∑ k ⊗ i = 1 B k ) = ( ∑ j ⊗ i = 0 A j ) ( ∑ k ⊗ i = 0 B k ) − ( ∑ j ⊗ i = 0 A j ) ( ∑ k ⊗ i = 1 B k ) − ( ∑ j ⊗ i = 1 A j ) ( ∑ k ⊗ i = 0 B k ) + ( ∑ j ⊗ i = 1 A j ) ( ∑ k ⊗ i = 1 B k ) = ∑ i ⊗ ( j xor ⁡ k ) = 0 A j B k − ∑ i ⊗ ( j xor ⁡ k ) = 1 A j B k = F W T ( C ) i \begin{aligned} &FWT(A)_i\cdot FWT(B)_i\\ =&\left(\sum_{j\otimes i=0}A_j-\sum_{j\otimes i=1}A_j \right)\left(\sum_{k\otimes i=0}B_k-\sum_{k\otimes i=1}B_k \right)\\ =&\left(\sum_{j\otimes i=0}A_j\right)\left(\sum_{k\otimes i=0}B_k\right)-\left(\sum_{j\otimes i=0}A_j\right)\left(\sum_{k\otimes i=1}B_k\right)-\\ &\left(\sum_{j\otimes i=1}A_j\right)\left(\sum_{k\otimes i=0}B_k\right)+\left(\sum_{j\otimes i=1}A_j\right)\left(\sum_{k\otimes i=1}B_k\right)\\ =&\sum_{i\otimes(j\operatorname{xor}k)=0}A_jB_k-\sum_{i\otimes(j\operatorname{xor}k)=1}A_jB_k\\ =&FWT(C)_i \end{aligned} ====FWT(A)iFWT(B)i(ji=0Ajji=1Aj)(ki=0Bkki=1Bk)(ji=0Aj)(ki=0Bk)(ji=0Aj)(ki=1Bk)(ji=1Aj)(ki=0Bk)+(ji=1Aj)(ki=1Bk)i(jxork)=0AjBki(jxork)=1AjBkFWT(C)i

得证。

因此可以得出:

F W T ( A ) = ( F W T ( A 0 ) + F W T ( A 1 ) , F W T ( A 0 ) − F W T ( A 1 ) ) U F W T ( A ) = ( U F W T ( A 1 ) + U F W T ( A 0 ) 2 , U F W T ( A 1 ) − U F W T ( A 0 ) 2 ) \begin{aligned} FWT(A)&=(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))\\ UFWT(A)&=\left(\cfrac{UFWT(A_1)+UFWT(A_0)}{2},\cfrac{UFWT(A_1)-UFWT(A_0)}{2}\right) \end{aligned} FWT(A)UFWT(A)=(FWT(A0)+FWT(A1),FWT(A0)FWT(A1))=(2UFWT(A1)+UFWT(A0),2UFWT(A1)UFWT(A0))

代码

这份代码里面的 F W T FWT FWT 是在取模意义下的,不用取模的话修改一下就好了。

 inline int plus(int x,int y) { 
    x+=y; if(x>=P) x-=P; else if(x<0) x+=P; return x; } inline void fwt_xor(int f[],int opt) { 
    for(int i=2,j=1,x,y;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<t+j;++k) { 
    x=plus(f[k],f[k+j]),y=plus(f[k],P-f[k+j]); opt>0? f[k]=x,f[k+j]=y : (f[k]=inv2*x%P,f[k+j]=inv2*y%P) ; } } 

同或运算

同或运算下的 F W T FWT FWT 与异或的类似。

先给出结论: F W T ( A ) i = ∑ 2 ∣ popcount ⁡ ( i or ⁡ j ) A j − ∑ 2 ∤ popcount ⁡ ( i or ⁡ j ) A j FWT(A)_i=\sum_{2\mid\operatorname{popcount}(i\operatorname{or} j)} A_j -\sum_{2\nmid\operatorname{popcount}(i\operatorname{or} j)} A_j FWT(A)i=2popcount(iorj)Aj2popcount(iorj)Aj

可以得到

F W T ( A ) = ( F W T ( A 0 ) − F W T ( A 1 ) , F W T ( A 0 ) + F W T ( A 1 ) ) U F W T ( A ) = ( U F W T ( A 1 ) − U F W T ( A 0 ) 2 , U F W T ( A 1 ) + U F W T ( A 0 ) 2 ) \begin{aligned} FWT(A)&=(FWT(A_0)-FWT(A_1),FWT(A_0)+FWT(A_1))\\ UFWT(A)&=\left(\cfrac{UFWT(A_1)-UFWT(A_0)}{2},\cfrac{UFWT(A_1)+UFWT(A_0)}{2}\right) \end{aligned} FWT(A)UFWT(A)=(FWT(A0)FWT(A1),FWT(A0)+FWT(A1))=(2UFWT(A1)UFWT(A0),2UFWT(A1)+UFWT(A0))

在异或运算的代码上改一下可以了。

例题

P4717

P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

#include<cstdio> #include<cstring> using namespace std; #define fo(i,l,r) for(i=l;i<r;++i) #define N  const int P=; const long long inv2=; int m,a[N],b[N],c[N],A[N],B[N]; inline void add(int &x,int y) { 
    x+=y; if(x>=P) x-=P; else if(x<0) x+=P; } inline int plus(int x,int y) { 
    x+=y; if(x>=P) x-=P; else if(x<0) x+=P; return x; } inline void fwt_or(int f[],int opt) { 
    for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) add(f[k+j],f[k]*opt); } inline void fwt_and(int f[],int opt) { 
    for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) add(f[k],f[k+j]*opt); } inline void fwt_xor(int f[],int opt) { 
    for(int i=2,j=1,x,y;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<t+j;++k) { 
    x=plus(f[k],f[k+j]),y=plus(f[k],P-f[k+j]); opt>0? f[k]=x,f[k+j]=y : (f[k]=inv2*x%P,f[k+j]=inv2*y%P) ; } } int main() { 
    freopen("fwt.in","r",stdin); freopen("fwt.out","w",stdout); int n,i; scanf("%d",&n),m=1<<n; fo(i,0,m) scanf("%d",a+i); fo(i,0,m) scanf("%d",b+i); fo(i,0,m) A[i]=a[i],B[i]=b[i]; fwt_or(a,1),fwt_or(b,1); fo(i,0,m) c[i]=1LL*a[i]*b[i]%P; fwt_or(c,-1); fo(i,0,m) printf("%d ",c[i]);puts(""); memcpy(a,A,sizeof a),memcpy(b,B,sizeof b); fwt_and(a,1),fwt_and(b,1); fo(i,0,m) c[i]=1LL*a[i]*b[i]%P; fwt_and(c,-1); fo(i,0,m) printf("%d ",c[i]);puts(""); memcpy(a,A,sizeof a),memcpy(b,B,sizeof b); fwt_xor(a,1),fwt_xor(b,1); fo(i,0,m) c[i]=1LL*a[i]*b[i]%P; fwt_xor(c,-1); fo(i,0,m) printf("%d ",c[i]);puts(""); return 0; } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/128345.html

(0)
上一篇 2025-09-01 14:45
下一篇 2025-09-01 15:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信