Fast Walsh-Hadamard Transform——快速沃爾什變換

模板題:數組

  給定$n = 2^k$和兩個序列$A_{0..n-1}$, $B_{0..n-1}$,求ide

  $$C_i = \sum_{j \oplus k = i} A_j B_k$$函數

  其中$\oplus$是某一知足交換律的位運算,要求複雜度$O(nlogn)$。spa


 

快速沃爾什變換:code

  這是什麼東西?能吃嗎?有用嗎? 請參閱SDOI2017r2d1-cut。blog

  看到這個你們是否是馬上想到了快速傅里葉變換?io

  $$C_i = \sum_{j + k = i} A_j B_k$$event

  咱們來想一想離散傅里葉變換的本質。模板

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

  令$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異或卷積
相關文章
相關標籤/搜索