這是我的第一篇學習筆記,如有差錯,請海涵...
目錄
引子
首先,考慮這是兔子
數一數,會發現你有一隻兔子,現在,我再給你一隻兔子
再數一數,會發現什麼?沒錯,你有兩隻兔子,也就是說,1+1=2!
這就是算數的基本原理了,聰明的你懂了嗎?
好,我們可以學FWT了..
卷積形式
我們回憶一下多項式乘法的式子:
這個可以用FFT或NTT優化到O(nlogn)求出每一個Ci,但不是本章的重點,只是引出卷積的概念:
而FWT主要是解決以下三種卷積形式:
算法流程
卷積的算法原理就是把一個數列快速轉換成另一種數列,然後每一位元素之間就可以直接單獨相乘計算,最後再把答案數列快速轉換回來。
FFT體現這個原理的方式就是把多項式轉換成點值表達式,然後由於每個點的橫座標相同,縱座標直接乘起來就得到最終的點值表達式,最後把答案的多項式表達通過點值表達式解出來。
那FWT怎麼做呢?
首先就是數列長度的問題,我們知道,多項式乘法最終會得到一個長爲lenA+lenB-1的多項式,而考慮位運算的卷積——很容易想出,最終的數列長度一定是,n是A、B大小轉換爲二進制後的數的最大位數。
我們設數列A的轉換數列是DWT(A),轉換後的數列A的原數列是IDWT(A)
既然它是位運算,那麼我們就按位分治
我們從二進制最高位考慮起,每次把當前位爲0或1的元素分開成兩個數列,很顯然,由於數列長度爲,直接每次從中間分開就好了,
那麼
這裏的“{ , }”是把兩個數列前後拼一起,A+B是把兩個數列排頭對齊,然後每一位相加。
具體的係數a,b,c,d是怎麼樣,or , and 和 xor 的情況是不一樣的。
OR卷積
因爲是按位或,所以當前位爲1的對0沒有影響,而0的元素都要對1有影響(0可以 | 1變成1,但是1怎麼 | 都不會變成0),於是它的DWT就是這樣
這樣DWT(A)[i]就相當於下標按位或 i 後等於 i 的元素和,轉換回去剛好就把當前位爲1的減去爲0的就行,即
這就是DWT的逆運算形式吧。
ps:巧合的是,這個玩意其實也是快速莫比烏斯變換FMT,兩個是一樣的,完全沒有區別,也就是說DWT(A)[i]其實也是i的所有子集元素和。
舉個栗子
,
解決了!
AND卷積
和or很相像
因爲是按位與,所以當前位爲0的對1沒有影響,而1的元素都要對0有影響(1可以&0變成0,但是0怎麼&都不會變成1),於是它的DWT就是這樣
這樣DWT(A)[i]就相當於下標按位與 i 後等於 i 的元素和,轉換回去剛好就把當前位爲0的減去爲1的就行,即
這又剛好是DWT的逆運算了。
再舉個栗子
,
XOR卷積
這個就比較特殊了
我們從栗子裏會發現,對於異或,我們最後其實要把 a0b0+a1b1 和 a1b0+a0b1 單獨刨出來。(這不是廢話!)
那麼在DWT(C)中,a0b0的係數要和a1b1一樣,a1b0的係數要和a0b1一樣
……
於是它的DWT就是這樣!:
這樣DWT(C)就符合條件了,它的IDWT是
這個得看栗子才明白
,
模板
下面是非遞歸版本的DWT以及IDWT,m爲數列長度()
inline void DWTOR(int *s,int m) {
for(int k = m;k > 1;k >>= 1) {
for(int i = 0;i < m;i += k) {
for(int j = i+(k>>1);j < i+k;j ++) {
int s0 = s[j-(k>>1)],s1 = s[j];
s[j] = qm((s0 +0ll+ s1) , zxy);
}
}
}
return ;
}
inline void IDWTOR(int *s,int m) {
for(int k = 2;k <= m;k <<= 1) {
for(int i = 0;i < m;i += k) {
for(int j = i+(k>>1);j < i+k;j ++) {
int s0 = s[j-(k>>1)],s1 = s[j];
s[j] = qm((s1 +0ll+ zxy - s0) , zxy);
}
}
}
return ;
}
inline void DWTAND(int *s,int m) {
for(int k = m;k > 1;k >>= 1) {
for(int i = 0;i < m;i += k) {
for(int j = i+(k>>1);j < i+k;j ++) {
LL s0 = s[j-(k>>1)],s1 = s[j];
s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy);
}
}
}
return ;
}
inline void IDWTAND(int *s,int m) {
for(int k = 2;k <= m;k <<= 1) {
for(int i = 0;i < m;i += k) {
for(int j = i+(k>>1);j < i+k;j ++) {
int s0 = s[j-(k>>1)],s1 = s[j];
s[j-(k>>1)] = qm((s0 +0ll+ zxy - s1) , zxy);
}
}
}
return ;
}
inline void DWTXOR(int *s,int m) {
for(int k = m;k > 1;k >>= 1) {
for(int i = 0;i < m;i += k) {
for(int j = i+(k>>1);j < i+k;j ++) {
int s0 = s[j-(k>>1)],s1 = s[j];
s[j] = qm((s0 +0ll+ zxy - s1) , zxy);
s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy);
}
}
}
return ;
}
inline void IDWTXOR(int *s,int m) {
for(int k = 2;k <= m;k <<= 1) {
for(int i = 0;i < m;i += k) {
for(int j = i+(k>>1);j < i+k;j ++) {
int s0 = s[j-(k>>1)],s1 = s[j];
s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy) *1ll* inv2 % zxy;
s[j] = qm((s0 +0ll+ zxy - s1) , zxy) *1ll* inv2 % zxy;
}
}
}
return ;
}
FWT就到這裏了,大家都懂了吧