The Walsh transform (Walsh Transform)1 is a method widely used in signal processing as an alternative to the discrete Fourier transform in spectral analysis. FFT uses double-precision values, whereas Walsh decomposes a signal into square waves of different oscillation frequencies, so all coefficients are integers with the same absolute value. This avoids floating-point multiplication and improves the computation speed.
Thus, FWT and FFT have the same core idea: transforming an array. Let the result of applying the fast Walsh transform to array A be FWT[A].
The core idea of FWT is as follows:
We need a new sequence C, obtained from sequences A and B by some operation, namely C=A⋅B.
First compute FWT[A],FWT[B] by the forward transform. Then, from FWT[C]=FWT[A]⋅FWT[B], compute O(n) in FWT[C] time, where ⋅ denotes multiplication at corresponding positions of the sequences.
Finally, apply the inverse operation to obtain the original sequence C. The time complexity is O(nlogn).
In algorithm competitions, FWT is used to solve convolutions whose indices are combined by bitwise operations.
The formula is Ci=∑i=j⊕kAjBk.
Here ⊕ is one of the binary bitwise operations.
We use ∪ (bitwise OR), ∩ (bitwise AND), and ⊕ (bitwise XOR) as examples.
FWT Operations
OR Convolution
If k=i∪j, then the positions where the binary digits of i are 1 and the positions where the binary digits of j are 1 must form a subset of the positions where the binary digits of k are 1.
To obtain FWT[C]=FWT[A]⋅FWT[B], we must construct the rule for this FWT.
By definition, we can construct FWT[A]i=Ai′=∑i=i∪jAj to represent the indices j whose positions with bit value 1 form a subset of the positions with bit value one in i.
We clearly cannot enumerate all pairs, which would take O(n2). Since global enumeration is too slow, we use divide and conquer.
Split the whole interval into two halves. After this split, the binary representation of an index follows a regular pattern.
Let A0 denote the first half of A, and A1 the second half. Then A0 has the highest bit of its largest index equal to 0, so its subsets are just subsets within itself because the highest bit is 0. In contrast, A1 has highest bit 1; its valid subsets include not only itself but also subsets whose highest bit is 0. Therefore,
FWT[A]=merge(FWT[A0],FWT[A0]+FWT[A1])
Here merge concatenates the two arrays like strings, + is ordinary addition, and it adds corresponding binary positions.
The divide-and-conquer construction completes a merge in O(logn) time. Since each merge performs one operation, it obtains O(nlogn) in FWT[A] time.
The inverse is also simple. The subsets of A0 are itself (A0=FWT[A0]), while the subsets of A1 are FWT[A0]+FWT[A1]; hence the inverse recurrence is
UFWT[A′]=merge(UFWT[A0′],UFWT[A1′]−UFWT[A0′])
Here is the implementation. The forward and inverse transforms can share one function: use type=1 for the forward transform and type=−1 for the inverse transform.
Implementation
voidOr(ll *a, ll type){ // Iterative implementation with a smaller constant factorfor (ll x = 2; x <= n; x <<= 1) {
ll k = x >> 1;
for (ll i = 0; i < n; i += x) {
for (ll j = 0; j < k; j++) {
(a[i + j + k] += a[i + j] * type) %= P;
}
}
}
}
AND Convolution
The same reasoning as for OR gives analogous conclusions.
Let us see how to compute the values of A,B quickly; again, use divide and conquer.
For the subsequence FWT[A0] whose current bit of i is 0, the ∘ operation gives the same result when combined with 0 or 1, because 0∩0=0,0∩1=0. Thus, in FWT[A]=∑i∘j=0Aj−∑i∘j=1Aj, the term ∑i∘j=1Aj=0 is zero.
For the subsequence A1 whose current bit of i is 1, the ∘ operation gives 0 when combined with 0, and 1 when combined with 1, because 1∩0=0,1∩1=1.
These four numbers suffice to complete the transform. We call this matrix the bit matrix.
For the inverse transform, we need the inverse of the bit matrix above.
If the inverse matrix is c−1, a similar argument gives the original values:
Ai=j=0∑nc−1(i,j)FWT[A]j
The inverse matrix does not always exist. For example, if there is a row 0 or a column 0, the matrix has no inverse, so the construction requires care.
Bitwise OR
We can construct the matrix
[1101]
It satisfies c(i,j)c(i,k)=c(i,j∪k). This is exactly the same as the previously derived FWT[A]=merge(FWT[A0],FWT[A0]+FWT[A1]). Similarly, the following matrix also satisfies the condition, but we generally use the first one:
[1110]
Although the following matrix also satisfies c(i,j)c(i,k)=c(i,j∪k), it contains a row 0, has no inverse, and is therefore invalid:
[0101]
For the inverse transform, invert the matrix. For the top matrix, the result is:
[1−101]
Then substitute this inverse matrix into the forward-transform procedure.
Bitwise AND
We can construct the matrix
[1011]
It satisfies c(i,j)c(i,k)=c(i,j∩k).
The inverse matrix is:
[10−11]
Bitwise XOR
We can construct the matrix
[111−1]
It satisfies c(i,j)c(i,k)=c(i,j⊕k).
The inverse matrix is:
[0.50.50.5−0.5]
FWT Is a Linear Transform
FWT is a linear transform. In other words, it satisfies:
FWT[A+B]=FWT[A]+FWT[B]
and:
FWT[c⋅A]=c⋅FWT[A]
K-Dimensional FWT
At its core, a bitwise operation acts on an n-dimensional {0,1} vector. OR takes the max in each dimension, AND takes the min, and XOR adds corresponding coordinates modulo mod2.
A key feature of bitwise operations is that the coordinates of the vector are independent.
Extend {0,1} to [0,K)∩Z, that is, to base-K, and see what happens.
Max Operation
Extend the ∪ operation to base-K. Define i∪j to take the max in each digit; then
c(i,j)c(i,k)=c(i,j∪k)
If j=k, the equation becomes:
c(i,j)c(i,j)=c(i,j)
Thus, in every row, the 1s must occur before the 0s; placing them afterward is invalid. One valid construction is:
1111011100110001
Its inverse is:
1−10001−10001−10001
Min Operation
Extend the ∩ operation to base-K. Define i∩j to take the min in each digit; then
c(i,j)c(i,k)=c(i,j∩k)
If j=k, the equation again becomes:
c(i,j)c(i,j)=c(i,j)
Thus, in every row, the 1s must occur after the 0s; placing them beforehand is invalid. One valid construction is:
1000110011101111
Its inverse is:
1000−11000−11000−11
The first two operations are used less often. The more common one is:
Carryless Addition
Extend the ⊕ operation to base-K. Define i⊕j as digitwise addition modulo modK; then
c(i,j)c(i,k)=c(i,j⊕k)
Choosing c(i,j)=ωKj satisfies the requirement:
ωKjωKk=ωKj⊕k
However, identical rows give a singular matrix, so instead choose c(i,j)=ωK(i−1)j.
If the modulus in the problem contains a suitable root of unity, the transform is straightforward to implement.
However, a root of unity may not exist modulo the given modulus. Consider an extension field: define x with xK=1, and substitute x into the computation. Every value then becomes a polynomial in x of degree k−1. We only need to compute modulo modxK−1. The matrix can therefore be written as:
This may introduce zero divisors, meaning one value has multiple representations, so its actual value is ambiguous.
Instead of reducing modxK−1, take mod modulo the cyclotomic polynomial ΦK(x). It makes x have order k and is irreducible over Q, so the above computation can be performed modulo modΦK(x).
One issue is that the constants in modΦK(x) are large because Φ itself is a polynomial. Since ΦK(x)∣xk−1, we can reduce modulo modxk−1 during the computation and reduce modulo modΦK(x) only at the end.
Examples
"CF 1103E" Radix sum
Given a sequence a1,a2,...,an of length n, for every p∈[0,n−1], find the number of integer sequences i1,i2,...,in satisfying the following conditions, modulo 258:
∀j∈[1,n],ij∈[1,n];
j=1∑naij=p, where addition is defined as decimal carryless addition.
n≤105,ai≤105
Solution. We can use DP: let fi,s denote the state after considering the i-th number, with current carryless-addition state s. Since FWT is linear, transform the sequence into its FWT point-value representation, raise it to the n-th power, and transform it back.
The idea above is routine, but the modulus is 258, which has no root of unity. We therefore use an extension field. The cyclotomic polynomial here is Φ10(x)=x4−x3+x2−x+1.
During UFWT we need to divide by 10, but {14} has no inverse modulo 10. In contrast, 258 has an inverse modulo 5, namely 258. We only need to divide by another 57646075230342349. Let the answer after division by 2 be 5, and let the true answer be x; then y. Consequently 25y≡x(mod264), so y≡25x(mod264−5). Hence the final answer can simply be divided by y≡25x(mod259). Although the reason for using modulus 25 is unclear, reducing once more gives the desired result.
The constraint is 258.
[CF103329F] [XXII Opencup, Grand Prix of XiAn] The Struggle
Given an elliptic curve E, all of whose integral-point coordinates lie in [1,4⋅106], compute ∑(x,y)∈E(x⊕y)33x−2y−1mod109+7.
Solution
This problem is not a direct application; the author provides a detailed English solution at this link.