zoukankan      html  css  js  c++  java
  • 快速莫比乌斯变换(FMT)和快速沃尔什变换(FWT)

    这篇博客是上篇博客的补充,重点在于如何正确构造变换的函数。


    先考虑或卷积的较简单的情况:

    [a_n=sum_{i|j=n}b_ic_j ]

    考虑我们现在要构造函数 (F(x)),满足:

    [hat a_n=F(a_n)=hat b_ncdothat c_n ]

    考虑到需要满足变换前后的次数不变,于是我们可以肯定 (F) 一定是一种线性变换,于是我们可以先将变换表示成:

    [hat a_n=sum_{i=0}^NF_{n,i}a_i ]

    那么:

    [sum_{i=0}^NF_{n,i}a_i=sum_{i=0}^NF_{n,i}b_isum_{i=0}^NF_{n,i}c_i ]

    这时,我们代入需要变换的函数(或函数):

    [sum_{i=0}^NF_{n,i}sum_{j|k=i}b_jc_k=sum_{i=0}^NF_{n,i}b_isum_{i=0}^NF_{n,i}c_i ]

    化简:

    [sum_{j=0}^Nsum_{k=0}^NF_{n,j|k}b_jc_k=sum_{j=0}^Nsum_{k=0}^NF_{n,j}F_{n,k}b_jc_k ]

    比较系数,我们有:

    [F_{n,j}F_{n,k}=F_{n,j|k} ]

    在更普遍的情况下,这就是:

    [a_n=sum_{f(j,k)=n}b_jc_kLeftrightarrow F_{n,j}F_{n,k}=F_{n,f(j,k)} ]

    回归正题,这种情况下,一种显然成立的函数就是 (F(n,i)=[n|i=n]),这就是上一篇文章中的构造来源。


    考虑完了或变换,我们来考虑与变换:

    [F_{n,j}F_{n,k}=F_{n,j&k} ]

    一种显然的构造就是 (F_{n,j}=[n&j=n]),也就是说,这是一个超集的和。

    那么我们可以倒着做高维前缀和,也就是说,我们实际做的是一个高维后缀和:

    void FMT(int *a, int N, int op) {
        for(int i = 1; i < N; i <<= 1)
          	for(int j = 0; j < N; ++j)
                if(j & i) a[j - i] += op * a[j];
    }
    

    这里并不需要倒着枚举每一个数,是因为每一维都只可能是 1 向 0 转移,也就是说我们假想的无向图当中没有长度大于 1 的链。


    让我们考虑一种更复杂的情况:异或卷积。也就是说,我们要求:

    [F_{n,j}F_{n,k}=F_{n,joplus k} ]

    什么函数满足异或等于乘法呢?答案是 (1) 的个数的奇偶性。也就是说,(F_{n,j}=(-1)^{|j|}) 是可行的,是对的吗?

    按照上述的理论,(F_{n,j}=(-1)^{|j|}) 是完全没有问题的,但是问题在于这个函数不具有逆变换!也就是说,有很多不同的 (a_n) 可以对应到同一个 (hat a_n)

    考虑如何构造一个可行的函数,这就要求 (F_{n,j}) 同时与 (n,j) 有关,这才能保证它具有可逆性(事实上还需要线性无关,但一般都可以)。那么考虑到异或对与运算有特殊的分配率:

    [(i&j)otimes(i&k)=i&(jotimes k) ]

    于是可以构造函数 (F_{n,j}=(-1)^{|n&j|}),也即:

    [hat a_n=sum_{i=0}^N(-1)^{|n&i|}a_i ]

    这不能再像 FMT 一样做高维前缀和,而是需要使用特别的分治技巧,这就是快速沃尔什变换(FWT)。

    我们考虑分治这个过程,以下均假设下标从 (0)(n-1)(n)(2) 的幂。那么假定我们已经得到了只考虑前半部分的结果 (hat a_0) 和只考虑后半部分的结果 (hat a_1)(这两者都是长为 (frac n2) 的数列,且不考虑当前的最高位),现在需要合并成 (hat a)。那么根据柿子,(hat a) 的前半部分显然二者均可以贡献 1,而后半部分前者的可以贡献 (1),后者可以贡献 (-1),这是因为后者的最高位现在变成了 (1),这是可以进行贡献的。也就是说:

    [hat a=(hat a_0+hat a_1,hat a_0-hat a_1) ]

    那么其逆变换就是:

    [a=(frac{a_0+a_1}{2},frac{a_0-a_1}{2}) ]

    这个式子也是像之前的柿子一样,不需要特意反着做,这可以通过数学归纳法得到。

    void FWT(int *a, int N, double op) {
    	for(int len = 1; len < n; len <<= 1)
    		for(int i = 0; i < n; i += 2 * len)
    			for(int k = i; k < i + len; ++k) {
    				int g = a[k], h = a[k + len];
    				a[k] = (g + h) * op, a[k + len] = (g - h) * op;
    			}
    }
    

    或卷积和与卷积也可以使用同样的方法得出,且常数是直接高维前缀和的一半(是因为每一次都是找到了恰好有贡献的位置,高位前缀和也可以稍微改一下减小常数)。

    通过同样的方法也可以得到 FFT 的卷积系数。而借此也可以得到一些其他奇奇怪怪的卷积的系数。

  • 相关阅读:
    迭代平方根
    windows怎么进如debug调试
    wxwidgets安装环境配置
    【android官方文档】与其他App交互
    Android ImageView图片自适应
    Gridview的stretchMode详解附自动宽度
    Activity生命周期解说
    一些问题
    android 文件读取(assets)
    android:scaleType属性
  • 原文地址:https://www.cnblogs.com/whx1003/p/13983057.html
Copyright © 2011-2022 走看看