Дискретное преобразование Фурье

Давайте научимся быстро умножать многочлены.

Пусть у нас есть два многочлена A(x)A(x) и B(x)B(x) степени nn.

A(x)=k=0nakxk=anxn+an1xn1++a2x2+a1x+a0B(x)=k=0nbkxk=bnxn+bn1xn1++b2x2+b1x+b0\align{ A(x) &= \sum\limits_{k=0}^n a_k \cdot x^k = a_{n} \cdot x^n + a_{n-1} \cdot x^{n-1} + \dotsb + a_2 \cdot x^2 + a_1 \cdot x + a_0 \\ B(x) &= \sum\limits_{k=0}^n b_k \cdot x^k = b_{n} \cdot x^n + b_{n-1} \cdot x^{n-1} + \dotsb + b_2 \cdot x^2 + b_1 \cdot x + b_0 }

В школе изучался неэффективный метод умножения «в столбик»

A(x)B(x)=k=0nxki+j=kaibjA(x) \cdot B(x) = \sum\limits_{k=0}^n x^k \sum\limits_{i+j=k} a_i b_j

Этот метод требует (n+1)(n+2)/2=Θ(n2)(n+1)(n+2)/2 = \Theta(n^2) умножений.

Каждый многочлен P(x)=k=0nakxkP(x) = \sum\limits_{k=0}^n a_k \cdot x^k степени nn может быть представлен списком своих n+1n+1 коэффициентов a0,a1,,ana_0, a_1, \dotsc, a_n, а может быть представлен списком значений в n+1n+1 точках (x0,P(x0)),(x1,P(x1)),,(xn,P(xn))\bigl( x_0, P(x_0) \bigr), \bigl( x_1, P(x_1) \bigr), \dotsc, \bigl( x_n, P(x_n) \bigr).

Если два многочлена представлены значениями, а не коэффициентами, то их произведение считается быстро. Пусть многочлены A(x)A(x) и B(x)B(x) имеют в точках x0,x1,,x2n+1x_0, x_1, \dotsc, x_{2n+1} значения A0,A1,,A2n+1A_0, A_1, \dotsc, A_{2n+1} и B0,B1,,B2n+1B_0, B_1, \dotsc, B_{2n+1}. Тогда многочлен A(x)B(x)A(x) \cdot B(x) имеет в этих точках значения A0B0,A1B1,,A2n+1B2n+1A_0 B_0, A_1 B_1, \dotsc, A_{2n+1} B_{2n+1}.

Такой способ вычисления требует 2n+1=O(n)2n+1 = O(n) умножений. Получаем следующий код

input const int n input array[int] a[n+1] input array[int] b[n+1] array[int] x[2*n+1] = [???, ???, ..., ???] array[int] A[2*n+1] = evaluate(a, x) array[int] B[2*n+1] = evaluate(b, x) array[int] C[2*n+1] for int i = 0; i < 2*n+1; i++: C[i] = A[i] * B[i] array[int] c[2*n+1] = interpolate(x, C)

Нам осталось придумать эффективную реализацию операций evaluate(p, x) — вычисление значений многочлена, заданного коэффициентами p, в точках x и interpolate(x, P) — восстановление коэффициентов многочлена по его значениям P в точках x.

Стандартными методами можно реализовать операцию evaluate(p, x) за n+1n+1 умножений с помощью схемы Горнера и операцию interpolate(x, P) за O(n3)O(n^3) методами обычной интерполяции. Можно, конечно, попробовать оптимизировать интерполяцию до O(n2)O(n^2), но это тоже не подходит, потому что школьный метод всё еще эффективнее.

Мы вольны выбирать узлы xjx_j, и эта способность является ключом к построению эффективного алгоритма.

Дискретное преобразование Фурье

Выберем число Nn+1N \ge n+1, чтобы иметь запас на будущее (мы из двух многочленов степени nn хотим сделать многочлен степени 2n2n) и чтобы было проще считать.

Для того, чтобы все операции делать эффективно, можно выбрать узлы xjx_j следующим образом

xj=e2πij/N=wjx_j = e^{2 \pi i j / N} = w^j

где w=e2π/Nw = e^{2 \pi / N} — первый комплексный корень из единицы.

Дискретное преобразование Фурье многочлена P(x)P(x), заданного коэффициентами P(x)=j=0N1pjxjP(x) = \sum\limits_{j=0}^{N-1} p_j \cdot x^j, называется вычисление его значений в NN точках w0,w1,w2,,wN1w^0, w^1, w^2, \dotsc, w^{N-1}. При этом список коэффициентов a0,a1,a2,,ana_0, a_1, a_2, \dotsc, a_n дополнен нулями до длины nn.

Можно сформулировать задачу поиска дискретного преобразования Фурье в матричных терминах

( 111111 1ww2w3wN2wN1 1w2w4w6w2(N2)w2(N1) 1w3w6w9w3(N2)w3(N1)  1w(N2)w(N2)3w(N2)4w(N2)(N2)w(N2)(N1) 1w(N1)w(N1)3w(N1)4w(N1)(N2)w(N1)(N1))(p0p1p2p3pN2pN1)=(P0P1P2P3PN2PN1)\pmatrix{ ~ 1 & 1 & 1 & 1 & \cdots & 1 & 1\\[0.4em]~ 1 & w & w^2 & w^3 & \cdots & w^{N-2} & w^{N-1}\\[0.4em]~ 1 & w^2 & w^4 & w^6 & \cdots & w^{2 (N-2)} & w^{2 (N-1)}\\[0.4em]~ 1 & w^3 & w^6 & w^9 & \cdots & w^{3 (N-2)} & w^{3 (N-1)} \\ ~ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots\\[0.4em]~ 1 & w^{(N-2)} & w^{(N-2) \cdot 3} & w^{(N-2) \cdot 4} & \cdots & w^{(N-2) \cdot (N-2)} & w^{(N-2) \cdot (N-1)}\\[0.4em]~ 1 & w^{(N-1)} & w^{(N-1) \cdot 3} & w^{(N-1) \cdot 4} & \cdots & w^{(N-1) \cdot (N-2)} & w^{(N-1) \cdot (N-1)} } \cdot \pmatrix{p_0 \\ p_1 \\ p_2 \\ p_3 \\ \vdots \\ p_{N-2} \\ p_{N-1}} = \pmatrix{P_0 \\ P_1 \\ P_2 \\ P_3 \\ \vdots \\ P_{N-2} \\ P_{N-1}}

Алгоритм Кули-Тьюки

Алгоритм Кули-Тьюки находит дискретного преобразования Фурье многочлена P(x)=j=0npjxjP(x) = \sum\limits_{j=0}^{n} p_j \cdot x^j.

Перед всеми манипуляциями нам надо дополнить массив коэффициентов pjp_j нулями так, чтобы его длина равнялась степени двойки. То есть, если исходная степень многочлена P(x)P(x) равнялась nn, то после дополнения его степень будет N=2log2nN = 2^{\lceil \log_2 n \rceil}, а коэффициенты pj=0p_j = 0 при j>nj > n. Исходный многочлен теперь выглядит так

P(x)=j=0N1pjxjP(x) = \sum\limits_{j=0}^{N-1} p_j \cdot x^j

Представим многочлен в виде P(x)=S(x2)+xT(x2)P(x) = S(x^2) + x \cdot T(x^2), где S(x)S(x) состоит из коэффициентов при чётных степенях P(x)P(x), а T(x)T(x) состоит из коэффициентов при нечётных степенях P(x)P(x).

S(x)=j=0N/21p2jxjT(x)=j=0N/21p2j+1xjS(x) = \sum\limits_{j=0}^{N/2-1} p_{2j} \cdot x^j \qquad T(x) = \sum\limits_{j=0}^{N/2-1} p_{2j+1} \cdot x^j

При N=2kN = 2k можно использовать равенство

w2j=w2jmod2k=w2(jmodk)w^{2j} = w^{2j \bmod 2k} = w^{2 \cdot (j \bmod k)}

С помощью этого можно разделить задачу вычисления дискретного преобразования Фурье P(x)P(x) к двум вдвое меньшим задачам

P(wj)=S(w2j)+wjT(w2j)=S((w2)jmodk)+wjT((w2)jmodk)P(w^j) = S(w^{2j}) + w^j \cdot T(w^{2j}) = S \big( (w^2)^{j \bmod k} \big) + w^j \cdot T \big( (w^2)^{j \bmod k} \big)
function fft(array[int] p[N], complex w) -> array[N]: if N == 1: return P const int k = N / 2 array[int] s[k], t[k] for int i = 0; i < N; i++: if i % 2 == 0: s[i/2] = p[i] else: t[i/2] = p[i] S = fft(s, w * w) T = fft(t, w * w) array[int] result[N] complex wt = 1 for int i = 0; i < N; i++: result[i] = S[i % k] + wt * T[i % k] wt *= w return result

Другие основания

Что нам нужно было от wjw^j? Только то, что все они являются корнями из единицы, то есть образуют циклическую группу порядка nn. Мы выбрали первый попавшийся объект — дискретную подгруппу группы C/R\CC / \RR, имеющую порядок nn.