關於快速沃爾什變換(FWT)的一點學習和思考


  最近在學FWT,抽點時間出來把這個算法總結一下。

 

  快速沃爾什變換(Fast Walsh-Hadamard Transform),簡稱FWT。是快速完成集合卷積運算的一種算法。

  主要功能是求:,其中為集合運算符。

  就像FFT一樣,FWT是對數組的一種變換,我們稱數組X的變換為FWT(X)。

  所以FWT的核心思想是:

    為了求得C=A★B,我們瞎搞搞出一個變換FWT(X),

    使得FWT(C)=FWT(A)  FWT(B),然后根據FWT(C)求得C。

    (其中★表示卷積運算,表示將數組對應下標的數相乘的運算)

    也就是說我們可以通過FWT(X)變換把復雜度O(n^2)的★運算變為O(n)的運算。

  跟FFT是完全相同的。所以我們考慮怎么搞出這個FWT(X)。

 

  與運算(&)和或運算(|)最容易理解,我們先講講這兩個怎么搞。我們以或運算為例。

  若 x | y = z,那么x和y一定滿足二進制中的1為z的子集。

  所以我們可以令FWT(A)=A',其中。(“i | j = i”就是表示“j滿足二進制中的1為i的子集”)

  (雖然以上兩行根本構不成邏輯,但是請相信這個定義就是正確的)

  於是我們就有C'=A'  B',從這些數組的定義來看,這個式子確實是正確的。

  然后我們就要研究一下FWT(A)也就是A'怎么求。當然不能枚舉 i 的子集,這樣復雜度太高。

  既然是二進制的,我們不妨考慮用分治進行計算。(為毛是二進制就要用分治啊喂!)

  我們設A0為A的前一半,A1為A的后一半,如果A的長度為2^k,那么A0、A1的長度為2^(k-1)。

  如果我們知道了FWT(A0)和FWT(A1),那么FWT(A)是不是就可以求了呢?當然可以。

    FWT(A)的前一半相當於二進制位的第k位填了0,那么是它子集的仍然只有FWT(A0) (它本身);

    FWT(A)的后一半相當於二進制位的第k位填了1,那么是它子集的不僅僅有FWT(A1) (它本身),還有FWT(A0)。

  那么轉移就出來了,

  其中merge(X,Y)為X和Y兩個數組接在一起,  表示將數組對應下標的數相加的運算。

  這樣我們就在O(2^k*k)也就是O(nlogn)的時間內求出了FWT(A)。

  還有一個問題,我們怎么通過FWT(C)求得C?

  這不就是FWT()的逆變換嗎?根據轉移方程,你難道不會倒着推回來嗎?

  這其實就相當於你知道了A0'、A1',然后要求得A0、A1。

  因為我們已經知道了A0'=A0,A1'=A1+A0,所以我們就有A0=A0',A1=A1'-A0'。

  所以:

    

 

  既然知道了或運算,與運算也是一樣的,因為與運算和或運算本質是相同的。

  讀者可以試着自己推一遍,再繼續往下看:

    

    

 

  然后就是鬼畜的異或,先說結論:

    

    

  一種比較靠譜的說法是,其中d(x)為x的二進制中1的個數。

  先想一想再往下看吧~

  然后你可能會想,與和或本質上不是相同的嗎?

  然后就有,其中d'(x)為x的二進制(到第k位為止)中0的個數。

  然后就有

      

  然后結果是一毛一樣的。

  (其實就是把整個數組倒過來而已)

  所以又回到FWT的核心思想:

    為了求得C=A★B,我們瞎搞搞出一個變換FWT(X),

    使得FWT(C)=FWT(A)  FWT(B),然后根據FWT(C)求得C。

  所以我們只要找到運算中,數字x的一個特征FWT(x),滿足FWT(x  y)=FWT(x)*FWT(y)。這個特征就是FWT啦~

  怎么樣,是不是給你一種“我上我也行”的感覺?

  然后你試圖尋求異或FWT的更簡單的公式:

  然后你開始腦補:

  然后驚喜地發現正好滿足 FWT(C)=FWT(A)  FWT(B) !

  然后你開始寫轉移公式:

    

    

    

  (以上18行都是小C在口胡)

 

  到底有沒有靠譜的做法呢?小D說他有一種解方程的做法。

  為了不失一般性,假設A、B、C數組的長度為2。C=A★B,為異或卷積運算。

  然后顯然C[0] = A[0]*B[0] + A[1]*B[1]  , C[1] = A[0]*B[1] + A[1]*B[0]。

  然后我們開始變形了!由於(我們猜想)FWT(A)一定是a*FWT(A0) + b*FWT(A1)的形式,所以:

    設A'[0] = a*A[0] + b*A[1],A'[1] = c*A[0] + d*A[1]。

    B和C同理,因為小C知道列一大堆方程出來肯定沒人看所以小C就不列出來了。

  然后就有C'[0] = A'[0]*B'[0]。解方程,得:

  因為a,b不同時等於0,所以a=1,b=±1。同理,c=1,d=±1。

  (由於解方程過程中有很多槽點所以小C也不能保證正確性)

  然后究竟是+1還是-1呢?小D給的說法是“枚舉,check一下”。

  check個鬼啊(╯‵□′)╯︵┻━┻!這個做法槽點真的太多了好嗎?

  不過能夠得出正確答案是真的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM