Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье: различия между версиями
Строка 80: | Строка 80: | ||
== Последовательная сложность алгоритма == | == Последовательная сложность алгоритма == | ||
− | + | Быстрое дискретное преобразование Фурье выполнимо за <math>O(N(n_1+\cdots+n_m))</math> действий при <math>N=n_1n_2\cdots n_m</math> (в простом случае, при <math>N=2^m</math> необходимо <math>O(N\log_2(N))</math> действий).Дискретное преобразование Фурье преобразует вектор <math>a = (a_0, \dots, a_{N-1})</math> в вектор чисел <math>b = (b_0, \dots, b_{N-1})</math>, такой, что <math>b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}</math>, где <math>\varepsilon^n=1</math> и <math>\varepsilon^k\neq 1</math> при <math>0<k<N</math>. | |
− | + | Основной шаг алгоритма состоит в сведении задачи для <math>N</math> чисел к задаче для <math>n_1=N/n_2</math> числам, где <math>n_2</math> — делитель <math>N</math>. | |
+ | Пусть мы уже умеем решать задачу для <math>N/n_2</math> чисел. Применим преобразование Фурье к векторам <math>a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}</math> для <math>i=0,1,\dots,n_2-1</math>. Покажем теперь, что за <math>O(Np)</math> действий можно решить исходную задачу. Заметим, что <math>b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})</math>. | ||
+ | Выражения в скобках нам уже известны — это <math>(i \pmod p)</math>-тое число после преобразования Фурье <math>j</math>-го вектора. Таким образом, для вычисления каждого <math>b_i</math> нужно <math>O(n_2)</math> действий, а для вычисления всех <math>b_i</math> всего <math>O(Nn_2)</math> действий, что и требовалось. | ||
− | Вычисления всех <math>\bar{a}_i</math> и <math>c_i</math> требуют <math>O(N)</math> действий, три преобразования Фурье требуют <math>O(N\log(N))</math> действий, перемножение результатов преобразований Фурье требует <math>O(N)</math> действий, вычисление всех <math>b_i</math> зная значения свертки требует <math>O(N)</math> действий. Итого для дискретного преобразования Фурье требуется <math>O(N\log(N))</math> действий для любого <math>N</math>. | + | В '''общем''' случае. Пусть <math>4N>2^k\ge2N</math>. Заметим, что тогда <math>b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j</math>. Обозначим <math>\bar{a}_i=\varepsilon^{-i^2/2}a_i</math>, <math>\bar{b}_i=\varepsilon^{i^2/2}b_i</math>, <math>c_i=\varepsilon^{(2N-2-i)^2/2}</math>. Тогда <math>\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}</math>, если положить <math>\bar{a}_i=0</math> при <math>i\ge N</math>. |
+ | |||
+ | Таким образом задача сведена к вычислению '''''свёртки''''', а это можно сделать с помощью трёх преобразований Фурье для <math>2^k</math> элементов. Выполняем прямое преобразование Фурье для <math>(\bar{a_0}, \cdots, \bar{a}_{2^k-1})</math> и <math>(c_1,\cdots,c_{2^k-1})</math>, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье. | ||
+ | |||
+ | Вычисления всех <math>\bar{a}_i</math> и <math>c_i</math> требуют <math>O(N)</math> действий, три преобразования Фурье требуют <math>O(N\log(N))</math> действий, перемножение результатов преобразований Фурье требует <math>O(N)</math> действий, вычисление всех <math>b_i</math> зная значения свертки требует <math>O(N)</math> действий. Итого для дискретного преобразования Фурье требуется '''<math>O(N\log(N))</math>''' действий для любого <math>N</math>. | ||
== Ресурс параллелизма алгоритма == | == Ресурс параллелизма алгоритма == |
Версия 13:12, 24 сентября 2016
Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел | |
Последовательный алгоритм | |
Последовательная сложность | O (n \log n) |
Объём входных данных | n действительных чисел |
Объём выходных данных | \lfloor n/2 \rfloor+1 комплексных чисел |
Параллельный алгоритм | |
Высота ярусно-параллельной формы | O (\log n) |
Ширина ярусно-параллельной формы | n |
Быстрое преобразование Фурье (БПФ, FFT) — алгоритм быстрого вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем O(N^{2}), требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность O(N\log(N)). Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:
Содержание
- 1 ЧАСТЬ. Свойства и структура алгоритмов
- 1.1 Общее описание алгоритма
- 1.2 Математическое описание алгоритма
- 1.3 Рекурсивное описание
- 1.4 Вычислительное ядро алгоритма
- 1.5 Макроструктура алгоритма
- 1.6 Схема реализации последовательного алгоритма
- 1.7 Последовательная сложность алгоритма
- 1.8 Ресурс параллелизма алгоритма
- 1.9 Входные и выходные данные алгоритма
- 1.10 Свойства алгоритма
- 2 ЧАСТЬ. Программная реализация алгоритма
- 3 Литература
1 ЧАСТЬ. Свойства и структура алгоритмов
1.1 Общее описание алгоритма
Одним из вариантов быстрого преобразования Фурье для вектора действительных чисел с размерностью равной степени двойки является алгоритм Кули-Тьюки. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет эрмитовой избыточности (Hermitian redundancy) , т.е. out[i] является сопряженным с out[n-i]. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с комплексным аналогом алгоритма.
1.2 Математическое описание алгоритма
Входные данные: вектор действительных чисел a = (a_1,a_2,...,a_n).
Выходные данные: вектор комплексных чисел b = (b_1,b_2,...,b_{\lfloor n/2 \rfloor+1}).
Замечание: В простейшем случае алгоритм Кули-Тьюки применяется к векторам размерности степени двойки, поэтому на практике вектора иной размерности часто дополнять до ближайшей степени двойки. Такой подход делает алгоритм Кули-Тьюки не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.
1.3 Рекурсивное описание
Алгоритм:
- Входной вектор a = (a_1,a_2,...,a_n) преобразуется в матрицу A размера n_1 \times n_2 , где n=n_1 \cdot n_2 и n_1 \lt n_2
A = \begin{pmatrix} a_1 & a_2 & \cdots & a_{n_1} \\ a_{n_1+1} & a_{n_1} & \cdots & a_{2n_1} \\ \vdots & \vdots & \ddots & \vdots \\ a_{(n2-1)\cdot n1+1} & a_{(n2-1)\cdot n1+1} & \cdots & a_{n2\cdot n1} \end{pmatrix}
- К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка n_1
- Каждый элемент полученный после применения БПФ умножается на поворотные множители exp (2 \pi i(m-1)(j-1)/n), где m - номер строки, а j - номер столбца
- Полученная после шагов 1-2 матрица A транспонируется
- К каждой строке матрицы A^T применяется БПФ порядка n_2
Замечание: Как правило все поворотные множители вычисляются заранее и хранятся в специальном массиве.
1.4 Вычислительное ядро алгоритма
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, "бабочка". В простейшем случае "бабочка" представляет из себя двухточечное преобразование. Рассмотрим этот случай:
На вход алгоритму подается двухэлементный вектор ‒ v = (v[0], v[1]) . Тогда для вычисления будут происходить по следующим формулам:
V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1]
V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1]
Данный процесс удобно изобразить с помощью следующей схемы:
Для 4-х элеметного вектора v=(v[0],v[1],v[2],v[3]), алгоритм строится похожим образом. Сначала создаются простейшие "бабочки", а потом их результаты соединяются с противоположеной "бабочкой":
V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])
V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])
V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])
V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])
Схема в таком случае будет выглядеть следующим образом:
Для случая, когда вход не является степенью двойки, "бабочки" будут "несимментричными", но в остальном вычисления будут проходить схожим образом.
1.5 Макроструктура алгоритма
Для исходного вектора a = (a_1,a_2,...,a_n) размерности n = n_1 \cdot n_2, n_1 \lt n_2 БПФ представляется как:
n_2 БПФ порядка n_1 \Rightarrow n_1 \cdot n_2 умножение комплексных чисел \Rightarrow n_1 БПФ порядка n_2
1.6 Схема реализации последовательного алгоритма
1.7 Последовательная сложность алгоритма
Быстрое дискретное преобразование Фурье выполнимо за O(N(n_1+\cdots+n_m)) действий при N=n_1n_2\cdots n_m (в простом случае, при N=2^m необходимо O(N\log_2(N)) действий).Дискретное преобразование Фурье преобразует вектор a = (a_0, \dots, a_{N-1}) в вектор чисел b = (b_0, \dots, b_{N-1}), такой, что b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}, где \varepsilon^n=1 и \varepsilon^k\neq 1 при 0\lt k\lt N.
Основной шаг алгоритма состоит в сведении задачи для N чисел к задаче для n_1=N/n_2 числам, где n_2 — делитель N. Пусть мы уже умеем решать задачу для N/n_2 чисел. Применим преобразование Фурье к векторам a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i} для i=0,1,\dots,n_2-1. Покажем теперь, что за O(Np) действий можно решить исходную задачу. Заметим, что b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}). Выражения в скобках нам уже известны — это (i \pmod p)-тое число после преобразования Фурье j-го вектора. Таким образом, для вычисления каждого b_i нужно O(n_2) действий, а для вычисления всех b_i всего O(Nn_2) действий, что и требовалось.
В общем случае. Пусть 4N\gt 2^k\ge2N. Заметим, что тогда b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j. Обозначим \bar{a}_i=\varepsilon^{-i^2/2}a_i, \bar{b}_i=\varepsilon^{i^2/2}b_i, c_i=\varepsilon^{(2N-2-i)^2/2}. Тогда \bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}, если положить \bar{a}_i=0 при i\ge N.
Таким образом задача сведена к вычислению свёртки, а это можно сделать с помощью трёх преобразований Фурье для 2^k элементов. Выполняем прямое преобразование Фурье для (\bar{a_0}, \cdots, \bar{a}_{2^k-1}) и (c_1,\cdots,c_{2^k-1}), перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.
Вычисления всех \bar{a}_i и c_i требуют O(N) действий, три преобразования Фурье требуют O(N\log(N)) действий, перемножение результатов преобразований Фурье требует O(N) действий, вычисление всех b_i зная значения свертки требует O(N) действий. Итого для дискретного преобразования Фурье требуется O(N\log(N)) действий для любого N.
1.8 Ресурс параллелизма алгоритма
1.9 Входные и выходные данные алгоритма
1.10 Свойства алгоритма
2 ЧАСТЬ. Программная реализация алгоритма
2.1 Масштабируемость алгоритма и его реализации
2.2 Существующие реализации алгоритма
3 Литература
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL Быстрое преобразование Фурье (дата обращения 17.09.2016)