码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-07-28 20:37:49      阅读:554      评论:0      收藏:0      [点我收藏+]

标签:code   集合   必须   排除   html   amp   比较   logs   参考   

定义

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。对于其一些我不太理解的地方加了证明和改动。如果有错误之处还请多多包涵。

 

 

 

 

 

 

 

 

 

 

  

 

 

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

标签:code   集合   必须   排除   html   amp   比较   logs   参考   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!