多项式 - 快速沃尔什变换
概述
\(FWT\) 解决的问题和核心思想与 \(FFT\) 类似,都是处理形如
的卷积,其中 \(\odot\) 是一种二元运算。在 \(FFT\) 中一般是 \(+\) ,而 \(FWT\) 处理的一般是位运算 \(\and\) , \(\or\) , \(\oplus\) 。
核心思想上,\(FWT\) 希望通过一种可逆的 \(\boldsymbol{线性}\)变换 \(FWT[A]\) 变换 \(A\) 序列与 \(B\) 序列,
使得 \(FWT[C] = FWT[A] \cdot FWT[B]\) 。
再通过逆变换 \(IFWT[C]\) 得到 \(C\) 序列。其中 \(\cdot\) 是对应位相乘。
详述
贡献系数
我们用小写字母来表示原序列,大写字母来表示变换后的序列。
我们沿用 \(FFT\) 的理解,将 \(\boldsymbol{线性}\)变换理解为乘上系数矩阵。
即 \(A_i=\sum_j f(i, j)a_j\) 。
现在,我们希望 \(f\) 满足两个性质。
\(\boldsymbol{这里注意,尽管我们叫它性质,但其实称其为我们需要满足的限制似乎更适合}\)
- \(f(i, j)\times f(i, k) = f(i, j\odot k)\) 。
- 可以拆位贡献。
性质一:
同时,
所以只要 \(f(i, j) \times f(i, k) = f(i, j\odot k)\) ,就可以保证 \(C_i = A_i \times B_i\) 。
性质二:
我们发现上一条性质没有用到位运算的性质,所以对于任意运算都是成立的。
而这条是 \(FWT\) 位运算所需的特殊性质。
等价于我们需要满足 \(f(i, j) = \prod_k f(i_k, j_k)\) ,其中 \(i_k\) 表示 \(i\) 二进制下的第 \(k\) 位。
如果我们拥有 \(f(0, 0)\), \(f(0, 1)\), \(f(1, 0)\), \(f(1, 1)\), 就可以递推出整个矩阵,所以这个性质也是好满足的。
且只要这个 \(2\times 2\) 的位矩阵可逆,整个矩阵就是可逆的。
注意,我们只是要求其满足这样的性质,并没有关于子集什么的实际含义。
Ps:和 \(FFT\) 对照着来看,\(FFT\) 矩阵的性质是由 \(w_n^k\) 保证的,而现在我们通过拆位来给矩阵赋予性质。
迭代过程
我们先假定我们已经拥有了一个可逆的贡献矩阵,来看看应该怎么转换。
然后我们就可以迭代或递归地处理这个变换了。
记 \(A_i'\) 为上一轮变换后的值,考虑如何进行下一次变换。
记 \(i'\) 第 \(k\) 位是 \(0\) 为 \(i^0\) , 第 \(k\) 位是 \(1\) 的为 \(i^1\) 。
则
void FWT(int *A) {
for (int len = 2; len <= n; len <<= 1)
for (int i = 0; i + len - 1 < n; i += len)
for (int j = i; j < i + len / 2; ++j) { f0 = A[j], f1 = A[j + len / 2];
A[j] = (1ll * f0 * f[0][0] % mod + 1ll * f1 * f[0][1] % mod) % mod;
A[j + len / 2] = (1ll * f0 * f[1][0] % mod + 1ll * f1 * f[1][1] % mod) % mod;
}
}
观察上面的迭代过程,因为每一位的贡献是独立的,并且没有实际含义,所以我们完全可以按照任意顺序枚举 \(len\) 。
同样的,还原过程也是不讲究顺序的。
求贡献矩阵
下面以 \(\or\) 操作为例。
根据上面的分析,我们只需要得到
根据性质 \(1\) ,省略第一维可以列出下列方程组:
取两组不同解,比如可以取
注意我们要求其有逆,所以不能取
合法矩阵的逆为
逆变换就是将上文中代码的 \(f\) 换成 \(f^{-1}\) 即可。
线性变换
按照上述理解,可以发现 \(FWT\) 是线性变换。
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

FWT
浙公网安备 33010602011771号