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. Qt之UI文件设计和运行机制

    1.项目文件组成在QtCreator中新建一个WidgetApplocation项目,选中窗口基类中选中QWidget作为窗口基类,并选中"GnerateForm"复选框.创建后项 ...

  2. Topcoder SRM656div1 250 ( 期望DP )

    Problem Statement    Charlie has N pancakes. He wants to serve some of them for breakfast. We will n ...

  3. P2835 刻录光盘 (tarjan缩点)

    [题目描述] 现在假设总共有N个营员(2<=N<=200),每个营员的编号为1~N.LHC给每个人发了一张调查表,让每个营员填上自己愿意让哪些人到他那儿拷贝资料.当然,如果A愿意把资料拷贝 ...

  4. VirtualBox虚拟机与主机互通,并且虚拟机又能上网配置

    1.在Virtualbox 的全局模式下建立host-only网络,完成之后在网络邻居的属性中会出现本地连接和virtualbox host-only ethernet 连接 2.点击本地连接的属性, ...

  5. Web学习之JS总结

    银角大王武Sir的博客地址 银角大王武Sir的博客地址二 1.Javascript的作用域链 由于javascript没有块级作用域,而且每个函数作为一个作用域,如果出现嵌套函数,则就会出现作用域链 ...

  6. 2018-8-27-C#-powshell-调用

    title author date CreateTime categories C# powshell 调用 lindexi 2018-8-27 16:20:4 +0800 2018-06-18 20 ...

  7. $PMTargetFileDir 参数位置

    系统/session参数与变量参数和变量都配置在Session中,如$PMTargetFileDir.$PMBadFileDir等.这些变量有哪些.在哪里定义.是否可以修改呢?在控制台(Admin C ...

  8. spl_autoload_register() 函数实现的自动加载

    和Python用module来区分代码块不同,PHP按照命名空间来区分,开始学PHP的时候一心认定了如果想用 use 关键字来导入(Python的习惯说法)一个类或者函数或者其他对象的话,必须先inc ...

  9. JAVA四种引用方式

    JAVA四种引用方式: java.lang.ref: 强引用(直接变量赋值) 软引用(SoftReference): 只有在要发生OOM错误之前才会回收掉老的软引用对象,应用场景主要防止内存溢出.(缓 ...

  10. 超低功耗研发-STM32L151C8T6芯片(一)时钟系统概述

    前言: 由于之前对STM32Fxx系列相对熟悉,所以涉及到超低功耗设备时,自然就选用STM32家族的STM32Lxx系列产品. STM32L151C8T6 功能特点: (1)Flash:64k (2) ...