算法学习笔记(45): 快速沃尔什变换 FWT

遗憾的是 math 里面一直没有很好的讲这个东西……所以这次细致说说。

FWT 的本质

类似于多项式卷积中,利用 ntt 变换使得卷积 点乘,fwt 也是类似的应用。

定义某种位运算 ,那么 fwt 处理的位运算卷积形如:

H=FGHk=ij=kFiGj

那么我们需要构造出一种变换,使得:

H=FGfwt(H)=fwt(F)fwt(G)

暂时我们还不得而知如何变换,考虑设 c(i,j) 表示变换系数,那么有:

fwt(F)i=c(i,j)Fj

那么对应的点积:

fwt(H)k=ijc(k,i)Fic(k,j)Gj=ic(k,i)Hi

根据卷积定义:

ic(k,i)Hi=ic(k,i)xy=iFxGy=xyFxGyc(k,xy)

对比:

xyFxGyc(k,xy)=ijc(k,i)Fic(k,j)Gj

我们可以得知:

c(k,xy)=c(k,x)c(k,y)

这个等式便是 fwt 的核心。

另外,考虑到位运算每一位是独立的,那么 c(x,y) 非常重要的性质是可以按位考虑。也就是说:

c(i,j)=c(i0,j0)c(i1,j1)

其中 ik 表示 i 的第 k 位。

那么我们只需要构造出 c(0/1,0/1) 即可。

不妨假设我们已经构造出了 c,那么怎么求解呢?

类似 ntt 的考虑,分治:

fwti=c(i,j)Fj=j=0(n/2)1c(i,j)Fj+j=n/2n1c(i,j)Fj

将最高位拆出来,分别记为 i,j

fwti=c(i0,0)j=0(n/2)1c(i,j)Fj+c(i0,1)j=n/2n1c(i,j)Fj

于是分半之后:

fwt(F)i=c(i0,0)fwt(F0)i+c(i0,1)fwt(F1)i

于是可以 O(n) 的合并两个规模减半的东西了,于是总复杂度 O(w2w),其中 w 是位数。

对于逆变换,将 c 求个逆,变换回去即可。

FWT 的构造

现在我们对于 or, and, xor 尝试构造其 c 矩阵。

or

首先,注意到 c(0,0)c(0,0)=c(0,0|0),于是 c(0,0)=0/1

同理,不难得出 c(0/1,0/1){0,1}

考虑 c(0,0)c(0,1)=c(0,1) 可以得出 c(0,0)=c(0,1)=1 或者 c(0,1)=0

同理考虑 c(1,0)c(1,1)=c(1,1) 也可以知道 c(1,1)=0 或者 c(1,0)=c(1,1)=1

注意到需要构造出的矩阵有逆,那么只能是:

[1110]或者[1011]

值得注意的是,第二种矩阵 c(i,j) 对应的等式为 [i&j=j],也就是说:

fwti=i&j=jFj=jiFj

相当于子集求和!

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;
}

and

首先还是注意到 c(0/1,0/1){0,1}

考虑 c(0,0)c(0,1)=c(0,0) 得出 c(0,0)=0 或者 c(0,0)=c(0,1)=1

同理考虑 c(1,0)c(1,1)=c(1,0) 得出 c(1,0)=0 或者 c(1,0)=c(1,1)=1

那么还是:

[1101]或者[0111]

值得注意的是,第一种矩阵 c(i,j) 对应的是 [i&j=i],也就是说:

fwti=i&j=jFj=ijFj

相当于超集求和!

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;
}

xor

考虑对于任意 x,y{0,1} 存在 c(0,0)c(x,y)=c(x,y),那么一定存在 c(0,0)=1,否则 c(1,1)=c(1,0)=0 显然没有逆,不可行。

考虑 c(0,1)c(0,1)=c(0,0),那么 c(0,1)=±1

c(1,0)c(1,0)=c(1,1)c(1,1)=c(1,0),所以 c(1,0)=1,否则 c(1,1)=c(1,0)=0 则显然没有逆。

c(1,1)c(1,1)=c(1,0),考虑到 c(1,0)=1,那么自然 c(1,1)=±1

所以可行的矩阵为:

[1111]或者[1111]

注意到对于第一个矩阵,实际上的系数为 (1)|i&j|。啥也不相当于。

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;
			}
}
posted @   jeefy  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示