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

1、FWT用来干啥啊

回忆一下多项式的卷积\(C_k=\sum_{i+j=k}A_i*B_j\)

我们可以用\(FFT\)来做。

甚至在一些特殊情况下,我们\(C_k=\sum_{i*j=k}A_i*B_j\)也能做(SDOI2015 序列统计)。

但是,如果我们把操作符换一下呢?

比如这样?

\(C_k=\sum_{i|j=k}A_i*B_j\)

\(C_k=\sum_{i\&j=k}A_i*B_j\)

\(C_k=\sum_{i\wedge j=k}A_i*B_j\)

似乎这就不能用\(FFT\)来做了。

这样子就有了\(FWT\)——用来解决多项式的位运算卷积

2、FWT大概要怎么搞啊

我们想一想\(FFT\)在干啥?

先对于一个多项式求出他在若干个单位根的点值表示法

再将多项式乘起来,最后再复原。

那么,我们可不可以用一个类似的思路呢?

先将多项式求出另外一个多项式\(FWT(A)\),再将对应的位置乘起来,最后再复原?

也就是\(FWT(C)=FWT(A)*FWT(B)\)(这个不是卷积,是对应位置相乘)?

废话,显然可以,要不然我还写什么\(FWT\)呢?

我们先来一点奇奇怪怪的记号吧。

因为多项式可以看成一个\(n\)维向量

所以,我们定义以下东西:

\(A+B=(A_0+B_0,A_1+B_1,......)\)

\(A-B=(A_0-B_0,A_1-B_1,......)\)

\(A\oplus B=(\sum_{i\oplus j=0}A_i*B_j,\sum_{i\oplus j=1}A_i*B_j,......)\)

3、或(or)卷积

或卷积长成这个样子:\(C_k=\sum_{i|j=k}A_i*B_j\)

写成向量的形式也就是这样子:\(A|B=(\sum_{i|j=0}A_i*B_j,\sum_{i|j=1}A_i*B_j,......)\)

很显然的一点:这个东西满足交换律,也就是\(A|B=B|A\)

再来仔细的看看,这个东西也满足结合律。

简单的证明一下\((A+B)|C=(\sum_{i|j=0}(A_i+B_i)*C_j,......)\)

很明显可以把括号拆开,然后分成两个\(\sum\),也就是\(A|C+B|C\)

我们这样子定义一下:

对于一个多项式\(A\)(最高次项是\(2^n\)),

我们把它分成两部分\(A_0,A_1\),分别表示前\(2^{n-1}\)次项和后面的\(2^{n-1}\)次项

也就是最高位为\(0\)与\(1\)的两部分。

对于或卷积,我们有:

\[FWT(A)=\begin{cases}(FWT(A_0),FWT(A_0+A_1)) & n\gt0 \\ A & n=0\end{cases}
\]

对于\(n=0\)的时候,这个是非常显然的(常数还不显然了。。。)

啥?你问打个括号,然后中间一个逗号是啥意思?

不是说了这个结果是一个\(2^n\)维向量?

也就表示前\(2^{n-1}\)项是逗号前面的东西,后面那几项是逗号后面的东西

完全可以理解为将两个多项式强行前后拼接成一个新的多项式。

好的,我们来伪证(感性理解)一下\(n>0\)的时候的式子

对于\(A_0\)中的任意一项,如果在做\(or\)卷积的时候,和任意一个\(A_1\)中的项\(or\)了一下

那么它的首位一定是\(1\),必定不会对于\(FWT(A_0)\)产生任何贡献,

所以\(FWT(A)\)的前\(2^{n-1}\)项一定等于\(FWT(A_0)\)。

后面这个东西看起来就很不好证明了,所以我们先考虑证明点别的东西。

\(FWT(A+B)=FWT(A)+FWT(B)\)

证明(伪):

对于一个多项式\(A\)的\(FWT(A)\),它一定只是若干个原多项式中的若干项的若干倍的和。

即\(FWT(A)\)一定不包含原多项式的某几项的乘积。

如果包含了原多项式的乘积,那么在求出卷积之后,

我们发现此时的结果与某个多项式自身的某两项的乘积有关

但是我们在或卷积的结果式中发现一定只与某个多项式的某一项与另一个多项式的某一项有关。

因此,我们知道\(FWT(A)\)的任意一项只与\(A\)的某几项的和有关

因此,我们这个式子可以这样拆开。

此时,这个伪证成立。(这话怎么这么别扭)

但是怎么说,总是感觉证明后面都要贴上一个伪字,

如果我们能够知道\(FWT(A)\)是个啥东西,我们就可以把这个字给扔掉了

先给出结论:

对于\(or\)卷积而言,\(FWT(A)[i]=\sum_{j|i=i}A[j]\)

它基于的原理呢?

如果\(i|k=k,j|k=k\),那么就有\((i|j)|k=k\)

这样说很不清楚,我们现在来证明后半部分为啥是\(FWT(A_0+A_1)\)

因为\(A_0\)中取一项和\(A_1\)中取一项做\(or\)卷积,显然贡献会产生到\(A_1\)中去

首先,对于\(A_1\)中的任意两项的贡献,一定在\(A_1\)中,即使去掉了最高位,此时也会产生这部分的贡献

但是现在在合并\(A_0\)和\(A_1\)的贡献的时候,还需要考虑\(A_0\)的贡献

相当于只丢掉了最高位,因此,在\(A_0\)与\(A_1\)对应项的\(FWT\)的和就是我们现在合并之后的结果

所以也就是\(FWT(A_0+A_1)=FWT(A_0)+FWT(A_1)\)

这样子,我们来考虑或卷积,也就是\(FWT(A|B)\)

我们要证明它等于\(FWT(A)\times FWT(B)\),这样子我们就可以放心的使用\(or\)卷积了

证明:

\[\begin{aligned}
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),FWT(A_0|B_0+A_0|B_1+A_1|B_0+A_1|B_1))\\
=&(FWT(A_0)\times FWT(B_0)\\&,FWT(A_0)\times FWT(B_0)+FWT(A_0)\times FWT(B_1)+FWT(A_1)\times FWT(B_0)+FWT(A_1)\times FWT(B_1))\\
=&(FWT(A_0)\times FWT(B_0),(FWT(A_0)+FWT(A_1))\times (FWT(B_0)+FWT(B_1)))\\
=&(FWT(A_0),FWT(A_0+A_1))\times (FWT(B_0),FWT(B_0+B_1))\\
=&FWT(A)\times FWT(B)
\end{aligned}
\]

这是一个数学归纳法的证明,请仔细看一看QwQ

当只有一项的时候这个是显然的。

好啦,这样就证明出了\(or\)卷积的正确性了

4、和(and)卷积

\(and\)卷积是这样的:\(C_k=\sum_{i\&j=k}A_i*B_j\)

写成向量的形式:\(A\&B=(\sum_{i\&j=0}A_i*B_j,\sum_{i\&j=1}A_i*B_j,......)\)

交换律?\(A\&B=B\&A\)显然成立

结合律?和前面一样是满足的。

好的,先把变换的式子写出来。

\[FWT(A)=\begin{cases}(FWT(A_0+A_1),FWT(A_1)) & n\gt0 \\ A & n=0\end{cases}
\]

从某种意义上来说,\(and\)和\(or\)和很类似的。

我们这样看:

\(0|0=0,0|1=1,1|0=1,1|1=1\)

\(0\&0=0,0\&1=0,1\&0=0,1\&1=1\)

都是\(3\)个\(0/1\),然后剩下的那个只有一个

既然如此,其实我们也可以用\(or\)卷积类似的东西很容易的证明\(and\)卷积

\(FWT(A+B)=FWT(A)+FWT(B)\)

大致的伪证就是\(FWT(A)\)是关于\(A\)中元素的一个线性组合,显然满足分配率

接着只要再证明

\(FWT(A)\times FWT(B)=FWT(A\&B)\)就行了

方法仍然是数学归纳法。

证明:

\[\begin{aligned}
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(A_0)+FWT(A_1))\times (FWT(B_0)+FWT(B_1)),FWT(A_1)*FWT(B_1))\\
=&(FWT(A_0+A_1),FWT(A_1))\times (FWT(B_0+B_1),FWT(B_1))\\
=&FWT(A)\times FWT(B)
\end{aligned}
\]

好啦,这样子\(and\)卷积就证明完啦。

5、异或(xor)卷积

为了方便打,我就把异或操作用\(\oplus\)来表示吧(而且这样似乎也是一种常用的表达方式)

主要原因是\(\wedge\)太难打了

表达式:\(C_k=\sum_{i\oplus j=k}A_i*B_j\)

向量式按照上面写就行了

先写一下\(FWT(A)\)吧

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

\(FWT(A+B)=FWT(A)+FWT(B)\)

这个显然还是成立的,理由和上面是一样的。

接下来还是证明相同的东西

\(FWT(A)\times FWT(B)=FWT(A\oplus B)\)

证明:

\[\begin{aligned}
FWT(A\oplus B)=&(FWT(A\oplus B)_0+FWT(A\oplus B)_1,FWT(A\oplus B)_0-FWT(A\oplus B)_1)\\
=&(FWT(A_0\oplus B_0+A_1\oplus B_1+A_1\oplus B_0+A_0\oplus B_1)\\&,FWT(A_0\oplus B_0+A_1\oplus B_1-A_1\oplus B_0-A_0\oplus B_1))\\
=&((FWT(A_0)+FWT(A_1))\times(FWT(B_0)+FWT(B_1))\\
&,(FWT(A_0)-FWT(A_1))\times(FWT(B_0)-FWT(B_1))\\
=&(FWT(A_0+A_1)\times(B_0+B_1),FWT(A_0-A_1)\times FWT(B_0-B_1))\\
=&(FWT(A_0+A_1),FWT(A_0-A_1))\times(FWT(B_0+B_1),FWT(B_0-B_1))\\
=&FWT(A)\times FWT(B)
\end{aligned}
\]

好啦好啦

这样子\(xor\)卷积也证明完啦。

于是我们可以开心的来写\(FWT\)啦

6、IFWT

我们现在可以在\(O(nlogn)\)的时间复杂度里面得到\(FWT(A\oplus B)\),其中\(\oplus\)表示一个位运算。

得到了\(FWT\)之后我们需要还原这个数组,也就是\(IFWT\)(叫\(UFWT\)也没啥问题??)

怎么求?

正向的过程我们知道了,逆向的反着做啊。

所以:

\(or\)卷积:$$IFWT(A)=(IFWT(A_0),IFWT(A_1)-IFWT(A_0))$$

\(and\)卷积:$$IFWT(A)=(IFWT(A_0)-IFWT(A_1),IFWT(A_1))$$

\(xor\)卷积:$$IFWT(A)=(\frac{IFWT(A_0)+IFWT(A_1)}{2},\frac{IFWT(A_0)-IFWT(A_1)}{2})$$

7、代码实现

\(or\)卷积的代码

void FWT(ll *P,int opt)
{
for(int i=2;i<=N;i<<=1)
for(int p=i>>1,j=0;j<N;j+=i)
for(int k=j;k<j+p;++k)
P[k+p]+=P[k]*opt;
}

\(and\)卷积只需要在\(or\)卷积的基础上修改一点点就好了

void FWT(ll *P,int opt)
{
for(int i=2;i<=N;i<<=1)
for(int p=i>>1,j=0;j<N;j+=i)
for(int k=j;k<j+p;++k)
P[k]+=P[k+p]*opt;
}

\(xor\)卷积其实也差不多(这个是在模意义下的\(FWT\))

如果不是在模意义下的话,开一个\(long\ long\),然后把逆元变成直接除二就好了。

void FWT(int *P,int opt)
{
for(int i=2;i<=N;i<<=1)
for(int p=i>>1,j=0;j<N;j+=i)
for(int k=j;k<j+p;++k)
{
int x=P[k],y=P[k+p];
P[k]=(x+y)%MOD;P[k+p]=(x-y+MOD)%MOD;
if(opt==-1)P[k]=1ll*P[k]*inv2%MOD,P[k+p]=1ll*P[k+p]*inv2%MOD;
}
}

Upd:

写了个好看点的板子,这样就和\(FFT\)长得很像了。

void FWT_or(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[i+j+k]=(a[j+k]+a[i+j+k])%MOD;
else a[i+j+k]=(a[i+j+k]+MOD-a[j+k])%MOD;
}
void FWT_and(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%MOD;
else a[j+k]=(a[j+k]+MOD-a[i+j+k])%MOD;
}
void FWT_xor(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD;
if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
}
}

FWT快速沃尔什变换学习笔记的更多相关文章

  1. [学习笔记]FWT——快速沃尔什变换

    解决涉及子集配凑的卷积问题 一.介绍 1.基本用法 FWT快速沃尔什变换学习笔记 就是解决一类问题: $f[k]=\sum_{i\oplus j=k}a[i]*b[j]$ 基本思想和FFT类似. 首先 ...

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

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

  3. 【原创】SpringBoot & SpringCloud 快速入门学习笔记(完整示例)

    [原创]SpringBoot & SpringCloud 快速入门学习笔记(完整示例) 1月前在系统的学习SpringBoot和SpringCloud,同时整理了快速入门示例,方便能针对每个知 ...

  4. Sass简单、快速上手_Sass快速入门学习笔记总结

    Sass是世界上最成熟.稳定和强大的专业级css扩展语言 ,除了Sass是css的一种预处理器语言,类似的语言还有Less,Stylus等. 这篇文章关于Sass快速入门学习笔记. 资源网站大全 ht ...

  5. 知识点简单总结——FWT(快速沃尔什变换),FST(快速子集变换)

    知识点简单总结--FWT(快速沃尔什变换),FST(快速子集变换) 闲话 博客园的markdown也太傻逼了吧. 快速沃尔什变换 位运算卷积 形如 $ f[ i ] = \sum\limits_{ j ...

  6. FWT快速沃尔什变换——基于朴素数学原理的卷积算法

    这是我的第一篇学习笔记,如有差错,请海涵... 目录 引子 卷积形式 算法流程 OR卷积 AND卷积 XOR卷积 模板 引子 首先,考虑这是兔子 数一数,会发现你有一只兔子,现在,我再给你一只兔子 再 ...

  7. ASP.NET Core快速入门--学习笔记系列文章索引目录

    课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 抓住国庆假期的尾巴完成了此系列课程的学习笔记输出! ASP.NET Core快 ...

  8. 初学FWT(快速沃尔什变换) 一点心得

    FWT能解决什么 有的时候我们会遇到要求一类卷积,如下: Ci=∑j⊕k=iAj∗Bk\large C_i=\sum_{j⊕k=i}A_j*B_kCi​=j⊕k=i∑​Aj​∗Bk​此处乘号为普通乘法 ...

  9. Python快速入门学习笔记(二)

    注:本学习笔记参考了廖雪峰老师的Python学习教程,教程地址为:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb49318210 ...

随机推荐

  1. python数据处理excel和pdf,并打包成exe

    之前零散的用过一点python做数据处理,这次又遇到一个数据处理的小功能,因此,记录一下整个流程,方便以后查阅. 功能要求:读取excel,找指定的PDF文件的页数是否与excel中记录的一致 整个处 ...

  2. java新知识系列 二

      1:数据库事务隔离以及事务隔离的级别 数据库事务隔离: 在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别:为了解决更新丢失,脏读,不可重读(包括虚读和幻读)等问题在标准SQL规 ...

  3. asyncio异步IO--同步原语

    asyncio同步原语与线程(threading)模块同步原语基本类似,但有两点重要区别: asyncio同步原语非线程安全,因此不应被用作系统线程同步(可以使用threading代替): async ...

  4. 【Python 20】BMR计算器4.0(异常处理)

    1.案例描述 基础代谢率(BMR):我们安静状态下(通常为静卧状态)消耗的最低热量,人的其他活动都建立在这个基础上. 计算公式: BMR(男) = (13.7*体重kg)+(5.0*身高cm)-(6. ...

  5. Docker 镜像仓库

    仓库 仓库(Repository)是集中存放镜像的地方. 一个容易混淆的概念是注册服务器(Registry).实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜 ...

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Clien

    https://blog.csdn.net/asahinokawa/article/details/84746422

  7. SQL SUM() 函数

    SUM() 函数 SUM 函数返回数值列的总数(总额). SQL SUM() 语法 SELECT SUM(column_name) FROM table_name SQL SUM() 实例 我们拥有下 ...

  8. Java 前后端List传值

    js代码 function click(){ var arrays = new Array(); for (var i = 0; i < arr.length; i++) { arrays.pu ...

  9. SpringCloud(7)服务链路追踪Spring Cloud Sleuth

    1.简介 Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可.本文主要讲述服务追踪组件zipki ...

  10. 基于 HTML5 WebGL 的 3D 棉花加工监控系统

    前言 现在的棉花加工行业还停留在传统的反应式维护模式当中,当棉花加下厂的设备突然出现故障时,控制程序需要更换.这种情况下,首先需要客户向设备生产厂家请求派出技术人员进行维护,然后生产厂家才能根据情况再 ...