Proofs: Multidimensional FFT
These are the human proofs of Multidimensional FFT, at full length.
MULTIDIM-FFT computes the d-dimensional DFT with N lg N additions and (N/2) lg N multiplications full
This proof shows: correctness, complexity.
Statement. Let be a commutative ring, , and powers of two with product . For each axis let satisfy and, when , (over with both hold). For every array stored in row-major order, MULTIDIM-FFT ends with the array
in the same layout. Lines L5 to L8 do exactly additions and subtractions and multiplications in , the tables of line L4 do at most more, and the extra space is with .
Lemma 1 (the lines partition the array). Fix an axis and write . Every position with has a unique expression with , and : this is division of by with remainder, followed by division of the remainder by . So the lines for the starts of line L5 are disjoint and cover all positions. In row-major order the position holds the entry with indices where ; the mixed-radix digits of are exactly these indices, so on one line the indices with are fixed (they are the digits of and ) and runs over .
Lemma 2 (one line). Let and with and when . The call FFT of line L7, with , replaces by , with additions and subtractions and multiplications. Proof. By induction on . For there is nothing to do and . For write and for the DFTs, with root , of the even and odd entries; satisfies the hypothesis for , since when . Splitting the sum by the parity of gives for , and since and have period and , also . The iterative radix-2 code computes exactly these butterflies after the bit-reversal permutation, stage by stage; this is the standard decimation-in-time FFT, proved in full on the page of the iterative FFT. Each of the stages does butterflies of one multiplication, one addition and one subtraction, which gives the counts.
Invariant. Write for the array after the passes of the loop at line L2. We prove by induction on that
Base. For the empty sum and product leave , which is the array before the loop.
Step. Suppose (1) holds for . In pass , line L5 visits each start once, line L6 copies the line into the buffer, line L7 transforms it, and line L8 writes it back to the same positions. By Lemma 1 the lines are disjoint, so the write of one line never changes an entry read by another line in the same pass, and every entry belongs to exactly one line. Fix the indices ; on that line the entry with is at . By Lemma 2 the entry written back at is
using (1) for . Renaming as and using that finite sums in a commutative ring can be reordered, this is the right side of (1) for .
End. For , (1) is the definition of , which proves correctness.
Operation count. In pass there are lines by Lemma 1, each costing additions and multiplications by Lemma 2, so the pass costs additions and multiplications. Since , we have , and summing over gives additions and multiplications. Line L4 builds table entries; taken as successive powers, each costs at most one multiplication, so the tables add at most multiplications. (The Python code instead evaluates each entry directly, by modular powering or from its exact angle over , which is more accurate in floating point and does not change the asymptotic cost.) Lines L6 and L8 move entries per pass and do no ring operations.
Space. Apart from the array, the pass for axis holds the buffer of entries, the table of entries and indices, and both are released before the next pass. The extra space is therefore at most entries, and at least for the buffer of the longest axis, so it is .