跳转至

快速沃尔什变换

(本文转载自桃酱的算法笔记,原文戳链接,已获得作者授权)

简介

沃尔什转换(Walsh Transform)是在频谱分析上作为离散傅立叶变换的替代方案的一种方法。——维基百科

其实这个变换在信号处理中应用很广泛,fft 是 double 类型的,但是 walsh 把信号在不同震荡频率方波下拆解,因此所有的系数都是绝对值大小相同的整数,这使得不需要作浮点数的乘法运算,提高了运算速度。

所以,FWT 和 FFT 的核心思想应该是相同的。都是对数组的变换。我们设数组 A经过快速沃尔什变换之后记作 FWT[A].

那么 FWT 核心思想就是:

我们需要一个新序列 C,由序列 A和序列 B经过某运算规则得到,即 C = A \cdot B

我们先正向得到 FWT[A], FWT[B]

然后根据 FWT[C]=FWT[A] \cdot FWT[B]

O(n)求出 FWT[C]

然后逆向想运算得到原序列 C。时间复杂度为 O(nlogn)

在算法竞赛中,FWT 是用于解决对下标进行位运算卷积问题的方法。

公式: C[i] = \sum_{i=j \bigoplus k}A[j] * B[k]

(其中 \bigoplus是二元位运算中的某一种, *是普通乘法)

FWT 的运算

FWT 之与( \And)运算和或( |)运算

与运算和或运算的本质是差不多的,所以这里讲一下或运算,与运算也是可以自己根据公式 yy 出来的。

或运算 A_i

如果有 k=i|j,那么 i的二进制位为 1的位置和 j的二进制位为 1的位置肯定是 k的二进制位为 1的位置的子集。

现在要得到 FWT[C] = FWT[A] * FWT[B],我们就要构造这个 fwt 的规则。

我们按照定义,显然可以构造 FWT[A] = A' = \sum_{i=i|j}A[j],来表示 j满足二进制中 1i的子集。

那么显然会有 C[i] = \sum_{i=j|k}A[j]*B[k] \Rightarrow FWT[C] = FWT[A] * FWT[B]

那么我们接下来看 FWT[A]怎么求。

首先肯定不能枚举了,复杂度为 O(n^2)。既然不能整体枚举,我们就考虑分治。

我们把整个区间二分,其实二分区间之后,下标写成二进制形式是有规律可循的。

我们令 A_0表示 A的前一半, A_1表示区间的后一半,那么 A_0就是 A 下标最大值的最高位为 0,他的子集就是他本身的子集(因为最高位为 0了),但是 A_1的最高位是 1,他满足条件的子集不仅仅是他本身,还包最高位为 0的子集,即

FWT[A] = merge(FWT[A_0], FWT[A_0] + FWT[A_1])

其中 merge 表示像字符串拼接一样把它们拼起来, +就是普通加法,表示对应二进制位相加。

这样我们就通过二分能在 O(logn)完成拼接,每次拼接的时候要完成一次运算,也就是说在 O(nlogn)的时间复杂度得到了 FWT[A]

接下来就是反演了,其实反演是很简单的,既然知道了 A_0的本身的子集是他自己 ( A_0 = FAT[A_0]), A_1的子集是 FAT[A_0] + FAT[A_1](A_1'= A_0' + A_1'),那就很简单的得出反演的递推式了:

UFWT[A'] = merge(UFWT[A_0'], UFWT[A_1'] - UFWT[A_0'])

与运算

与运算类比或运算可以得到类似结论

FWT[A] = merge(FWT[A_0] + FWT[A_1], FWT[A_1])
UFWT[A'] = merge(UFWT[A_0'] - UFWT[A_1'], UFWT[A_1'])

异或运算

最常考的异或运算。

异或的卷积是基于如下原理:

若我们令 i\And j1数量的奇偶性为 ij的奇偶性,那么 ik的奇偶性异或 jk的奇偶性等于 i \operatorname{xor} jk的奇偶性。

对于 FWT[A]的运算其实也很好得到。

公式如下:

A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]( C_1表示 i \And j奇偶性为 0C_2表示 i \And j的奇偶性为 1)

结论:

FWT[A] = merge(FWT[A_0] + FWT[A_1], FWT[A_0] - FWT[A_1])
UFWT[A'] - merge(\frac{FWT[A_0'] + FWT[A_1']}{2}, \frac{FWT[A_0'] - FWT[A_1']}{2})

同或运算

类比异或运算给出公式:

A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]( C_1表示 i|j奇偶性为 0C_2表示 i|j的奇偶性为 1)

FWT[A] = merge(FWT[A_1] - FWT[A_0], FWT[A_1] + FWT[A_0])
UFWT[A'] = merge(\frac{FWT[A_1'] - FWT[A_0']}{2}, \frac{FWT[A_1'] + FWT[A_0']}{2})

评论