关于快速沃尔什变换(FWT)的一些个人理解

定义

FWT是一种快速完成集合卷积运算的算法。

它可以用于求解类似C[i]=∑j?k=i A[j]*B[k]的问题。

其中?代表位运算中的|,&,^的其中一种。

求解(正变换)

设F(A)是对于A的一种变换。

并且F(A)要求满足:      F(A)*F(B)=F(A?B) ①

                k*F(A)=F(k*A)   ②

               F(A+B)=F(A)+F(B) ③ (A,B长度相同)

鉴于FWT和FFT长得特别像(而且求解的问题也比较类似),我们可以借鉴一下FFT的思路,采用分治的想法。

首先先把多项式的长度用0补到2n,即多项式A为a0+a1x+a2x2+.....+a2n-1x2^n-1

我们可以将多项式A拆成A0和A1。A0为多项式下标二进制最高位为0的部分,A1即为多项式下标二进制最高位为1的部分。

则A=(A0,A1)。  (ps:此处的括号意为将A0,A1拼起来。)

我们猜测    F(A)=(k1*F(A0)+k2*F(A1),k3*F(A0)+k4*F(A1)),其中当A的长度为1时,F(A)=A

对于②式证明如下:

 假设A的长度为2n

 由原式得(k1*F(A0)+k2*F(A1),k3*F(A0)+k4*F(A1))*k=

        (k1*F(k*A0)+k2*F(k*A1),k3*F(k*A0)+k4*F(k*A1))

 则若要证明k*F(A)=F(k*A),我们需要证明的是F(k*A‘)=k*F(A‘),其中A的长度为2n-1按照此方法递归直到A的长度为1,因为k*A=k*A,所以k*F(A)=F(k*A)。证毕。

对于③式证明如下:(其实和②式的证明一样的)

 假设A,B的长度为2n

 由原式得(k1*F(A0+B0)+k2*F(A1+B1),k3*F(A0+B0)+k4*F(A1+B1))=

(k1*F(A0)+k2*F(A1),k3*F(A0)+k4*F(A1))+(k1*F(B0)+k2*F(B1),k3*F(B0)+k4*F(B1)) 

 则若要证明F(A+B)=F(A)+F(B),需要条件F(A‘+B‘)=F(A‘)+F(B),其中A的长度为2n-1照此方法递归,同理可证明。

  

如今我们证明了的正确性,以下计算是为了确保正确。

(以下计算以异或为例)

        F(C)=F(A)*F(B)

      C=A?B→ (A0,A1)?(B0,B1)=(A0?B0+A1?B1,A1?B0+A0?B1

可以得出  (以下我们以k1,k2为例)

  F(A)的前半部分                     F(B)的前半部分                                     F(C)的前半部分

           ↓                   ↓                           

 k1*F(A0)+k2*F(A1))*k1*F(B0)+k2*F(B1))

            =k1*F(A0?B0+A1?B1)+k2*F(A1?B0+A0?B1)

所以          k12F(A0?B0)+k1k2*F(A0?B1)+k1k2*F(A1?B0)+k22F(A1?B1)

            =k1*F(A0?B0)+k2*F(A0?B1)+k2*F(A1?B0)+k1*F(A1?B1)

                        可得k12=k1,k1k2=k2,k22=k1。

            解得k1,k2为(0,0)或(1,1)或(1,-1)

由于我们的操作必须可逆,所以排除掉(0,0),并且(k1,k2)(k3,k4)不相等。所以我们可以令k1=k2=k3=1,k4=-1。

   则逆变换的时候,k1=k2=k3=1/2,k4=-1/2(这个解一下方程就可以算出来了)。

如果是|或者&运算,将红色部分修改为:

  | :(A0,A1)?(B0,B1)=(A0?B0,A1?B0+A0?B1+A1?B1

  & : (A0,A1)?(B0,B1)=(A0?B0+A1?B0+A0?B1,A1?B1)

  以下代码都以异或为例

void fwt(int *a,int len)//xor
{
    for (int i=1;i<len;i<<=1)
        for (int j=0;j<len;j+=i*2)
            for (int k=0;k<i;k++)
            {
                int u=a[j+k],v=a[j+k+i];
                a[j+k]=u+v;a[j+k+i]=u-v+mod;
                if (a[j+k]>mod) a[j+k]-=mod;
                if (a[j+k+i]>mod) a[j+k+i]-=mod;
                // or:a[j+k+i]=u+v;
                // and:a[j+k]=u+v;
            }
}

  FWT逆变换代码(以异或为例)

void ufwt(int *a,int len)
{
    for (int i=1;i<len;i<<=1)
        for (int j=0;j<len;j+=i*2)
            for (int k=0;k<i;k++)
            {
                int x=a[j+k],y=a[j+k+i];
                a[k+j]=(x+y)*inv2%mod;
                a[j+k+i]=(x-y+mod)*inv2%mod;
            }
}

  其中的inv2为2的逆元。如果题目没有要求将答案除以某数,也可以写作:a[k+j]=(x+y)/2,a[k+j+i]=(x-y)/2

一个神神秘秘的问题

  学习FWT的时候我比较好奇一个问题。在正变换的时候我们先处理F(A0),F(A1)后处理F(A),那为什么我们在求逆变换的时候不需要先求F(A)的逆变换再处理F(A0),F(A1)的。。。

  请大佬不吝赐教。

本篇博客参考hy大佬的博客http://www.cnblogs.com/yoyoball/p/9260176.html。对于其一些我不太理解的地方加了证明和改动。如果有错误之处还请多多包涵。

 

  

原文地址:https://www.cnblogs.com/coco-night/p/9376925.html

时间: 2024-08-26 16:51:42

关于快速沃尔什变换(FWT)的一些个人理解的相关文章

Codeforces 662C(快速沃尔什变换 FWT)

感觉快速沃尔什变换和快速傅里叶变换有很大的区别啊orz 不是很明白为什么位运算也可以叫做卷积(或许不应该叫卷积吧) 我是看 http://blog.csdn.net/liangzhaoyang1/article/details/52819835 里的快速沃尔什变换 这里说一下自己的理解吧,快速傅里叶变换是计算卷积的,就是∑f(x)*g(n-x)这种 快速沃尔什变换也是计算∑f(x)*g(y) ,但这里是计算所有的满足x^y = n(卷积是计算x+y=n)的和 当然,异或也可以换成&,|这些运算符

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

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_

Fast Walsh-Hadamard Transform——快速沃尔什变换

模板题: 给定$n = 2^k$和两个序列$A_{0..n-1}$, $B_{0..n-1}$,求 $$C_i = \sum_{j \oplus k = i} A_j B_k$$ 其中$\oplus$是某一满足交换律的位运算,要求复杂度$O(nlogn)$. 快速沃尔什变换: 这是什么东西?有用吗?请参阅SDOI2017r2d1-cut. 看到这个大家是不是立刻想到了快速傅里叶变换? $$C_i = \sum_{j + k = i} A_j B_k$$ 我们来想想离散傅里叶变换的本质. $$\b

Codeforces 662C Binary Table(快速沃尔什变换)

Problem 给定一个n(≤20)*m(≤100 000)的01矩阵,每次操作可以将一行或一列取反. 求最终1的最少个数. Solution 前置技能:快速沃尔什变换(FWT). 观察到n较小,考虑\(O(2^n)\)枚举每一行选或不选. 不妨设f(x)表示行的操作状态为x时(我们可用一个二进制数表示状态),经过各种列操作后所得到的最少的1的个数. 可以\(O(m)\)再扫一遍所有列.但显然T飞了. 定义\(C_j\)表示有多少列的状态为j:\(E_k\)表示对于某一列而言,若它经过各种行操作

快速沃尔什变换

快速沃尔什变换 题目背景 模板题,无背景 题目描述 给定长度为\(2^n\)两个序列\(A,B\),设\(C_i=\sum_{j\oplus k=i}A_jB_k\) 分别当\(\oplus\)是or,and,xor时求出\(C\) 输入输出格式 输入格式: 第一行一个数\(n\). 第二行\(2^n\)个数\(A_0..A_{2^n-1}\) 第三行\(2^n\)个数\(B_0..B_{2^n-1}\) 输出格式: 三行每行\(2^n\)个数,分别代表\(\oplus\)是or,and,xor

快速沃尔什变换与k进制FWT

这是一篇用来卖萌的文章QAQ 考虑以下三类卷积 \(C_k = \sum \limits_{i \;or\;j = k} A_i * B_j\) \(C_k = \sum \limits_{i\;and\;j = k} A_i * B_j\) \(C_k = \sum \limits_{i\;xor\;j = k}A_i * B_j\) 由于前两种可以用FMT(高维前缀和)解决,那我们就谈谈第三种吧 下文中的\(n\)都是形如\(2^i - 1\)的数 下标的开与闭是根据好不好写来定的,但是还是

BZOJ4589 Hard Nim(快速沃尔什变换模板)

终于抽出时间来学了学,比FFT不知道好写到哪里去. #include <cstdio> typedef long long ll; const int N=65536,p=1e9+7; int k,m,n,a[N],pi[N]; bool pr(int x) {for(int i=2;i*i<=x;i++) if(x%i==0) return 0; return 1;} ll pw(ll a,int b) {ll r=1; for(;b;b>>=1,a=a*a%p) if(b

[LUOGU 4717] 快速沃尔什变换

题目传送-Luogu4717 题意: 给你\(A\),\(B\),求\[C_i=\sum_{j\oplus k=i}A_j*B_k \] \(\oplus\)包括\(or\),\(and\),\(xor\) \(len\le 2^{17}\) 题解: 裸的\(FWT\),总结先留坑 过程: 一切顺利 代码: const int BIT=17,N=1<<BIT; const ll P=998244353; int n; ll A[N],B[N]; ll a[N],b[N],c[N]; inlin

多项式 - 快速沃尔什变换

若\(·\)是一种适用于整数域的二元运算,则两多项式关于此运算的方式定义为 \(C_k = \sum_{i·j=k} A_i * B_j\),即 \(C=A·B\). \(FWT\) 主要解决多项式的常见的三种二元位运算,在三种运算下分别构造出不同的变换方式,个人认为比 \(NTT\) 简单 好背 一些.形式与 \(NTT\) 近似. 没有新东西可说,直接放上洛谷模板题的代码好了: #include <cmath> #include <queue> #include <cst