该解决方案使用了两个技巧。
第一个技巧是计算n + 1个前缀和σ [0],…,σ [n](正如所指出的),但是使用XOR而不是+(例如,σ [2] = a [0] XOR a [1] XOR a [2])。从i到j的子列表的XOR和等于σ [i-1] XOR σ [j]。如果我们将i-1和j循环遍历所有可能的值0,…,n而不考虑约束条件i ≤ j,则每个子列表都会有两次(一次“向前”,一次“向后”),并且还会有n + 1个额外的零,即当i-1 = j时。
第二个技巧是快速沃尔什-哈达玛变换可以在O(n log n)时间内解决以下问题:给定列表X和列表Y,我们希望找到(x,y)的频率计数,其中(x,y)∈ X × Y。 (对于此问题,X = Y,但如果使用单独的变量,则此技巧的结构更清晰。)为什么我们应该怀疑首先存在快速算法?除了极限之外,如果是x + y而不是x XOR y,那么我们将寻求快速乘多项式。
让我们用其频率向量f替换列表X,并用其频率向量g替换列表Y。例如,X = [0, 0, 0, 2, 2, 3]变为f = [3, 0, 2, 1]。假设f和g有四个元素,则期望的结果是
[f[0] g[0] + f[1] g[1] + f[2] g[2] + f[3] g[3]
,f[0] g[1] + f[1] g[0] + f[2] g[3] + f[3] g[2]
,f[0] g[2] + f[1] g[3] + f[2] g[0] + f[3] g[1]
,f[0] g[3] + f[1] g[2] + f[2] g[1] + f[3] g[0]
].
这是一个称为对称双线性形式的代数对象示例,意味着存在某个基变换矩阵B使得所需结果为B⁻¹ (B f * B g),其中*表示逐元素乘法。(剧透:B是Walsh矩阵。)
为了对快速沃尔什-哈达玛变换产生直观感受,它可以高效地计算出每个向量v的B v,让我展示一下当我将结果的前两个元素相加时会发生什么:
f[0] g[0] + f[1] g[1] + f[2] g[2] + f[3] g[3]
+ f[0] g[1] + f[1] g[0] + f[2] g[3] + f[3] g[2]
= f[0] (g[0] + g[1]) + f[1] (g[1] + g[0]) + f[2] (g[2] + g[3]) + f[3] (g[3] + g[2])
= (f[0] + f[1]) (g[0] + g[1]) + (f[2] + f[3]) (g[2] + g[3])
并添加后两个元素:
f[0] g[2] + f[1] g[3] + f[2] g[0] + f[3] g[1]
+ f[0] g[3] + f[1] g[2] + f[2] g[1] + f[3] g[0]
= f[0] (g[2] + g[3]) + f[1] (g[3] + g[2]) + f[2] (g[0] + g[1]) + f[3] (g[1] + g[0])
= (f[0] + f[1]) (g[2] + g[3]) + (f[2] + f[3]) (g[0] + g[1])
并减去前两个元素:
f[0] g[0] + f[1] g[1] + f[2] g[2] + f[3] g[3]
− (f[0] g[1] + f[1] g[0] + f[2] g[3] + f[3] g[2])
= f[0] (g[0] − g[1]) + f[1] (g[1] − g[0]) + f[2] (g[2] − g[3]) + f[3] (g[3] − g[2])
= (f[0] − f[1]) (g[0] − g[1]) + (f[2] − f[3]) (g[2] − g[3])
并减去第二个和第三个元素:
f[0] g[2] + f[1] g[3] + f[2] g[0] + f[3] g[1]
− (f[0] g[3] + f[1] g[2] + f[2] g[1] + f[3] g[0])
= f[0] (g[2] − g[3]) + f[1] (g[3] − g[2]) + f[2] (g[0] − g[1]) + f[3] (g[1] − g[0])
= (f[0] − f[1]) (g[2] − g[3]) + (f[2] − f[3]) (g[0] − g[1]) .
如果我们令
f′ = [f[0] + f[1], f[2] + f[3]]
和
g′ = [g[0] + g[1], g[2] + g[3]]
,那么前两个量是
[f′[0] g′[0] + g′[1] g′[1], f′[0] g′[1] + f′[1] g′[0]]
,这与原问题相同但规模减半。第二个两个量也是如此,最后我们可以恢复原问题。
x = f[0] g[0] + f[1] g[1] + f[2] g[2] + f[3] g[3]
y = f[0] g[1] + f[1] g[0] + f[2] g[3] + f[3] g[2]
通过 x + y
和 x - y
得到 x = ((x + y) + (x - y))/2
和 y = ((x + y) - (x - y))/2
(对于所有其他成对的也同理)。请注意,Kache
的代码将除法推迟到最后,以便可以重用相同的转换。