学习:多项式算法----FWT
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的更多相关文章
- 数学杂烩总结(多项式/形式幂级数+FWT+特征多项式+生成函数+斯特林数+二次剩余+单位根反演+置换群)
数学杂烩总结(多项式/形式幂级数+FWT+特征多项式+生成函数+斯特林数+二次剩余+单位根反演+置换群) 因为不会做目录所以请善用ctrl+F 本来想的是笔记之类的,写着写着就变成了资源整理 一些有的 ...
- [置顶] 小白学习KM算法详细总结--附上模板题hdu2255
KM算法是基于匈牙利算法求最大或最小权值的完备匹配 关于KM不知道看了多久,每次都不能完全理解,今天花了很久的时间做个总结,归纳以及结合别人的总结给出自己的理解,希望自己以后来看能一目了然,也希望对刚 ...
- 学习cordic算法所得(流水线结构、Verilog标准)
最近学习cordic算法,并利用FPGA实现,在整个学习过程中,对cordic算法原理.FPGA中流水线设计.Verilog标准有了更加深刻的理解. 首先,cordic算法的基本思想是通过一系列固定的 ...
- 学习排序算法(一):单文档方法 Pointwise
学习排序算法(一):单文档方法 Pointwise 1. 基本思想 这样的方法主要是将搜索结果的文档变为特征向量,然后将排序问题转化成了机器学习中的常规的分类问题,并且是个多类分类问题. 2. 方法流 ...
- 从 SGD 到 Adam —— 深度学习优化算法概览(一) 重点
https://zhuanlan.zhihu.com/p/32626442 骆梁宸 paper插画师:poster设计师:oral slides制作人 445 人赞同了该文章 楔子 前些日在写计算数学 ...
- 吴裕雄 python 机器学习——集成学习AdaBoost算法回归模型
import numpy as np import matplotlib.pyplot as plt from sklearn import datasets,ensemble from sklear ...
- 吴裕雄 python 机器学习——集成学习AdaBoost算法分类模型
import numpy as np import matplotlib.pyplot as plt from sklearn import datasets,ensemble from sklear ...
- 学习:多项式算法----FFT
FFT,即快速傅里叶变换,是离散傅里叶变换的快速方法,可以在很低复杂度内解决多项式乘积的问题(两个序列的卷积) 卷积 卷积通俗来说就一个公式(本人觉得卷积不重要) $$C_k=\sum_{i+j=k} ...
- 浅谈算法——FWT(快速沃尔什变换)
其实FWT我啥都不会,反正就是记一波结论,记住就好-- 具体证明的话,推荐博客:FWT快速沃尔什变换学习笔记 现有一些卷积,形如 \(C_k=\sum\limits_{i\lor j=k}A_i*B_ ...
随机推荐
- [常用类]StringBuffer 类,以及 StringBuilder 类
线程安全,可变的字符序列. 字符串缓冲区就像一个String ,但可以修改. 在任何时间点,它包含一些特定的字符序列,但可以通过某些方法调用来更改序列的长度和内容. 字符串缓冲区可以安全地被多个线程使 ...
- 使用pdfobject.js实现在线浏览PDF
1.pdfobject.js官网:https://pdfobject.com/ 2.在html文件中引入这个文件,以pdfobject.min.js为例 <script type="t ...
- LeetCode107. 二叉树的层次遍历 II
107. 二叉树的层次遍历 II 描述 给定一个二叉树,返回其节点值自底向上的层次遍历. (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 示例 例如,给定二叉树: [3,9,20,null ...
- ssh1
#coding=utf-8 import paramiko ssh = paramiko.SSHClient() #允许链接不在linux上.ssh文件中不在known_hosts文件中的主机 ssh ...
- 基于socket 实现单线程并发
基于socket 实现单线程并发: 基于协程实现内IO的快速切换,我们必须提前导入from gevent import monkey;monkey pacth_all() 以为 gevent spaw ...
- Ubuntu apt-get install E: 无法定位软件包Ubuntu apt-get install E: 无法定位软件包
sudo cp /etc/apt/sources.list /etc/apt/sources.list-bak #先将之前的source-list备份 sudo vi /etc/apt/sources ...
- 修改图片尺寸网站https://www.yasuotu.com/
修改图片尺寸网站https://www.yasuotu.com/
- Flask 之装饰器有关
- 先记住一句话:自定义python装饰器时一定要记住使用@functools.wraps(func)修饰wrapper - 在Flask中使用装饰器遇到AssertionError: View fu ...
- Redis基础系列-安装启动
安装 ①将Redis 的tar 包上传到opt 目录②解压缩③安装gcc 环境我们需要将源码编译后再安装,因此需要安装c 语言的编译环境!不能直接make! 可以上网,yum install –y g ...
- php strchr()函数 语法
php strchr()函数 语法 作用:搜索字符串在另一字符串中的第一次出现.直线电机哪家好 语法:strchr(string,search,before_search); 参数: 参数 描述 st ...