其他分享
首页 > 其他分享> > FWT(快速沃尔什变换)简要讲解

FWT(快速沃尔什变换)简要讲解

作者:互联网

写在前面

本蒟蒻很菜,所以这篇博客依然几乎不会有证明

还是先说这东西是干什么的

平时我们所见的卷积是这样的:
\[ h(k) = \sum_{i + j = k} f(i) \cdot g(j) \]
但是偶尔还会遇到条件中的加号变成其它符号的情况,\(FWT\)就是这个符号是按位与,或,异时快速求解\(h\)的

主要的三种情况

其实就是我们要构造一种变换\(tf\),使得\(tf(h) = tf(f) \cdot tf(g)\),其中点乘表示对应位相乘

或卷积(|)

即:
\[ h(k) = \sum_{i | j = k} f(i) \cdot g(j) \]

我们可以构造变换\(FWT(A)\),使得\(FWT(A)_i = \sum_{j | i = i} A_j\)(我也不知道这个方法是怎么想出来的……)

但是验证它还是不难的,首先有这个:\(i | j | k = k \Rightarrow i | k = k, j | k = k\)

那么
\[ \begin{align} FWT(h)_i = \sum_{j | i = i} h_j & = \sum_{j | i = i} \sum_{s | t = j} f(s) \cdot g(t) \\ & = \sum_{s | i = i} \sum_{t | i = i} f(s) \cdot g(t) \\ & = \sum_{s | i = i} f(s) \sum_{t | i = i} g(t) = FWT(f)_i \cdot FWT(g)_i \end{align} \]
然后问题变成了快速求\(FWT(A)\)

由于这个变换满足下面的等式:
\[ FWT(A) = (FWT(A_0), FWT(A_0) + FWT(A_1)) \]
其中\(A_0\)表示序列前半段,\(A1\)表示序列后半段,加号表示对应位置相加。简单理解一下就是根据定义,\(FWT(A)_i\)是\(i\)的所有子集\(j\)处\(A_j\)之和,而序列前半段的下标二进制最高位为\(0\),增加这一位并不会产生新的子集,后半段下标二进制最高位为\(1\),增加这一位后前面高位为\(0\)的部分也会成为新的子集

所以显然就可以每次将序列分成前后两段分别计算,然后合并即可

然后怎么逆变换呢,根据上面的正变换,不难发现就是减掉所有它的子集处的值就是了,可以得出:
\[ IFWT(A) = (IFWT(A_0), IFWT(A_1) - IFWT(A_0)) \]

与卷积(&)

与和或在各个方面都联系紧密,那么就可以类比或卷积,不难得出:
\[ \begin{align} FWT(A) & = (FWT(A_0) + FWT(A_1), FWT(A_1)) \\ IFWT(A) & = (IFWT(A_0) - IFWT(A_1), IFWT(A_1)) \end{align} \]

异或卷积(^)

这个比较复杂,蒟蒻我并没能吃透,好像和奇偶性有关??

结论是:
\[ \begin{align} FWT(A) & = (FWT(A_0) + FWT(A_1), FWT(A_0) - FWT(A_1)) \\ IFWT(A) & = (\frac{IFWT(A_0) + IFWT(A_1)}{2}, \frac{IFWT(A_0) - IFWT(A_1)}{2}) \end{align} \]

另外一个很少见的运算——同或(\(\odot\))

直接上结论了:
\[ \begin{align} FWT(A) & = (FWT(A_1) - FWT(A_0), FWT(A_1) + FWT(A_0)) \\ IFWT(A) & = (\frac{IFWT(A_1) - IFWT(A_0)}{2}, \frac{IFWT(A_1) + IFWT(A_0)}{2}) \end{align} \]

代码

模板题戳这里

因为没见到同或的题,也没有看见过同或的模板,下面的代码只有前三种运算QwQ

还有就是取模意义下除以\(2\)转换为乘上逆元QwQ

/*
    type == 0  =>  or
    type == 1  =>  and
    type == 2  =>  xor
*/
void FWT(LL *arr, int n, int type) {
    for (int len = 2, half = 1; len <= (1 << n); len <<= 1, half <<= 1)
        for (int i = 0; i < (1 << n); i += len)
            for (int j = 0; j < half; ++j)
                if (type == 0) inc(arr[i + half + j], arr[i + j]);
                else if (type == 1) inc(arr[i + j], arr[i + half + j]);
                else {
                    LL a0 = arr[i + j], a1 = arr[i + half + j];
                    inc(arr[i + j], a1); dec(arr[i + half + j] = a0, a1);
                }
}
void IFWT(LL *arr, int n, int type) {
    for (int len = 2, half = 1; len <= (1 << n); len <<= 1, half <<= 1)
        for (int i = 0; i < (1 << n); i += len)
            for (int j = 0; j < half; ++j)
                if (type == 0) dec(arr[i + half + j], arr[i + j]);
                else if (type == 1) dec(arr[i + j], arr[i + half + j]);
                else {
                    LL a0 = arr[i + j], a1 = arr[i + half + j];
                    arr[i + j] = (a0 + a1) * inv % mod;
                    arr[i + half + j] = (a0 - a1) * inv % mod;
                    if (arr[i + half + j] < 0) arr[i + half + j] += mod;
                }
}

标签:简要,cdot,sum,IFWT,卷积,沃尔什,FWT,align
来源: https://www.cnblogs.com/Rhein-E/p/10502969.html