zoukankan      html  css  js  c++  java
  • 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$$

      我们来想想离散傅里叶变换的本质。

      $$egin{aligned}& DFT(A)_i \
      &= A(omega_n^i)\
      &=sum_{j=0}^{n-1} A_j * (omega_n^i)^jend{aligned}$$

      令$f(n, i, j) = (omega_n^i)^j$,则

      $$DFT(A)_i = sum_{j=0}^{n-1} A_j f(n, i, j)$$

      它要满足$DFT(A)_i * DFT(B)_i = DFT(C)_i$,即

      $$(sum_{j=0}^{n-1} A_j f(n, i, j))(sum_{k=0}^{n-1} B_k f(n, i, k))=sum_{l=0}^{n-1} C_l f(n, i, l)$$

      $$sum_{j=0}^{n-1} sum_{k=0}^{n-1} A_j B_k f(n, i, j) f(n, i, k))=sum_{l=0}^{n-1} (sum_{a+b=l} A_a B_b) f(n, i, l)$$

      这时我们发现左右分别有$n^2$项,令对应项系数相等,得

      $$f(n, i, j)f(n, i, k) = f(n, i, j + k)$$

      只要任意一个可以进行逆变换且满足上述条件的$f$都可以。

      现在我们把上面的$+$都改成$oplus$,就是离散沃尔什变换即

      $$DWT(A)_i = sum_{j=0}^{n-1} A_j f(n, i, j)$$

      $$f(n, i, j)f(n, i, k) = f(n, i, j oplus k)$$

      怎么样,是不是云里雾里顿开茅塞?

      然而我们还需要变快,所以快速傅里叶变换采用

      $$f(n, i, j) = (omega_n^i)^j$$

      那它有什么优美的性质呢?

      我们发现, 由于有折半引理,$f(n, i, j)$和$f(n, i+n/2, j)$可以同时从$f(n/2,i,j)$得来。

      那么,从感性的角度,既然$oplus$是一个位运算,那么应该更容易找到一个跟位运算有关的$f$,这样就自然有类似折半引理的东西使得我们可以做到上述事情。

      例如,当$oplus$是位与时,可以取$f(i, j) = [i & j = i]$, 即$j$的二进制完全包含在$i$的二进制里时为1,否则为0。

      当$oplus$是位异或时, 可取$f(i, j) = (-1)^{count(i & j)}$,其中$count(x)$表示$x$的二进制表示中1的个数。


    逆变换:

      逆变换看上去好难啊。。。

      其实逆变换还是比较简单的。因为既然$f$跟位运算有关,我就只需要考虑某一位就好了。

      例如$oplus$是位异或时我考虑$n=2,A=(a_0, a_1)$,

      那么$DWT(A) = (da_0 = a_0 + a_1, da_1 = a_0 - a_1)$

      我只需要解一个二元一次方程(把$da_0, da_1$作为常数, $a_0, a_1$作为变量)就可以解出$a_0, a_1$了。

      没了。


    关于$f$函数的构造:

      $f$函数怎么构造。。。和逆变换的方法差不多啊。。。只需要看$n=2$的情况就行(实际上一般就是$-1$的几次幂,或者$0, 1, -1$)

      如果记忆力好可以把所有都背下来,反正满足交换律的位运算只有8个,其中还有2个是全一和全零。。。

      把剩下六个列出来吧。。。(下列$f$函数均将第一个参数$n$省略, $[expr]$在布尔表达式$expr$为真时为1, 否则为0)

      $oplus$为位与: $f(i, j) = [j & i = i]$.

      $oplus$为位或: $f(i, j) = [j & i = j]$.

      $oplus$为位异或: $f(i, j) = (-1)^{count(i & j)}$.

      $oplus$为位与非,位或非的时候把三个数组的下标都取反就对应位或和位与。

      $oplus$为同或时直接求位异或卷积再把$C$的下标取反就行了。


    吐槽:

      明明可以背代码我偏要说这么多。。。

      只是因为闲的慌。。。

      当然是要帮助大家更好的理解FWT。

      至于为什么要满足交换律。。。我才不会告诉你我还没有搞出不满足怎么做。

       有同学说FWT难以感性理解。。。我也不知道如何感性理解。。。

      代码嘛。。。直接拿FFT改一改就好了。。。

      

     1 void FWT(int *P, int len) {
     2   if (len == 1) return;
     3   FWT(P, len / 2);
     4   FWT(P + len / 2, len / 2);
     5   for (int i = 0; i < len / 2; ++i) {
     6     int t1 = P[i], t2 = P[i + len / 2];
     7     P[i] = t1 + t2;
     8     P[i + len / 2] = t1 - t2;
     9   }
    10 }
    11 void IFWT(int *P, int len) {
    12   if (len == 1) return;
    13   for (int i = 0; i < len / 2; ++i) {
    14     int t1 = P[i], t2 = P[i + len / 2];
    15     P[i] = (t1 + t2) / 2;
    16     P[i + len / 2] = (t1 - t2) / 2;
    17   }
    18   IFWT(P, len / 2);
    19   IFWT(P + len / 2, len / 2);
    20 }
    FWT异或卷积
  • 相关阅读:
    第十六节:类与对象基本概念
    dedecms源码分析:(1)index.php
    第十二节:控制结构foreachbreakcontinueswitch
    PHP的输出缓冲区(转)
    C 结构体小结
    指针参数在内存中的传递
    C typedef用途小结
    C语言考试2 题目整理
    MinGW 环境下 给hello.exe 自定义一个图标
    JavaEE程序设计快速入门小结
  • 原文地址:https://www.cnblogs.com/y-clever/p/6875743.html
Copyright © 2011-2022 走看看