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. PM真的不是PM

    上周写了一篇<PM意识2.0>,前同事老A留言给我说:“PM已死!”一句话勾起很多回忆啊~当年,我们在一家内资IT公司,我是质量总监,他是研发总监,带四五个PM.老A负责所有项目的计划和监 ...

  2. iOS证书配置与管理

    证书: 证书: 命名 特点 团队管理 开发证书 iOS Development 不与App ID对应 表示拥有开发应用的资格 一般只需一个,通过导出p12文件,分发给其他电脑安装: 生产证书 iOS ...

  3. Proxmox VE中出现TASK ERROR: command 'apt-get update' failed: exit code 100的解决方法

    问题描述: 出现这个错误一般在WEB或者在Proxmox VE的服务器上面能看到日志: PVE中出现TASK ERROR: command 'apt-get update' failed: exit ...

  4. windows10 1903 64位系统

    近日,微软完成并开始推送Windows 10 2019年的第一个重大升级的预览版本,版本号是v1903,命名则是2019年5月更新版. 点击下载windows10

  5. LeetCode算法题-Valid Palindrome II(Java实现)

    这是悦乐书的第287次更新,第304篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第155题(顺位题号是680).给定非空字符串s,最多可以删除一个字符. 判断它是否是回 ...

  6. java 非访问修饰符 final 的用法

    final 修饰符,用来修饰类.方法和变量 final修饰的类不能被继承 举例,String类是final类,不可以被继承: final修饰的方法不能被重写 只是不能重写,也就是不能被子类修改,但是可 ...

  7. vi命令下常用命令

    dd:删除游标所在的一整行(常用)ndd:n为数字.删除光标所在的向下n行,例如20dd则是删除光标所在的向下20行d1G:删除光标所在到第一行的所有数据dG:删除光标所在到最后一行的所有数据d$:删 ...

  8. 【English】20190320

    valid有效的 [ˈvælɪd]  solitary独立的 [ˈsɑ:ləteri] data definition not valid unless solitary qualifying有资格的 ...

  9. applicationSettings设置和appsttings

    applicationSettings 可以和sttings一样在配置文件中,设置参数.支持定义参数的类型“serializaAs=string”,并可以使用  . 语法. 可以使用.语法

  10. Mac下的效率工具autojump

    (转) IDE 用起来总是得不到满足,Mac 适合搞开发,我也十分喜欢 Mac 系统,当然可以说喜欢 Unix/Linux 系统.今天在 .zshrc 文件中添加了这么几行快捷命令: alias go ...