算法学习笔记(45): 快速沃尔什变换 FWT
1.算法学习笔记(∞):杂项2.算法学习笔记(1): 欧几里得算法及其扩展3.算法学习笔记(2): 欧拉定理与逆元4.算法学习笔记(3): 倍增5.算法学习笔记(3.1): ST算法6.算法学习笔记(4): 并查集及其优化7.算法学习笔记(5): 最近公共祖先(LCA)8.算法学习笔记(6): 树链剖分9.算法学习笔记(7): 二分图10.算法学习笔记(8): 网络流11.算法学习笔记(8.0): 网络流前置知识12.算法学习笔记(8.1): 网络最大流算法 EK, Dinic, ISAP13.算法学习笔记(8.2): 上下界网络流14.算法学习笔记(8.3): 网络最大流 - 模型篇15.算法学习笔记(9): 中国剩余定理(CRT)以及其扩展(EXCRT)16.算法学习笔记(10): BSGS算法及其扩展算法17.算法学习笔记(11): 原根18.算法学习笔记(12): 线性基19.算法学习笔记(13): Manacher算法20.算法学习笔记(14): 字符串哈希21.算法学习笔记(15): Trie(字典树)22.算法学习笔记(16): 组合数学基础23.算法学习笔记(17): 快速傅里叶变换(FFT)24.算法学习笔记(18): 平衡树(一)25.算法学习笔记(19): 树上启发式合并(DSU on tree)26.算法学习笔记(20): AC自动机27.算法学习笔记(21): 平衡树(二)28.算法学习笔记(22): 逆序对与原序列29.算法学习笔记(23): 马尔可夫链中的期望问题30.算法学习笔记(24): 狄利克雷卷积和莫比乌斯反演31.算法学习笔记(25): 矩阵树定理32.算法学习笔记(26): 计算几何33.算法学习笔记(27): 后缀结构34.算法学习笔记(28): 筛法35.算法学习笔记(29):分块36.算法学习笔记(30):Kruskal 重构树37.算法学习笔记(31): 李超线段树38.算法学习笔记(32): 分治39.算法学习笔记(33): 格路径与计数40.算法学习笔记(34): 矩阵乘法与线段树标记41.算法学习笔记(35): CMD Tree42.算法学习笔记(36): 期望中的停时43.算法学习笔记(37): 点分治,边分治小记44.算法学习笔记(38): 矩阵45.算法学习笔记(39): 2-SAT46.算法学习笔记(40): 具体数学47.算法学习笔记(41): 朴素多项式算法48.算法学习笔记(42): 颜色段均摊49.算法学习笔记(43): 可持久化线段树 - 区间加!50.算法学习笔记(44): 二维问题小计
51.算法学习笔记(45): 快速沃尔什变换 FWT
52.算法学习笔记(46): 离散余弦变换(DCT)遗憾的是 math
里面一直没有很好的讲这个东西……所以这次细致说说。
FWT 的本质
类似于多项式卷积中,利用 ntt
变换使得卷积 点乘,fwt
也是类似的应用。
定义某种位运算 ,那么 fwt
处理的位运算卷积形如:
那么我们需要构造出一种变换,使得:
暂时我们还不得而知如何变换,考虑设 表示变换系数,那么有:
那么对应的点积:
根据卷积定义:
对比:
我们可以得知:
这个等式便是 fwt
的核心。
另外,考虑到位运算每一位是独立的,那么 非常重要的性质是可以按位考虑。也就是说:
其中 表示 的第 位。
那么我们只需要构造出 即可。
不妨假设我们已经构造出了 ,那么怎么求解呢?
类似 ntt
的考虑,分治:
将最高位拆出来,分别记为 :
于是分半之后:
于是可以 的合并两个规模减半的东西了,于是总复杂度 ,其中 是位数。
对于逆变换,将 求个逆,变换回去即可。
FWT 的构造
现在我们对于 尝试构造其 矩阵。
首先,注意到 ,于是 。
同理,不难得出 。
考虑 可以得出 或者 。
同理考虑 也可以知道 或者 。
注意到需要构造出的矩阵有逆,那么只能是:
值得注意的是,第二种矩阵 对应的等式为 ,也就是说:
相当于子集求和!
void fwtor(int n, int inv = {1, -1}) {
for (int u = 2, k = 1; u <= n; u <<= 1, k <<= 1)
for (int i = 0; i < n; i += u)
for (int j = 0; j < k; ++j)
fwt[i + j + k] += fwt[i + j] * inv;
}
首先还是注意到 。
考虑 得出 或者 。
同理考虑 得出 或者 。
那么还是:
值得注意的是,第一种矩阵 对应的是 ,也就是说:
相当于超集求和!
void fwtand(int n, int inv = {1, -1}) {
for (int u = 2, k = 1; u <= n; u <<= 1, k <<= 1)
for (int i = 0; i < n; i += u)
for (int j = 0; j < k; ++j)
fwt[i + j] += fwt[i + j + k] * inv;
}
考虑对于任意 存在 ,那么一定存在 ,否则 显然没有逆,不可行。
考虑 ,那么 。
,所以 ,否则 则显然没有逆。
,考虑到 ,那么自然 。
所以可行的矩阵为:
注意到对于第一个矩阵,实际上的系数为 。啥也不相当于。
void fwtxor(int n, int inv = {1, 1/2}) {
for (int u = 2, k = 1; u <= n; u <<= 1, k <<= 1)
for (int i = 0; i < n; i += u)
for (int j = 0; j < k; ++j) {
int x = fwt[i + j], y = fwt[i + j + k];
fwt[i + j] = (x + y) * inv, fwt[i + j + k] = (x - y) * inv;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?