您的位置:首页 > 其它

快速沃尔什变换(FWT) 学习笔记

2017-08-02 14:33 302 查看
最近在做多校联赛的题目,发现有一道题需要用到FWT,于是我就去学了一下。膜拜一下大神,本篇博客仅对这篇博客进行一些细节上的补充。

FWT要解决的问题是

Ck=∑i⊕j=kai∗bi

其中⊕表示位运算and or xor 的其中一个

如果直接暴力枚举i和j,我们需要O(n2)的时间复杂度,但是FWT可以让这个问题在O(nlogn)的时间复杂度下解决这个问题。

它的思路有点类似于FFT,它们都是通过先做一个变换,然后直接相乘,最后逆变换得到的,而变换和逆变换的过程都是用折半、分治来完成。

以做xor的过程为例

这里用@表示卷积运算,+表示数字的加法运算,∗表示数字的乘法运算。

设A,B为一个2k的向量,如果A,B不足2^k,可以用0去补全,

设C=A@B,C也是一个2k的向量。

设tf(A)是对A做一次FWT的结果,结果也是一个2^k的向量,如果可以做FWT,需要满足的条件是:

tf(C)=tf(A)∗tf(B)

其中上面的*表示两个向量对应为相乘。

前人已经试出来了tf是一个怎么样的函数,对于xor的运算来说

当k=0是,tf(A)=tf(A) <
20000
br>
当k>0时,tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))

其中A0表示A0..2k−1−1(就是向量A的前2k−1维),A1表示A0..2k−1−1(就是向量A的后2k−1维),tf(A0)+tf(A1)和tf(A0)−tf(A1)是一个{2}^{k-1}的向量,而(tf(A0)+tf(A1),tf(A0)−tf(A1))就表示把这两个相连连起来,成为一个{2}^{k}的向量。这里就是一个递归分治的过程。

下面我们就来证明当tf是上面的那个定义的时候,满足tf(C)=tf(A)∗tf(B)

首先我们需要先证明一个引理tf(A+B)=tf(A)+tf(B)

当k=0时tf(A+B)=A+B=tf(A)+tf(B)

当k>0时

tf(A+B)

=(tf(A0+B0)+tf(A1+B1),tf(A0+B0)−tf(A1+B1))

其中A0+B0 和 A1+B1是长度为{2}^{k-1},所以

原式

=(tf(A0)+tf(B0)+tf(A1)+tf(B1),tf(A0)+tf(B0)−tf(A1)−tf(B1))

=(tf(A0)+tf(A1),tf(A0)−tf(A1))+(tf(B0)+tf(B1),tf(B0)−tf(B1))

=tf(A)+tf(B)

接下来我们还要证明tf(C)=tf(A)∗tf(B)

当k=0时,tf(C)=C=A∗B=tf(A)∗tf(B)

当k>0时,

tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))

tf(B)=(tf(B0)+tf(B1),tf(B0)−tf(B1))

tf(A)∗tf(B)

=((tf(A0)+tf(A1))∗(tf(B0)+tf(B1)),(tf(A0)−tf(A1))∗(tf(B0)−tf(B1))

暴力拆开括号

=(tf(A0)∗tf(B0)+tf(A0)∗tf(B1)+tf(A1)∗tf(B0)+tf(A1)∗tf(B1),tf(A0)∗tf(B0)−tf(A0)∗tf(B1)−tf(A1)∗tf(B0)+tf(A1)∗tf(B1))

因为tf(A0)∗tf(B0长度为2k−1,

所以tf(A0)∗tf(B0=tf(A0@B0)

所以原式

=(tf(A0@B0)+tf(A1@B1)+tf(A0@B1)+tf(A1@B0),tf(A0@B0)+tf(A1@B1)−tf(A0@B1)−tf(A1@B0))

因为异或每个位都是独立的,而我们根据最高位是0还是1,把A和B都拆成了两部分。

所以C=(C0,C1)=(A0∗B0+A1∗B1,A0∗B1+A1∗B0)

这里表示着当C0=A0∗B0+A1∗B1表示着当A的最高位为0且B的最高位是0时,或者当A的最高位为1且B的最高位是1时,xor出来的结果最高位是0,当C1=A0∗B1+A1∗B0表示着当A的最高位为0且B的最高位是1时,或者当A的最高位为1且B的最高位是0时,xor出来的结果最高位是1

所以

tf(C)=tf(C0,C1)

=tf(A0@B0+A1@B1,A0@B1+A1@B0)

=(tf(A0@B0+A1@B1)+tf(A0@B1+A1@B0),tf(A0@B0+A1@B1)−tf(A0@B1+A1∗B0))

=(tf(A0@B0)+tfA1@B1)+tf(A0@B1)+tf(A1@B0),tf(A0@B0)+tf(A1@B1)−tf(A0@B1)−tf(A1@B0))

=(tf(A0)∗tf(B0)+tf(A0)∗tf(B1)+tf(A1)∗tf(B0)+tf(A1)∗tf(B1),tf(A0)∗tf(B0)−tf(A0)∗tf(B1)−tf(A1)∗tf(B0)+tf(A1)∗tf(B1))

=((tf(A0)+tf(A1))∗(tf(B0)+tf(B1)),(tf(A0)−tf(A1))∗(tf(B0)−tf(B1))

=tf(C)

tf函数的你函数ntf也是类似。

下面给出三种运算的tf函数和ntf函数:

xor

tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))

utf(A)=(utf(A0+A12),(A0−A12)

and

tf(A)=(tf(A0)+tf(A1),tf(A1))

utf(A)=(utf(A0)−utf(A1),utf(A1))

or

tf(A)=(tf(A0),tf(A1)+tf(A0))

utf(A)=(utf(A0),utf(A1)−utf(A0))

下面是代码

void FWT(int a[],int n)
{
for(int d=1;d<n;d<<=1)
for(int m=d<<1,i=0;i<n;i+=m)
for(int j=0;j<d;j++)
{
int x=a[i+j],y=a[i+j+d];
a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
//xor:a[i+j]=x+y,a[i+j+d]=(x-y+mod)%mod;
//and:a[i+j]=x+y;
//or:a[i+j+d]=x+y;
}
}

void UFWT(int a[],int n)
{
for(int d=1;d<n;d<<=1)
for(int m=d<<1,i=0;i<n;i+=m)
for(int j=0;j<d;j++)
{
int x=a[i+j],y=a[i+j+d];
a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
//rev表示2在模mod下的逆元
//xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
//and:a[i+j]=x-y;
//or:a[i+j+d]=y-x;
}
}
void solve(int a[],int b[],int n)
{
FWT(a,n);
FWT(b,n);
for(int i=0;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;
UFWT(a,n);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM