zoukankan      html  css  js  c++  java
  • 初学FWT(快速沃尔什变换) 一点心得

    FWT能解决什么

    • 有的时候我们会遇到要求一类卷积,如下:
      Ci=jk=iAjBklarge C_i=sum_{j⊕k=i}A_j*B_k此处乘号为普通乘法,表示一种位运算,如 and(&)and(&)、 or()or(|)、异或 xor(xor(^))
      LaTeXLargeLaTeX打不了 ^ 啊…qwq

    FWT思想

    • 首先因为是位运算,所以需要按位分解。又因为是卷积的形式,联想到FFTFFT中利用了一种分治优化降低时间复杂度,所以我们首先把多项式拓展到22的次幂长度,方便按位分治
    • FWTFWT的思想就是利用一种向量变换来简化运算,首先我们定义向量VV(此处可理解为数组或多项式)的正变换为FWT[V]FWT[V],逆变换为FWT1[V]FWT^{-1}[V]
      • 先拿and(&)and(&)的情况举例,根据位运算常识
        (i&k) & (j&k)=(i&j) & k(i&k)~&~(j&k)=(i&j)~&~k
      • 所以构造FWT[V]i=(j&i)=iVjFWT[V]_i=sum_{(j&i)=i}V_j
        则有 FWT[C]i=FWT[A]iFWT[B]iFWT[C]_i=FWT[A]_i*FWT[B]_i
      • 那么我们只需要求出FWT[A],FWT[B]FWT[A],FWT[B],就能得到FWT[C]FWT[C],然后通过逆变换求出CC

    变换与逆变换具体实现

    • 像FFT一样,分治求FWT[V]FWT[V]。拿andand运算举例
    • 将一个长度为lenlen区间二分,那么左边和右边分别是最高位为0/10/1的数,此时递归处理左右两边。相当于先不考虑最高位,递归处理左右两边长度为len/2len/2的答案
    • 要想将两个区间合并,由于是andand运算,两个数的与运算只会变小,那么只会是右边的区间对左边造成贡献
    • 记左边处理出来的答案为XiX_i,右边处理出来的答案为YiY_i,合并后的答案为AnsiAns_iXXYY的实际含义为{Xi=i&j=i,jVjYi=i&j=i,jVjLarge left{ egin{aligned} X_i=&sum_{i&j=i,j在左边}V_j\ Y_i=&sum_{i&j=i,j在右边}V_j\ end{aligned} ight.
    • 显然有{Ansi=Xi+YiAnsi+len/2=YiLarge left{ egin{aligned} &Ans_i=X_i+Y_i\ &Ans_{i+len/2}=Y_i\ end{aligned} ight.
    • 求逆变换FWT[V]1FWT[V]^{-1}时有{Xi=AnsiAnsi+len/2Yi=Ansi+len/2Large left{ egin{aligned} &X_i=Ans_i-Ans_{i+len/2}\ &Y_i=Ans_{i+len/2}\ end{aligned} ight.
    • 于是我们就解决了与运算的问题,或运算可类比

    异或卷积

    • 异或(xor)(xor)与其他两个有点不一样(毕竟LaTeXLargeLaTeX写不出来),需要多想一想
    • 异或卷积基于以下原理
      • 定义iijj之间的奇偶性为(i&j)(i&j)中为1的位数的奇偶性,若为偶数则奇偶性是0,若为奇数则奇偶性是1。记作d(i,j)d(i,j)

      • FWT[V]i=d(i,j)=0Vjd(i,j)=1Vjlarge FWT[V]_i=sum_{d(i,j)=0}V_j-sum_{d(i,j)=1}V_j
        就有了FWT[C]i=FWT[A]iFWT[B]ilarge FWT[C]_i=FWT[A]_i*FWT[B]_i

        • 证明为d(i,k) xor d(j,k)=d(i xor j,k)d(i,k)~xor~d(j,k)=d(i~xor~j,k)
          • (i&k),(j&k)(i&k),(j&k)同时减去它们的相与的值(i&k)&(j&k)(i&k)&(j&k),它们的相对奇偶性(可以理解吧)不变,减去后(i&k),(j&k)(i&k),(j&k)在二进制下没有同时为11的位,所以异或可以直接相加
          • 所以当d(i,k)=d(j,k)d(i,k)=d(j,k),同时减去后奇偶性还是相等,那么(i xor j)&k(i~xor~j)&k的奇偶性=两个相等的奇偶性加起来=0=d(i,k) xor d(j,k)d(i,k)~xor~d(j,k)
          • 所以当d(i,k)!=d(j,k)d(i,k)!=d(j,k),同时减去后奇偶性还是不等,那么(i xor j)&k(i~xor~j)&k的奇偶性=两个不等的奇偶性加起来=1=d(i,k) xor d(j,k)d(i,k)~xor~d(j,k)
        • 证毕(看懵逼的写两个二进制数来看看,很好理解的)
      • 看看怎么分治,此处XXYY的实际含义为{Xi=d(i,j)=0,jVjd(i,j)=1,jVjYi=d(i,j)=0,jVjd(i,j)=1,jVjLarge left{ egin{aligned} X_i=&sum_{d(i,j)=0,j在左边}V_j-sum_{d(i,j)=1,j在左边}V_j\ Y_i=&sum_{d(i,j)=0,j在右边}V_j-sum_{d(i,j)=1,j在右边}V_j\ end{aligned} ight.

      • {Ansi=Xi+Yi.................(1)Ansi+len/2=XiYi.........(2)Large left{ egin{aligned} &Ans_i=X_i+Y_i.................(1)\ &Ans_{i+len/2}=X_i-Y_i.........(2)\ end{aligned} ight.

      • 怎么想呢,分类讨论吧。由于:

        • (1)(1)对于左边区间的ii,根据X,YX,Y的定义,显然满足
        • (2)(2)而对于右边区间的ii
          • jj在左边区间,j and ij~and~i的值一定和j and (ilen/2)j~and~(i-len/2)的值相等。所以加上XiX_i
          • jj在右边区间,j and ij~and~i的值一定和j and (ilen/2)j~and~(i-len/2)的值相反。所以减去YiY_i
      • 逆变换可自行推导(或看下方代码)

    Luogu板题链接:P4717 【模板】快速沃尔什变换

    写法跟FFT,NTT一模一样,还要更简(hao)单(bei)

    AC code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN = 1<<17;
    const int mod = 998244353;
    const int inv2 = 499122177;
    int n, a[MAXN], b[MAXN];
    int a1[MAXN], a2[MAXN];
    
    inline void FWT_or(int arr[], const int& len, const int& flg)
    {
    	register int x, y;
    	for(register int i = 2; i <= len; i<<=1)
    		for(register int j = 0; j < len; j += i)
    			for(register int k = j; k < j + i/2; ++k)
    			{
    				x = arr[k], y = arr[k + i/2];
    				if(~flg) arr[k + i/2] = (x + y) % mod;
    				else arr[k + i/2] = (y - x + mod) % mod;
    			}
    }
    
    inline void FWT_and(int arr[], const int& len, const int& flg)
    {
    	register int x, y;
    	for(register int i = 2; i <= len; i<<=1)
    		for(register int j = 0; j < len; j += i)
    			for(register int k = j; k < j + i/2; ++k)
    			{
    				x = arr[k], y = arr[k + i/2];
    				if(~flg) arr[k] = (x + y) % mod;
    				else arr[k] = (x - y + mod) % mod;
    			}
    }
    
    inline void FWT_xor(int arr[], const int& len, const int& flg)
    {
    	register int x, y;
    	for(register int i = 2; i <= len; i<<=1)
    		for(register int j = 0; j < len; j += i)
    			for(register int k = j; k < j + i/2; ++k)
    			{
    				x = arr[k], y = arr[k + i/2];
    				if(~flg) arr[k] = (x + y) % mod, arr[k + i/2] = (x - y + mod) % mod;
    				else arr[k] = (LL)(x + y) * inv2 % mod, arr[k + i/2] = (LL)(x - y + mod) * inv2 % mod;
    			}
    }
    
    inline void solve_or(const int& len)
    {
    	memcpy(a1, a, sizeof a);
    	memcpy(a2, b, sizeof b);
    	FWT_or(a1, len, 1);
    	FWT_or(a2, len, 1);
    	for(int i = 0; i < len; ++i)
    		a2[i] = (LL)a1[i] * a2[i] % mod;
    	FWT_or(a2, len, -1);
    	for(int i = 0; i < len; ++i)
    		printf("%d%c", a2[i], i == len-1 ? '
    ' : ' ');
    }
    
    inline void solve_and(const int& len)
    {
    	memcpy(a1, a, sizeof a);
    	memcpy(a2, b, sizeof b);
    	FWT_and(a1, len, 1);
    	FWT_and(a2, len, 1);
    	for(int i = 0; i < len; ++i)
    		a2[i] = (LL)a1[i] * a2[i] % mod;
    	FWT_and(a2, len, -1);
    	for(int i = 0; i < len; ++i)
    		printf("%d%c", a2[i], i == len-1 ? '
    ' : ' ');
    }
    
    inline void solve_xor(const int& len)
    {
    	memcpy(a1, a, sizeof a);
    	memcpy(a2, b, sizeof b);
    	FWT_xor(a1, len, 1);
    	FWT_xor(a2, len, 1);
    	for(int i = 0; i < len; ++i)
    		a2[i] = (LL)a1[i] * a2[i] % mod;
    	FWT_xor(a2, len, -1);
    	for(int i = 0; i < len; ++i)
    		printf("%d%c", a2[i], i == len-1 ? '
    ' : ' ');
    }
    
    int main ()
    {
    	scanf("%d", &n); n = 1<<n;
    	for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
    	for(int i = 0; i < n; ++i) scanf("%d", &b[i]);
    	solve_or(n);
    	solve_and(n);
    	solve_xor(n);
    }
    
  • 相关阅读:
    Java小细节
    LinkedHashMap的accessOrder的作用
    异或运算及其应用
    什么是FullStack设计
    Java正则表达式收藏
    offsetLeft,Left,clientLeft的区别
    java文件常用操作(2) 从文件末尾开始读取文件
    java文件常用操作
    各种排序方法
    2013微软暑期实习笔试&面试总结
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039447.html
Copyright © 2011-2022 走看看