快速沃尔什变换(FWT)

Preface

我们知道,对于下面的式子
f ( n ) = ∑ i ⊕ j = n g ( i ) h ( j ) f(n)=\sum\limits_{i⊕j=n}g(i)h(j) f(n)=ij=ng(i)h(j)

⊕ ⊕ 为+运算时,可以用FFT优化
⊕ ⊕ 为位运算(or,and,xor)时是否也有一种优化呢

答案是肯定的。

FWT

首先,对于多项式,我们定义 A ∗ B A*B AB为某一种位运算卷积
显然它满足交换律、结合律

类似FFT。我们利用分治的思想,将多项式 A A A分成两部分 A 0 A_0 A0 A 1 A_1 A1,表示其下标二进制最高位为0的部分(前 2 n − 1 2^{n-1} 2n1项)和最高位为1的部分(后 2 n − 1 2^{n-1} 2n1项),不足的不妨用0补齐

那么A是由 A 0 , A 1 A_0,A_1 A0,A1两部分拼接而成,记为 A = ( A 0 , A 1 ) A=(A_0,A_1) A=(A0,A1)
对于一个多项式A,我们尝试找到一种变换 t f tf tf
满足:
t f ( A ∗ B ) = t f ( A ) t f ( B ) tf(A*B)=tf(A)tf(B) tf(AB)=tf(A)tf(B)
并且它存在逆变换,满足
u t f ( t f ( A ) ) = A utf(tf(A))=A utf(tf(A))=A

这样对于两个多项式位运算卷积我们就可以先将它们变换,直接按项相乘,再变换回来

下面结论就来了

or卷积

⊕ ⊕ 为or运算时
t f ( A ) = ( t f ( A 0 ) , t f ( A 0 ) + t f ( A 1 ) ) tf(A)=\left(tf(A_0),tf(A_0)+tf(A_1)\right) tf(A)=(tf(A0),tf(A0)+tf(A1))
u t f ( A ) = ( u t f ( A 0 ) , u t f ( A 1 ) − t f ( A 0 ) ) utf(A)=\left(utf(A_0),utf(A_1)-tf(A_0)\right) utf(A)=(utf(A0),utf(A1)tf(A0))

当然只有一个数时就是它本身
实际上, t f ( A ) [ i ] = ∑ j   o r   i = i A [ j ] tf(A)[i]=\sum\limits_{j\ or\ i=i}A[j] tf(A)[i]=j or i=iA[j]

and卷积

⊕ ⊕ 为and运算时
t f ( A ) = ( t f ( A 0 ) + t f ( A 1 ) , t f ( A 1 ) ) tf(A)=\left(tf(A_0)+tf(A_1),tf(A_1)\right) tf(A)=(tf(A0)+tf(A1),tf(A1))
u t f ( A ) = ( u t f ( A 0 ) − u t f ( A 1 ) , u t f ( A 1 ) ) utf(A)=\left(utf(A_0)-utf(A_1),utf(A_1)\right) utf(A)=(utf(A0)utf(A1),utf(A1))

xor 卷积

⊕ ⊕ 为xor运算时
t f ( A ) = ( t f ( A 0 ) + t f ( A 1 ) , t f ( A 0 ) − t f ( A 1 ) ) tf(A)=\left(tf(A_0)+tf(A_1),tf(A_0)-tf(A_1)\right) tf(A)=(tf(A0)+tf(A1),tf(A0)tf(A1))
u t f ( A ) = ( u t f ( A 0 ) + u t f ( A 1 ) 2 , u t f ( A 0 ) − u t f ( A 1 ) 2 ) utf(A)=\left({utf(A_0)+utf(A_1)\over 2},{utf(A_0)-utf(A_1)\over 2}\right) utf(A)=(2utf(A0)+utf(A1),2utf(A0)utf(A1))

并且对于所有的变换,都满足 t f ( A ) + t f ( B ) = t f ( A + B ) tf(A)+tf(B)=tf(A+B) tf(A)+tf(B)=tf(A+B)

证明可以用数学归纳法,拆成两部分推导一波…
博主太懒不想推了

接下来就可以快乐写FWT了
过程和FFT是类似的,不过不需要蝴蝶变换的反位

Code

or

void FWT(int *a,bool pd)
{
	for(int m=2,half=1;m<=M;half=m,m<<=1)
		for(int j=0;j<M;j+=m)
			fo(i,0,half-1)
			{
				a[i+j+half]+=a[i+j]*((pd?)-1:1);
			}
}

And

void FWT(int *a,bool pd)
{
	for(int m=2,half=1;m<=M;half=m,m<<=1)
		for(int j=0;j<M;j+=m)
			fo(i,0,half-1)
			{
				a[i+j]+=a[i+j+half]*((pd?)-1:1);
			}
}

Xor



void FWT(int *a,bool pd)
{
	for(int m=2,half=1;m<=M;half=m,m<<=1)
		for(int j=0;j<M;j+=m)
			fo(i,0,half-1)
			{
				int v=a[i+j+half];
				a[i+j+half]=a[i+j]-v,a[i+j]+=v;
				if(pd) a[i+j]/=2,a[i+j+half]/=2;
			}
}

一个应用(子集卷积)

有时候我们常常要碰到这样的问题
f ( n ) = ∑ i   o r   j = n , i   a n d   j = 0 g ( i ) h ( j ) f(n)=\sum\limits_{i\ or\ j=n,i\ and\ j=0}g(i)h(j) f(n)=i or j=n,i and j=0g(i)h(j)
也就是子集卷补集

这似乎不太好处理
巧妙的转化一下
i   o r   j = n , i   a n d   j = 0 ⟺ i   o r   j = n , b i t [ i ] + b i t [ j ] = b i t [ n ] i\ or\ j=n,i\ and\ j=0 \Longleftrightarrow i\ or \ j=n,bit[i]+bit[j]=bit[n] i or j=n,i and j=0i or j=n,bit[i]+bit[j]=bit[n],其中 b i t [ i ] bit[i] bit[i]为i在二进制下1的个数

如果我们将所有数按bit分组,每一组存一个多项式,只有bit数对应的项才有值,其他项为0

这就变成 F b i t [ n ] ( x ) = ∑ i = 0 b i t [ n ] G i ( x ) ∗ H b i t [ n ] − i ( x ) F_{bit[n](x)}=\sum\limits_{i=0}^{bit[n]}G_i(x)*H_{bit[n]-i}(x) Fbit[n](x)=i=0bit[n]Gi(x)Hbit[n]i(x)
相当于里面的位运算卷积外面套一个普通的卷积。

枚举 b i t [ n ] bit[n] bit[n],再枚举i, t f ( G ) tf(G) tf(G) t f ( H ) tf(H) tf(H)可以提前预处理好,这里直接相乘
又由于 t f ( A ) + t f ( B ) = t f ( A + B ) tf(A)+tf(B)=tf(A+B) tf(A)+tf(B)=tf(A+B)
因此我们可以直接累加进 t f ( F b i t [ n ] ) tf(F_{bit[n]}) tf(Fbit[n]),最后全部做完了再一次性 u t f utf utf回来

注意不同的bit[n]之间的F是不能加在一起的,一个bit[n]做完以后只有bit对应的项的值是有意义的。

这样预处理和后面乘积累加时间复杂度都是 O ( n log ⁡ 2 n ) 的 O(n\log ^2n)的 O(nlog2n),空间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值