当前位置: 首页 > news >正文

【多项式】快速沃尔什变换 (FWT)

快速沃尔什变换(Fast Walsh Transform, FWT)是一种用于高效计算序列卷积的算法,特别适用于按位运算(如异或、与、或)定义的卷积。它在组合数学、概率论、信号处理和算法竞赛中有着重要应用。


1. 问题引入:什么是按位卷积?

1.1 普通卷积回顾

在学习FFT(快速傅里叶变换)时,我们处理的是加法卷积
ck=∑i+j=kai⋅bj c_k = \sum_{i+j=k} a_i \cdot b_j ck=i+j=kaibj
FFT可以将时间复杂度从 O(n2)O(n^2)O(n2) 优化到 O(nlog⁡n)O(n \log n)O(nlogn)

1.2 按位卷积

现在考虑以下三种按位运算卷积

(1) 按位或卷积(OR Convolution)

ck=∑i∣j=kai⋅bj c_k = \sum_{i \mid j = k} a_i \cdot b_j ck=ij=kaibj

(2) 按位与卷积(AND Convolution)

ck=∑i&j=kai⋅bj c_k = \sum_{i \& j = k} a_i \cdot b_j ck=i&j=kaibj

(3) 按位异或卷积(XOR Convolution)

ck=∑i⊕j=kai⋅bj c_k = \sum_{i \oplus j = k} a_i \cdot b_j ck=ij=kaibj

这些卷积无法直接用FFT解决,因为按位运算不满足加法的线性性质。FWT就是为了解决这类问题而设计的。


2. FWT 的核心思想

FWT 的核心思想是:

通过线性变换,将按位卷积转换为逐点乘积,类似于FFT将加法卷积转换为逐点乘积。

即:
FWT(C)=FWT(A)∘FWT(B) \text{FWT}(C) = \text{FWT}(A) \circ \text{FWT}(B) FWT(C)=FWT(A)FWT(B)
其中 ∘\circ 表示逐点乘积。

然后通过逆变换得到结果:
C=IFWT(FWT(A)∘FWT(B)) C = \text{IFWT}(\text{FWT}(A) \circ \text{FWT}(B)) C=IFWT(FWT(A)FWT(B))


3. FWT 的数学基础

3.1 变换的本质

FWT 是一种线性变换,可以表示为矩阵乘法:
A^=M⋅A \hat{A} = M \cdot A A^=MA
其中 MMM 是一个 n×nn \times nn×n 的变换矩阵(n=2mn = 2^mn=2mmmm 是位数)。

对于三种运算,MMM 的结构不同。

3.2 分治结构

FWT 利用分治策略,类似于FFT。假设序列长度为 2n2^n2n,我们可以将其分为前半部分和后半部分(对应最高位为0和1)。


4. 三种 FWT 的详细推导

我们以长度 n=2mn = 2^mn=2m 的序列为例。

4.1 按位或卷积 (OR Convolution)

目标:

ck=∑i∣j=kaibj c_k = \sum_{i \mid j = k} a_i b_j ck=ij=kaibj

变换定义(FWT):

AAA 是原序列,FWT(A)\text{FWT}(A)FWT(A) 定义为:
FWT(A)[i]=∑j⊆iA[j] \text{FWT}(A)[i] = \sum_{j \subseteq i} A[j] FWT(A)[i]=jiA[j]
即子集和。

分治实现:

将序列分为两部分:

  • A0A_0A0:最高位为0的部分
  • A1A_1A1:最高位为1的部分

则变换后:

  • FWT(A)0=FWT(A0)\text{FWT}(A)_0 = \text{FWT}(A_0)FWT(A)0=FWT(A0)
  • FWT(A)1=FWT(A0)+FWT(A1)\text{FWT}(A)_1 = \text{FWT}(A_0) + \text{FWT}(A_1)FWT(A)1=FWT(A0)+FWT(A1)
逆变换(IFWT):
  • A0=IFWT(FWT(A)0)A_0 = \text{IFWT}(\text{FWT}(A)_0)A0=IFWT(FWT(A)0)
  • A1=IFWT(FWT(A)1−FWT(A)0)A_1 = \text{IFWT}(\text{FWT}(A)_1 - \text{FWT}(A)_0)A1=IFWT(FWT(A)1FWT(A)0)
变换矩阵:

对于 n=2n=2n=2,矩阵为:
M=[1011] M = \begin{bmatrix} 1 & 0 \\ 1 & 1 \end{bmatrix} M=[1101]

4.2 按位与卷积 (AND Convolution)

目标:

ck=∑i&j=kaibj c_k = \sum_{i \& j = k} a_i b_j ck=i&j=kaibj

变换定义:

FWT(A)[i]=∑i⊆jA[j] \text{FWT}(A)[i] = \sum_{i \subseteq j} A[j] FWT(A)[i]=ijA[j]
即超集和。

分治实现:
  • FWT(A)0=FWT(A0)+FWT(A1)\text{FWT}(A)_0 = \text{FWT}(A_0) + \text{FWT}(A_1)FWT(A)0=FWT(A0)+FWT(A1)
  • FWT(A)1=FWT(A1)\text{FWT}(A)_1 = \text{FWT}(A_1)FWT(A)1=FWT(A1)
逆变换:
  • A0=IFWT(FWT(A)0−FWT(A)1)A_0 = \text{IFWT}(\text{FWT}(A)_0 - \text{FWT}(A)_1)A0=IFWT(FWT(A)0FWT(A)1)
  • A1=IFWT(FWT(A)1)A_1 = \text{IFWT}(\text{FWT}(A)_1)A1=IFWT(FWT(A)1)
变换矩阵:

M=[1101] M = \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix} M=[1011]

4.3 按位异或卷积 (XOR Convolution)

这是最复杂也最常用的一种。

目标:

ck=∑i⊕j=kaibj c_k = \sum_{i \oplus j = k} a_i b_j ck=ij=kaibj

变换定义(Walsh-Hadamard Transform):

FWT(A)[i]=∑j=0n−1(−1)popcount(i&j)A[j] \text{FWT}(A)[i] = \sum_{j=0}^{n-1} (-1)^{\text{popcount}(i \& j)} A[j] FWT(A)[i]=j=0n1(1)popcount(i&j)A[j]

分治实现:
  • FWT(A)0=FWT(A0)+FWT(A1)\text{FWT}(A)_0 = \text{FWT}(A_0) + \text{FWT}(A_1)FWT(A)0=FWT(A0)+FWT(A1)
  • FWT(A)1=FWT(A0)−FWT(A1)\text{FWT}(A)_1 = \text{FWT}(A_0) - \text{FWT}(A_1)FWT(A)1=FWT(A0)FWT(A1)
逆变换:
  • A0=IFWT(FWT(A)0+FWT(A)12)A_0 = \text{IFWT}\left(\frac{\text{FWT}(A)_0 + \text{FWT}(A)_1}{2}\right)A0=IFWT(2FWT(A)0+FWT(A)1)
  • A1=IFWT(FWT(A)0−FWT(A)12)A_1 = \text{IFWT}\left(\frac{\text{FWT}(A)_0 - \text{FWT}(A)_1}{2}\right)A1=IFWT(2FWT(A)0FWT(A)1)

注意:逆变换需要除以2。

变换矩阵:

M=[111−1] M = \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} M=[1111]


5. C++ 实现

以下是三种 FWT 的完整 C++ 实现:

#include <iostream>
#include <vector>
using namespace std;typedef long long ll;
const int MOD = 998244353; // 常用模数
const int INV2 = (MOD + 1) / 2; // 2的逆元,因为 (MOD+1) % 2 == 0class FWT {
public:// 快速幂(用于求逆元)static ll pow_mod(ll a, ll b) {ll res = 1;while (b) {if (b & 1) res = res * a % MOD;a = a * a % MOD;b >>= 1;}return res;}// 按位或卷积 FWTstatic void fwt_or(vector<ll>& a, bool inv) {int n = a.size();for (int len = 1; 2 * len <= n; len <<= 1) {for (int i = 0; i < n; i += 2 * len) {for (int j = 0; j < len; j++) {ll u = a[i + j];ll v = a[i + j + len];if (!inv) {// 正变换: a[i+j+len] += a[i+j]a[i + j + len] = (u + v) % MOD;} else {// 逆变换: a[i+j+len] -= a[i+j]a[i + j + len] = (v - u + MOD) % MOD;}}}}}// 按位与卷积 FWTstatic void fwt_and(vector<ll>& a, bool inv) {int n = a.size();for (int len = 1; 2 * len <= n; len <<= 1) {for (int i = 0; i < n; i += 2 * len) {for (int j = 0; j < len; j++) {ll u = a[i + j];ll v = a[i + j + len];if (!inv) {// 正变换: a[i+j] += a[i+j+len]a[i + j] = (u + v) % MOD;} else {// 逆变换: a[i+j] -= a[i+j+len]a[i + j] = (u - v + MOD) % MOD;}}}}}// 按位异或卷积 FWTstatic void fwt_xor(vector<ll>& a, bool inv) {int n = a.size();for (int len = 1; 2 * len <= n; len <<= 1) {for (int i = 0; i < n; i += 2 * len) {for (int j = 0; j < len; j++) {ll u = a[i + j];ll v = a[i + j + len];a[i + j] = (u + v) % MOD;a[i + j + len] = (u - v + MOD) % MOD;// 如果是逆变换,则除以2if (inv) {a[i + j] = a[i + j] * INV2 % MOD;a[i + j + len] = a[i + j + len] * INV2 % MOD;}}}}}// 三种卷积的完整接口// A[i] = sum_{j|k=i} a[j] * b[k]static vector<ll> or_convolution(vector<ll> a, vector<ll> b) {fwt_or(a, false);fwt_or(b, false);int n = a.size();for (int i = 0; i < n; i++) {a[i] = a[i] * b[i] % MOD;}fwt_or(a, true);return a;}// A[i] = sum_{j&k=i} a[j] * b[k]static vector<ll> and_convolution(vector<ll> a, vector<ll> b) {fwt_and(a, false);fwt_and(b, false);int n = a.size();for (int i = 0; i < n; i++) {a[i] = a[i] * b[i] % MOD;}fwt_and(a, true);return a;}// A[i] = sum_{j^k=i} a[j] * b[k]static vector<ll> xor_convolution(vector<ll> a, vector<ll> b) {fwt_xor(a, false);fwt_xor(b, false);int n = a.size();for (int i = 0; i < n; i++) {a[i] = a[i] * b[i] % MOD;}fwt_xor(a, true);return a;}
};// 辅助函数:扩展序列到2的幂
vector<ll> extend_to_power_of_two(const vector<ll>& a) {int n = 1;while (n < (int)a.size()) n <<= 1;vector<ll> res(n, 0);for (int i = 0; i < (int)a.size(); i++) {res[i] = a[i];}return res;
}// 测试函数
int main() {// 示例:计算异或卷积vector<ll> a = {1, 2, 3, 4};vector<ll> b = {1, 1, 1, 1};// 扩展到2的幂a = extend_to_power_of_two(a);b = extend_to_power_of_two(b);cout << "Original a: ";for (ll x : a) cout << x << " ";cout << endl;cout << "Original b: ";for (ll x : b) cout << x << " ";cout << endl;// 计算异或卷积vector<ll> c = FWT::xor_convolution(a, b);cout << "XOR Convolution result: ";for (ll x : c) cout << x << " ";cout << endl;// 验证手动计算// c[0] = a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3] = 1*1 + 2*1 + 3*1 + 4*1 = 10// c[1] = a[0]*b[1] + a[1]*b[0] + a[2]*b[3] + a[3]*b[2] = 1*1 + 2*1 + 3*1 + 4*1 = 10// c[2] = a[0]*b[2] + a[1]*b[3] + a[2]*b[0] + a[3]*b[1] = 1*1 + 2*1 + 3*1 + 4*1 = 10// c[3] = a[0]*b[3] + a[1]*b[2] + a[2]*b[1] + a[3]*b[0] = 1*1 + 2*1 + 3*1 + 4*1 = 10// 实际上所有位置都是10,因为b全为1return 0;
}

6. 算法复杂度分析

  • 时间复杂度O(nlog⁡n)O(n \log n)O(nlogn),其中 nnn 是序列长度(必须是 2m2^m2m)。
  • 空间复杂度O(n)O(n)O(n)

与暴力算法 O(n2)O(n^2)O(n2) 相比,FWT 在 nnn 较大时优势明显。


7. 应用场景

  1. 算法竞赛

    • 计算子集卷积
    • 概率DP中状态转移
    • 生成函数优化
  2. 组合数学

    • 莫比乌斯反演的快速实现
    • 子集和问题
  3. 信号处理

    • 沃尔什-阿达玛变换用于图像压缩
  4. 密码学

    • 分析布尔函数的非线性性质

8. 注意事项

  1. 序列长度:必须是 2m2^m2m,如果不是,需要补0。
  2. 模运算:通常在模意义下进行,注意逆元的计算。
  3. 精度:整数运算不会出现精度问题。
  4. 变换顺序:先变换,再逐点乘,最后逆变换。

9. 总结

FWT 是处理按位运算卷积的强大工具,其核心思想是线性变换 + 分治。通过将卷积转换为逐点乘积,实现了 O(nlog⁡n)O(n \log n)O(nlogn) 的高效计算。

掌握 FWT 需要理解:

  • 三种变换的数学定义
  • 分治实现的递推关系
  • 正逆变换的对称性
  • 模运算下的实现细节
http://www.xdnf.cn/news/1403821.html

相关文章:

  • SpringCloud Alibaba微服务--Sentinel的使用
  • 【计算机视觉】Pixel逐像素分类Mask掩码分类理解摘要
  • 2025年- H102-Lc210--3658.奇数和与偶数和的最大公约数(gcd最大公约数)--Java版
  • 【Linux基础】深入理解Linux环境下的BIOS机制
  • PostgreSQL:突破关系型数据库的边界
  • AI公司是怎样对权重和损失函数做处理的?
  • nacos3端口漂移问题
  • mysql的内置函数
  • 论《运动战》
  • 个性化导航新体验:cpolar让Dashy支持语音控制
  • Tomcat 企业级运维实战系列(四):Tomcat 企业级监控
  • 数值分析——数据误差对函数值的影响
  • nacos 2.5.1 心跳源码解析
  • 基于单片机商用电子计价秤电子秤系统Proteus仿真(含全部资料)
  • 图解LLM(AI大模型)的工作原理
  • Redis 测试:过期 key 内存释放情况
  • 深入理解shared_ptr与循环引用问题
  • node.js ---文件读写(FS模块)
  • 用【Coze】实现文案提取+创作
  • 蓓韵安禧活性叶酸独立包装日期标注
  • 加密软件哪个好用?加密软件-为数据共享提供安全保障
  • 【基础-单选】例如现在要实现一个广告弹窗,包含图片和文本等信息,使用下面那种弹窗可以实现
  • ROS 2 机器人开发$2
  • 项目管理方法论有哪些流派
  • basic_ostream
  • Linux网络基础1(三)之网络与协议栈and网络传输基本流程
  • Yolov8损失函数:回顾Yolov8-Loss
  • 6.1 Update不能写复杂的逻辑
  • HarmonyOS Router 基本使用详解:从代码示例到实战要点
  • 【随笔】【Debian】【ArchLinux】基于Debian和ArchLinux的ISO镜像和虚拟机VM的系统镜像获取安装