FWT也称快速沃尔什变换,是用来求多项式之间位运算的系数的。FWT的思想与FFT有异曲同工之妙,但较FFT来说,FWT比较简单。

前言


之前学习FFT(快速傅里叶变换)的时候,我们知道FFT是用来快速求两个多项式乘积的,即求序列C:

$$C_k=\sum_{i+j=k}A_iB_j$$

而FWT解决的多项式的位运算,即知道两个序列A与B,求:

$$C_k=\sum_{i\&j=k}A_iB_j\;\;(\& 表示位运算"与")$$

$$C_k=\sum_{i|j=k}A_iB_j\;\;(| 表示位运算"或")$$

$$C_k=\sum_{i\land j=k}A_iB_j\;\;(\land 表示位运算"异或")$$

如图FFT的解决方法,在FWT中,我们需要找到一种线性变换$FWT$,使得原序列$A$变成一个新的序列$FWT(A)$,新序列与由原序列线性相关

注意,由于FWT变换是一种线性变换,所以一定满足

$$FWT(A)+FWT(B)=FWT(A+B)$$

与FFT一样,我么需要把序列用0补成2的幂次方个,然后分割成序列为2的区间,然后更新数值,再合并,再一段段更新,再合并....直到最后合并成一个序列,然后进行最后一次更新即可得到变换后的序列。


FWT_OR


已知两个序列A,B,求新的序列C,其中

$$C=\left\{\sum_{i|j=0}A_iB_j,\sum_{i|j=1}A_iB_j,\sum_{i|j=2}A_iB_j,...\right\}$$

$$C_k=\sum_{i|j=k}A_iB_j$$

假设序列为$A$,前一半元素(前$2^{n-1}$个)元素组成的序列为$A_0$,后一半元素(后$2^{n-1}$个)元素组成的序列为$A_1$,故$A=(A_0,A_1)$

若序列A的长度为$2^n$,更新方法:

$$FWT(A)=\begin{cases}A&n=0\\(FWT(A_0),FWT(A_0)+FWT(A_1))&n>0\end{cases}$$

","表示合并前后两个序列。

此时可以证明$$FWT(C)=FWT(A|B)=FWT(A)*FWT(B)$$

借某位大佬的证明方法:

$FWT(A|B)=FWT((A|B)_0,(A|B)_1)$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A_0|B_0,A0|B_1+A_1|B0+A_1|B_1)$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0|B_0),FWT(A_0|B_0+A_0|B_1+A_1|B_0+A_1|B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0)×FWT(B_0),$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ FWT(A_0)×FWT(B_0)+FWT(A_0)×FWT(B_1)+$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ FWT(A_1)×FWT(B_0)+FWT(A_1)×FWT(B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0)×FWT(B_0),(FWT(A_0)+FWT(A_1))×$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (FWT(B_0)+FWT(B_1)))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0),FWT(A_0+A_1))×(FWT(B_0),FWT(B_0+B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A)×FWT(B)$

然后对FWT(C)序列进行FWT逆变换(UFWT)即可得到C序列。

逆变换的更新方法可以根据正变换的形式得到,为

$$UFWT(A)=(UFWT(A_0),UFWT(A_1)-UFWT(A_0))$$

FWT或变换代码:

typedef long long ll;
void FWT_or(ll *a,int n){
for(int i=;i<=n;i<<=)//i表示分治的区间
for(int p=i>>,j=;j<n;j+=i)//p表示区间的一半,j表示区间开头
for(int k=j;k<j+p;++k)//k来遍历每一个区间的前半部分
a[k+p]+=a[k];//更新
return;
}

UFWT或变换代码:

typedef long long ll;
void UFWT_or(ll *a,int n){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k)
a[k+p]-=a[k];
return;
}

合并代码:

void FWT_or(ll *a,int n,int opt){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k)
a[k+p]+=a[k]*opt;
return;
}

U/FWT_or


FWT_AND


已知两个序列A,B,求新的序列C,其中

$$C=\left\{\sum_{i\&j=0}A_iB_j,\sum_{i\&j=1}A_iB_j,\sum_{i\&j=2}A_iB_j,...\right\}$$

$$C_k=\sum_{i\&j=k}A_iB_j$$

假设序列为$A$,前一半元素(前$2^{n-1}$个)元素组成的序列为$A_0$,后一半元素(后$2^{n-1}$个)元素组成的序列为$A_1$,故$A=(A_0,A_1)$

若序列A的长度为$2^n$,更新方法:

$$FWT(A)=\begin{cases}A&n=0\\(FWT(A_0)+FWT(A_1),FWT(A_1))&n>0\end{cases}$$

","表示合并前后两个序列。

此时也可以证明$$FWT(C)=FWT(A\&B)=FWT(A)*FWT(B)$$

证明方法:

$FWT(A\&B)=FWT((A\&B)_0,(A\&B)_1)$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A_0\&B_0+A_0\&B_1+A_1\&B_0,A_1\&B_1)$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0\&B_0+A_0\&B_1+A_1\&B_0+A_1\&B_1),FWT(A_1\&B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =((FWT(A0)+FWT(A1))×(FWT(B0)+FWT(B1)),FWT(A1)∗FWT(B1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A0+A1),FWT(A1))×(FWT(B0+B1),FWT(B1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A)×FWT(B)$

然后对FWT(C)序列进行FWT逆变换(UFWT)即可得到C序列。

 

逆变换的更新方法可以根据正变换的形式得到,为

$$UFWT(A)=(UFWT(A_0)-UFWT(A_1),UFWT(A_1))$$

FWT与变换代码:

void FWT_and(ll *a,int n){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k)
a[k]+=a[k+p];
return;
}

UFWT或变换代码:

void UFWT_and(ll *a,int n){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k)
a[k]-=a[k+p];
return;
}

合并代码:

void FWT_and(ll *a,int n,int opt){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k)
a[k]+=a[k+p]*opt;
return;
}

U/FWT_and


FWT_XOR


已知两个序列A,B,求新的序列C,其中

$$C=\left\{\sum_{i\oplus j=0}A_iB_j,\sum_{i\oplus j=1}A_iB_j,\sum_{i\oplus j=2}A_iB_j,...\right\}$$

$$C_k=\sum_{i\oplus j=k}A_iB_j$$

假设序列为$A$,前一半元素(前$2^{n-1}$个)元素组成的序列为$A_0$,后一半元素(后$2^{n-1}$个)元素组成的序列为$A_1$,故$A=(A_0,A_1)$

若序列A的长度为$2^n$,更新方法:

$$FWT(A)=\begin{cases}A&n=0\\(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))&n>0\end{cases}$$

","表示合并前后两个序列。

此时仍然可以证明$$FWT(C)=FWT(A\oplus B)=FWT(A)*FWT(B)$$

证明方法:

$FWT(A⊕B)=(FWT(A⊕B)_0+FWT(A⊕B)_1,FWT(A⊕B)_0−FWT(A⊕B)_1)$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0⊕B_0+A_1⊕B_1+A_1⊕B_0+A_0⊕B_1),$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ FWT(A_0⊕B_0+A_1⊕B_1−A_1⊕B_0−A_0⊕B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =((FWT(A_0)+FWT(A_1))×(FWT(B_0)+FWT(B_1)),$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (FWT(A_0)−FWT(A_1))×(FWT(B_0)−FWT(B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0+A_1)×(B_0+B_1),FWT(A_0−A_1)×FWT(B_0−B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(FWT(A_0+A_1),FWT(A_0−A_1))×(FWT(B_0+B_1),FWT(B_0−B_1))$

$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =FWT(A)×FWT(B)$

然后对FWT(C)序列进行FWT逆变换(UFWT)即可得到C序列。

逆变换的更新方法可以根据正变换的形式得到,为

$$UFWT(A)=(\frac {UFWT(A_0)+UFWT(A_1)}2,\frac {UFWT(A_0)-UFWT(A_1)}2)$$

FWT异或变换代码:

void FWT_xor(ll *a,int n){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k){
ll x=a[k],y=a[k+p];
a[k]=x+y;a[k+p]=x-y;
}
return ;
}

UFWT变换代码:

void UFWT_xor(ll *a,int n){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k){
ll x=a[k],y=a[k+p];
a[k]=(x+y)/,a[k+p]=(x-y)/;
}
return ;
}

合并代码:

void FWT_xor(ll *a,int n,int opt){
for(int i=;i<=n;i<<=)
for(int p=i>>,j=;j<n;j+=i)
for(int k=j;k<j+p;++k){
ll x=a[k],y=a[k+p];
if(opt==) a[k]=x+y;a[k+p]=x-y;
else a[k]=(x+y)/,a[k+p]=(x-y)/;
}
return ;
}

U/FWT_xor


FWT异或变换的特殊作用


在FWT异或变换中,我们主要解决一个问题

$$h(i)=\sum_{j\oplus k=i}f(j)g(k)$$

根据某站某大佬的讲解,假设存在三个集合$L,R,S$满足

$$h(S)=\sum_{R\oplus L=i}f(R)g(L)$$

则为了解决快速多项式异或,我们需要将上面的式子变形。

首先介绍一个等式,假设全集为U,集合内有n个元素,其中$|T|$表示集合T的大小,则

$$\frac 1{2^n}\sum_{T\subseteq U}(-1)^{|W\cap T|}=1$$

上面的式子仅在$W=\varnothing$时成立

解释一下:由于集合$T$是集合$U$的子集,故集合$T$有$2^n$中可能,一旦$W$不是空集,$(-1)^{|W\cap T|}$就可能等于1,那么$\sum_{T\subseteq U}(-1)^{|W\cap T|}$就会小于$2^n$。所以只有当$W$是空集时,上面式子才等于1

有了上面的等式,就可以变形了,由于$R\oplus L=S$,故$R\oplus L\oplus S=\varnothing$

$$h(S)=\sum_{R\oplus L=i}f(R)g(L)$$

$$=\sum_{R\subseteq U}\sum_{L\subseteq U}[R\oplus L\oplus S=\varnothing]f(L)g(R)$$

$$=\sum_{R\subseteq U}\sum_{L\subseteq U}\frac 1{2^n}\sum_{T\subseteq U}(-1)^{|R\oplus L\oplus S\cap T|}f(L)g(R)$$

下面证明$|T\cap \oplus^{n}_{i=1}S_i|$与$\sum_{i=1}^n|S_i\cap T|$的奇偶性相同,先证明n=2的情况:

假设$|T\cap S_1|=A,|T\cap S_2|=B$

1.假设$T\cap S_1$与$T\cap S_2$没有相同位的数相同,那么:

$$(-1)^{\sum_{i=1}^2|S_i\cap T|}=(-1)^{|S_1\cap T|+|S_2\cap T|}=(-1)^{A+B}=(-1)^{|T\cap (S_1\oplus S_2)|}$$

2.假设$T\cap S_1$与$T\cap S_2$有x组相同位的数相同,那么:

$$(-1)^{\sum_{i=1}^2|S_i\cap T|}=(-1)^{|S_1\cap T|+|S_2\cap T|}=(-1)^{A+B}$$$$(-1)^{|T\cap (\oplus_{i=1}^2S_i)|}=(-1)^{|T\cap (S_1\oplus S_2)|}=(-1)^{A+B-2x}=(-1)^{A+B}$$

当然n等于任何数的时候也是像上面一样可以证明的,所以$$(-1)^{|R\oplus L\oplus S\cap T|}=(-1)^{|R\cap T|+|S\cap T|+|L\cap T|}$$

故上面式子继续变形可得

$$\frac 1{2^n}\sum_{L\subseteq U}\sum_{R\subseteq U}\sum_{T\subseteq U}(-1)^{|L\cap T|}(-1)^{|R\cap T|}(-1)^{|S\cap T|}f(R)g(L)$$

于是发现了FWT_xor变形的本质,即变形后的序列$f(T)$与变形前序列$f(R)$的关系

$$f(T)=\sum_{R\subseteq U}(-1)^{|R\cap T|}f(R)$$

通过以上的探究,得到结论:假设原序列为A,变形后的序列为A',那么

$$A'[x]=\sum_{|x\&i|\bmod {2}=0}A[i]-\sum_{|x\&i|\bmod {2}\neq 0}A[i]$$


例题


1.2019牛客暑期多校训练营(第一场)----D-Parity of Tuples:https://blog.csdn.net/weixin_43702895/article/details/97114770

学习:多项式算法----FWT的更多相关文章

  1. 数学杂烩总结(多项式/形式幂级数+FWT+特征多项式+生成函数+斯特林数+二次剩余+单位根反演+置换群)

    数学杂烩总结(多项式/形式幂级数+FWT+特征多项式+生成函数+斯特林数+二次剩余+单位根反演+置换群) 因为不会做目录所以请善用ctrl+F 本来想的是笔记之类的,写着写着就变成了资源整理 一些有的 ...

  2. [置顶] 小白学习KM算法详细总结--附上模板题hdu2255

    KM算法是基于匈牙利算法求最大或最小权值的完备匹配 关于KM不知道看了多久,每次都不能完全理解,今天花了很久的时间做个总结,归纳以及结合别人的总结给出自己的理解,希望自己以后来看能一目了然,也希望对刚 ...

  3. 学习cordic算法所得(流水线结构、Verilog标准)

    最近学习cordic算法,并利用FPGA实现,在整个学习过程中,对cordic算法原理.FPGA中流水线设计.Verilog标准有了更加深刻的理解. 首先,cordic算法的基本思想是通过一系列固定的 ...

  4. 学习排序算法(一):单文档方法 Pointwise

    学习排序算法(一):单文档方法 Pointwise 1. 基本思想 这样的方法主要是将搜索结果的文档变为特征向量,然后将排序问题转化成了机器学习中的常规的分类问题,并且是个多类分类问题. 2. 方法流 ...

  5. 从 SGD 到 Adam —— 深度学习优化算法概览(一) 重点

    https://zhuanlan.zhihu.com/p/32626442 骆梁宸 paper插画师:poster设计师:oral slides制作人 445 人赞同了该文章 楔子 前些日在写计算数学 ...

  6. 吴裕雄 python 机器学习——集成学习AdaBoost算法回归模型

    import numpy as np import matplotlib.pyplot as plt from sklearn import datasets,ensemble from sklear ...

  7. 吴裕雄 python 机器学习——集成学习AdaBoost算法分类模型

    import numpy as np import matplotlib.pyplot as plt from sklearn import datasets,ensemble from sklear ...

  8. 学习:多项式算法----FFT

    FFT,即快速傅里叶变换,是离散傅里叶变换的快速方法,可以在很低复杂度内解决多项式乘积的问题(两个序列的卷积) 卷积 卷积通俗来说就一个公式(本人觉得卷积不重要) $$C_k=\sum_{i+j=k} ...

  9. 浅谈算法——FWT(快速沃尔什变换)

    其实FWT我啥都不会,反正就是记一波结论,记住就好-- 具体证明的话,推荐博客:FWT快速沃尔什变换学习笔记 现有一些卷积,形如 \(C_k=\sum\limits_{i\lor j=k}A_i*B_ ...

随机推荐

  1. VS2015+QT环境配置后,Lauch Qt Designer打开失败,无法打开*.ui文件

    最近在VS2015上配置QT时出现了这个问题,遂百度其解决方法,解决之后记录下来.第一步: 在[解决方案资源管理器]中,右击你的 xxx.ui文件,选择[打开方式],此时列表中默认值是[ Qt des ...

  2. (六:NIO系列) 相关设计模式

    出处:  反应器模式 vs 观察者模式  反应器模式 vs 生产者消费者模式 反应器模式 vs 观察者模式 反应器模式 是一种为处理服务请求并发提交到一个或者多个服务处理程序的事件设计模式.当请求抵达 ...

  3. 山区建小学(区间dp+前缀和+预处理)

    [题目描述] 政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往.已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i ...

  4. sql server 应用bcp进行数据导出导入

    bcp 实用工具可以在 Microsoft SQL Server 实例和用户指定格式的数据文件间大容量复制数据. 使用 bcp 实用工具可以将大量新行导入 SQL Server 表,或将表数据导出到数 ...

  5. Git基本常用指令

    开发十年,就只剩下这套架构体系了! >>>   Git基本常用命令如下: mkdir:         XX (创建一个空目录 XX指目录名) pwd:          显示当前目 ...

  6. Scrapy抓取jobbole数据

    1.python版本3.6.1 2.python编辑器:JetBrains PyCharm 2.安装virtualenvwrapper-win pip3 install virtualenvwrapp ...

  7. linux测试 Sersync 是否正常

    [root@SERSYNC web]# for i in {1..10000};do echo 123456 > /data/web/$i &>/dev/null;do ne [r ...

  8. Kintex 7五兄弟

    基KC705E 增强版 基于FMC接口的Xilinx Kintex-7 FPGA K7 XC7K325T PCIeX8 接口卡(136) 本板卡是Xilinx公司芯片V5系列芯片设计信号处理板卡.由一 ...

  9. 记录卸载5.7版本MySQL并安装5.6版本MySQL

    新版本有些问题很烦,也没时间去找解决办法,只好用回5.6,首先卸载6.7的MySQL: sudo apt-get autoremove --purge mysql-server-* apt remov ...

  10. Could not resolve all files for configuration ':app:debugCompileClasspath'.解决方案

    异常如下: Error:FAILURE: Build failed with an exception. * What went wrong:Could not resolve all files f ...