快速沃尔什变换(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))
下面是代码
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); }
相关文章推荐
- [FWT] 快速沃尔什变换学习笔记
- 快速沃尔什变换 [学习笔记]
- 关于快速沃尔什变换(FWT)的一点学习和思考
- c++primer 学习笔记(1.0)快速入门
- Dubbo -- 系统学习 笔记 -- 快速启动
- 快速排序学习笔记
- guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用
- Dynamic CRM 2013学习笔记(三)快速创建实体 EntityCreater
- Kafka学习笔记2: 快速入门
- 高性能JavaScript--快速响应的用户界面(简要学习笔记三)
- NuGet学习笔记(1)——初识NuGet及快速安装使用
- 算法导论学习笔记 第7章 快速排序
- NuGet学习笔记(1)——初识NuGet及快速安装使用
- python数据结构学习笔记-2016-11-24-01-快速排序
- 让你快速学习python基础笔记002(一起动手实践)
- Bootstrap快速学习笔记(2)表单系列之二
- 安卓学习笔记之AlertDialogUtils实现快速创建自定义AlertDialog
- Log4j 学习笔记(一) log4j快速入门教程
- docker学习笔记6【docker快速删除所有镜像】
- 快速沃尔什变换FWT