<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ivanov.kir.m</id>
	<title>Алговики - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ivanov.kir.m"/>
	<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/ru/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Ivanov.kir.m"/>
	<updated>2026-10-22T21:26:00Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=18929</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=18929"/>
		<updated>2016-11-22T11:30:07Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел — &#039;&#039;сигнала&#039;&#039; — в вектор/матрицу комплексных чисел — &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличие от своего классического аналога, БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей так, чтобы, применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора — принцип «разделяй и властвуй».&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое преобразование Фурье вектора действительных чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран алгоритм Кули-Тьюки в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай — алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм реализует принцип «разделяй и властвуй», глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители, не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицей) до составного числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. Итого для дискретного преобразования Фурье требуется &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий для любого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Тестирование проводилось на вычислительном комплексе IBM Blue Gene/P&lt;br /&gt;
&lt;br /&gt;
=== Описание платформы IBM Blue Gene/P ===&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 8192 процессорных ядер (2 x 1024 четырехъядерных вычислительных узлов), с пиковой производительностью 27,9 терафлопс (27,8528 триллионов операций с плавающей точкой в секунду).&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* выделенные коммуникационные сети для межпроцессорных обменов и глобальных операций&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W (см. список Green500)&lt;br /&gt;
* система воздушного охлаждения&lt;br /&gt;
&lt;br /&gt;
Стойка (rack, cabinet) состоит из двух midplane&#039;ов. В midplane входит 16 node-карт (compute node card), на каждой из которых установлено 32 вычислительных узла (compute card). Midplane, 8 x 8 x 8 = 512 вычислительных узлов, — минимальный раздел, на котором становится доступна топология трехмерного тора; для разделов меньших размеров используется топология трехмерной решетки. Node-карта может содержать до двух узлов ввода-вывода (I/O card). Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Микропроцессорное ядро:&lt;br /&gt;
&lt;br /&gt;
* модель: PowerPC 450&lt;br /&gt;
* рабочая частота: 850 MHz&lt;br /&gt;
* адресация: 32-битная&lt;br /&gt;
* кэш инструкций 1-го уровня (L1 instruction): 32 KB&lt;br /&gt;
* кэш данных 1-го уровня (L1 data): 32 KB&lt;br /&gt;
* кэш предвыборки (L2 prefetch): 14 потоков предварительной выборки (stream prefetching): 14 x 256 байтов&lt;br /&gt;
* два блока 64-битной арифметики с плавающей точкой (Floating Point Unit, FPU), каждый из которых может выдавать за один такт результат совмещенной операции умножения-сложения (Fused Multiply-Add, FMA)&lt;br /&gt;
* пиковая производительность: 2 FPU x 2 FMA x 850 MHz = 3,4 GFlop/sec per core&lt;br /&gt;
&lt;br /&gt;
Вычислительные узлы и I/O-карты в аппаратном смысле неразличимы и являются взаимозаменяемыми, разница между ними состоит лишь в способе их использования. У них нет локальной файловой системы, поэтому все операции ввода-вывода перенаправляются внешним устройствам.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 cores x 3,4 GFlop/sec per core = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
=== Оценка масштабируемости алгоритма ===&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=18251</id>
		<title>Обсуждение участника:Ivanov.kir.m</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=18251"/>
		<updated>2016-11-18T19:07:00Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Замечания к статье Быстрое дискретное преобразование Фурье */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановке первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Указали в шапке статьи&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено&lt;br /&gt;
* в  п.п. 2.4 следует добавить описание компьютерной платформы--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 13:35, 16 ноября 2016 (MSK)&lt;br /&gt;
** Добавили&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье Быстрое дискретное преобразование Фурье ==&lt;br /&gt;
&lt;br /&gt;
* В разделе &amp;quot;Общее описание алгоритма&amp;quot; следует вставить именно описание алгоритма. Потому что смысл алгоритма проясняется только в следующем разделе, а желательно иметь его общее (с минимумом формул) описание во введении [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Стоит добавить слова о том, что происходит, когда размер массива не представим в виде произведения - можно ли тогда применять алгоритм, и если да, то какие изменения следует сделать [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не вполне понятна логика использования курсива и полужирного начертания [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не хватает данных о глубине рекурсии при использовании алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
&lt;br /&gt;
* Все хорошо, осталось только написать недостающие разделы по программной реализации алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 12:30, 18 ноября 2016 (MSK)&lt;br /&gt;
** Здравствуйте! Какие именно Вы имеете ввиду пункты?&lt;br /&gt;
** Согласно постановке первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17661</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17661"/>
		<updated>2016-11-16T12:41:04Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое преобразование Фурье вектора действительных чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран алгоритм Кули-Тьюки в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм реализует принцип &amp;quot;разделяй и властвуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицией) до непростого числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. Итого для дискретного преобразования Фурье требуется &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий для любого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Тестирование проводилось на вычислительном комплексе IBM Blue Gene/P&lt;br /&gt;
&lt;br /&gt;
=== Описание платформы IBM Blue Gene/P ===&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 8192 процессорных ядер (2 x 1024 четырехъядерных вычислительных узлов), с пиковой производительностью 27,9 терафлопс (27,8528 триллионов операций с плавающей точкой в секунду).&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* выделенные коммуникационные сети для межпроцессорных обменов и глобальных операций&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W (см. список Green500)&lt;br /&gt;
* система воздушного охлаждения&lt;br /&gt;
&lt;br /&gt;
Стойка (rack, cabinet) состоит из двух midplane&#039;ов. В midplane входит 16 node-карт (compute node card), на каждой из которых установлено 32 вычислительных узла (compute card). Midplane, 8 x 8 x 8 = 512 вычислительных узлов, — минимальный раздел, на котором становится доступна топология трехмерного тора; для разделов меньших размеров используется топология трехмерной решетки. Node-карта может содержать до двух узлов ввода-вывода (I/O card). Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Микропроцессорное ядро:&lt;br /&gt;
&lt;br /&gt;
* модель: PowerPC 450&lt;br /&gt;
* рабочая частота: 850 MHz&lt;br /&gt;
* адресация: 32-битная&lt;br /&gt;
* кэш инструкций 1-го уровня (L1 instruction): 32 KB&lt;br /&gt;
* кэш данных 1-го уровня (L1 data): 32 KB&lt;br /&gt;
* кэш предвыборки (L2 prefetch): 14 потоков предварительной выборки (stream prefetching): 14 x 256 байтов&lt;br /&gt;
* два блока 64-битной арифметики с плавающей точкой (Floating Point Unit, FPU), каждый из которых может выдавать за один такт результат совмещенной операции умножения-сложения (Fused Multiply-Add, FMA)&lt;br /&gt;
* пиковая производительность: 2 FPU x 2 FMA x 850 MHz = 3,4 GFlop/sec per core&lt;br /&gt;
&lt;br /&gt;
Вычислительные узлы и I/O-карты в аппаратном смысле неразличимы и являются взаимозаменяемыми, разница между ними состоит лишь в способе их использования. У них нет локальной файловой системы, поэтому все операции ввода-вывода перенаправляются внешним устройствам.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 cores x 3,4 GFlop/sec per core = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
=== Оценка масштабируемости алгоритма ===&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=17660</id>
		<title>Обсуждение участника:Ivanov.kir.m</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=17660"/>
		<updated>2016-11-16T12:39:42Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Замечания по тексту */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановке первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Указали в шапке статьи&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено&lt;br /&gt;
* в  п.п. 2.4 следует добавить описание компьютерной платформы--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 13:35, 16 ноября 2016 (MSK)&lt;br /&gt;
** Добавили&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье Быстрое дискретное преобразование Фурье ==&lt;br /&gt;
&lt;br /&gt;
* В разделе &amp;quot;Общее описание алгоритма&amp;quot; следует вставить именно описание алгоритма. Потому что смысл алгоритма проясняется только в следующем разделе, а желательно иметь его общее (с минимумом формул) описание во введении [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Стоит добавить слова о том, что происходит, когда размер массива не представим в виде произведения - можно ли тогда применять алгоритм, и если да, то какие изменения следует сделать [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не вполне понятна логика использования курсива и полужирного начертания [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не хватает данных о глубине рекурсии при использовании алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17657</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17657"/>
		<updated>2016-11-16T12:33:50Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Описание платформы IBM Blue Gene/P */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое преобразование Фурье вектора действительных чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран алгоритм Кули-Тьюки в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм реализует принцип &amp;quot;разделяй и властвуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицией) до непростого числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. Итого для дискретного преобразования Фурье требуется &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий для любого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Тестирование проводилось на вычислительном комплексе IBM Blue Gene/P&lt;br /&gt;
&lt;br /&gt;
=== Описание платформы IBM Blue Gene/P ===&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 8192 процессорных ядер (2 x 1024 четырехъядерных вычислительных узлов), с пиковой производительностью 27,9 терафлопс (27,8528 триллионов операций с плавающей точкой в секунду).&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* выделенные коммуникационные сети для межпроцессорных обменов и глобальных операций&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W (см. список Green500)&lt;br /&gt;
* система воздушного охлаждения&lt;br /&gt;
&lt;br /&gt;
Стойка (rack, cabinet) состоит из двух midplane&#039;ов. В midplane входит 16 node-карт (compute node card), на каждой из которых установлено 32 вычислительных узла (compute card). Midplane, 8 x 8 x 8 = 512 вычислительных узлов, — минимальный раздел, на котором становится доступна топология трехмерного тора; для разделов меньших размеров используется топология трехмерной решетки. Node-карта может содержать до двух узлов ввода-вывода (I/O card). Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Микропроцессорное ядро:&lt;br /&gt;
&lt;br /&gt;
* модель: PowerPC 450&lt;br /&gt;
* рабочая частота: 850 MHz&lt;br /&gt;
* адресация: 32-битная&lt;br /&gt;
* кэш инструкций 1-го уровня (L1 instruction): 32 KB&lt;br /&gt;
* кэш данных 1-го уровня (L1 data): 32 KB&lt;br /&gt;
* кэш предвыборки (L2 prefetch): 14 потоков предварительной выборки (stream prefetching): 14 x 256 байтов&lt;br /&gt;
* два блока 64-битной арифметики с плавающей точкой (Floating Point Unit, FPU), каждый из которых может выдавать за один такт результат совмещенной операции умножения-сложения (Fused Multiply-Add, FMA)&lt;br /&gt;
* пиковая производительность: 2 FPU x 2 FMA x 850 MHz = 3,4 GFlop/sec per core&lt;br /&gt;
&lt;br /&gt;
Вычислительные узлы и I/O-карты в аппаратном смысле неразличимы и являются взаимозаменяемыми, разница между ними состоит лишь в способе их использования. У них нет локальной файловой системы, поэтому все операции ввода-вывода перенаправляются внешним устройствам.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 cores x 3,4 GFlop/sec per core = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
=== Оценка масштабируемости алгоритма ===&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17653</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17653"/>
		<updated>2016-11-16T12:30:10Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое преобразование Фурье вектора действительных чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран алгоритм Кули-Тьюки в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм реализует принцип &amp;quot;разделяй и властвуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицией) до непростого числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. Итого для дискретного преобразования Фурье требуется &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий для любого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Тестирование проводилось на вычислительном комплексе IBM Blue Gene/P&lt;br /&gt;
&lt;br /&gt;
=== Описание платформы IBM Blue Gene/P ===&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 8192 процессорных ядер (2 x 1024 четырехъядерных вычислительных узлов), с пиковой производительностью 27,9 терафлопс (27,8528 триллионов операций с плавающей точкой в секунду).&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* выделенные коммуникационные сети для межпроцессорных обменов и глобальных операций&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W (см. список Green500)&lt;br /&gt;
* система воздушного охлаждения&lt;br /&gt;
&lt;br /&gt;
Стойка (rack, cabinet) состоит из двух midplane&#039;ов. В midplane входит 16 node-карт (compute node card), на каждой из которых установлено 32 вычислительных узла (compute card). Midplane, 8 x 8 x 8 = 512 вычислительных узлов, — минимальный раздел, на котором становится доступна топология трехмерного тора; для разделов меньших размеров используется топология трехмерной решетки. Node-карта может содержать до двух узлов ввода-вывода (I/O card). Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Микропроцессорное ядро:&lt;br /&gt;
&lt;br /&gt;
* модель: PowerPC 450&lt;br /&gt;
* рабочая частота: 850 MHz&lt;br /&gt;
* адресация: 32-битная&lt;br /&gt;
* кэш инструкций 1-го уровня (L1 instruction): 32 KB&lt;br /&gt;
* кэш данных 1-го уровня (L1 data): 32 KB&lt;br /&gt;
* кэш предвыборки (L2 prefetch): 14 потоков предварительной выборки (stream prefetching): 14 x 256 байтов&lt;br /&gt;
* два блока 64-битной арифметики с плавающей точкой (Floating Point Unit, FPU), каждый из которых может выдавать за один такт результат совмещенной операции умножения-сложения (Fused Multiply-Add, FMA)&lt;br /&gt;
пиковая производительность: 2 FPU x 2 FMA x 850 MHz = 3,4 GFlop/sec per core&lt;br /&gt;
&lt;br /&gt;
Вычислительные узлы и I/O-карты в аппаратном смысле неразличимы и являются взаимозаменяемыми, разница между ними состоит лишь в способе их использования. У них нет локальной файловой системы, поэтому все операции ввода-вывода перенаправляются внешним устройствам.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 cores x 3,4 GFlop/sec per core = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Оценка масштабируемости алгоритма ===&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=17436</id>
		<title>Обсуждение участника:Ivanov.kir.m</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=17436"/>
		<updated>2016-11-15T20:30:46Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Замечания к статье Быстрое дискретное преобразование Фурье */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановке первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Указали в шапке статьи&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье Быстрое дискретное преобразование Фурье ==&lt;br /&gt;
&lt;br /&gt;
* В разделе &amp;quot;Общее описание алгоритма&amp;quot; следует вставить именно описание алгоритма. Потому что смысл алгоритма проясняется только в следующем разделе, а желательно иметь его общее (с минимумом формул) описание во введении [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Стоит добавить слова о том, что происходит, когда размер массива не представим в виде произведения - можно ли тогда применять алгоритм, и если да, то какие изменения следует сделать [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не вполне понятна логика использования курсива и полужирного начертания [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не хватает данных о глубине рекурсии при использовании алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17384</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17384"/>
		<updated>2016-11-15T19:19:40Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм реализует принцип &amp;quot;разделяй и властвуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицией) до непростого числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17383</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17383"/>
		<updated>2016-11-15T19:19:25Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм реализует принцип &amp;quot;разделяй и влавствуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицией) до непростого числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17382</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17382"/>
		<updated>2016-11-15T19:19:14Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм релизует принцип &amp;quot;разделяй и влавствуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В случае простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; оно дополняется(как правило единицией) до непростого числа. В процессе слияния добавленные значения отбрасываются.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17379</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17379"/>
		<updated>2016-11-15T19:15:29Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм релизует принцип &amp;quot;разделяй и влавствуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В общем случае:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;свёртки&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17377</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17377"/>
		<updated>2016-11-15T19:13:46Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В отличии от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм релизует принцип &amp;quot;разделяй и влавствуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; В общем случае &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разбивается на &amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; сами не являлись простыми числами и разлагались на множители не являющиеся простыми числами.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17369</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17369"/>
		<updated>2016-11-15T19:06:09Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. В отличае от своего классического аналога БПФ имеет сложность &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;, а не &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, это достигается благодаря разделению исходного вектора на несколько частей, так чтобы применив к этим частям БПФ и объединив результаты путем умножения на поворотные множители и дополнительного БПФ, можно было получить преобразование Фурье исходного вектора - принцип &amp;quot;разделяй и властвуй&amp;quot;. С математической точки зрения преобразование сигнала &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; (как функции по времени &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;) в спектр &amp;lt;math&amp;gt;\hat{s}&amp;lt;/math&amp;gt; описывается формулой&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{s}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}s(t)e^{-it\omega}\,dt&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как алгоритм релизует принцип &amp;quot;разделяй и влавствуй&amp;quot; глубина рекурсии составляет &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt; от числа входных элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17352</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17352"/>
		<updated>2016-11-15T18:48:58Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Глубина рекурсии при таком алгоритме получается порядка &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17350</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17350"/>
		<updated>2016-11-15T18:47:46Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;поворотные множители&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;поворотные множители&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17348</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17348"/>
		<updated>2016-11-15T18:47:09Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;эрмитовой избыточности&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17347</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=17347"/>
		<updated>2016-11-15T18:46:28Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел - &#039;&#039;сигнала&#039;&#039; - в вектор/матрицу комплексных чисел - &#039;&#039;спектр&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое &#039;&#039;&#039;преобразование Фурье вектора действительных чисел&#039;&#039;&#039;, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанной на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно, в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Для эксперимента использовались:&lt;br /&gt;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{15}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{26}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw time good cr.png  |center|thumb|800px| Замеры времени работы параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Elapsed Time -- время затраченное на выполненине алгоритма в секундах.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Fftw efficency good cr.png |center|thumb|800px| Эффективность параллельной реализации FFTW для входного вектора действительных чисел. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; -- число входных элементов. По оси Procs Num указано &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; -- число задействованных процессоров. По оси Effeciency -- эффективность алгоритма в процентах.]]&lt;br /&gt;
&lt;br /&gt;
Из графиков можно сделать следующие выводы:&lt;br /&gt;
* Значительное увеличение числа процессоров не дает значимого уменьшения времени исполнения, что отражено и на графике эффективности. Это означает, что время, в основном, расходуется на пересылки данных, а не на рассчет.&lt;br /&gt;
* При большом количестве элементов и маленьком числе процессоров наблюдается сверхлинейное ускорение, что связано с маленьким число пересылок и активным использованием кеша.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14730</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14730"/>
		<updated>2016-10-29T16:26:09Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel good crop 2.png |center|thumb|800px| Параллельная реализация алгоритма Кули-Тьюки для преобразования вектора действительный чисел. Изменение времени работы алгоритма в зависимости от числа процессоров и размера входного вектора. По оси Data Size указано &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, размер входного вектора &amp;lt;math&amp;gt;2^N&amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14729</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14729"/>
		<updated>2016-10-29T16:25:48Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel good crop 2.png |center|thumb|800px| Параллельная реализация алгоритма Кули-Тьюки для преобразования вектора действительный чисел. Изменение времени работы алгоритма в зависимости от числа процессоров и размера входного вектора. По оси Data Size указано N, размер входного вектора &amp;lt;math&amp;gt;2^N&amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14728</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14728"/>
		<updated>2016-10-29T16:24:25Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel good crop 2.png |center|thumb|800px| Параллельная реализация алгоритма Кули-Тьюки для преобразования вектора действительный чисел. Изменение времени работы алгоритма в зависимости от числа процессоров и размера входного вектора.]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14727</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14727"/>
		<updated>2016-10-29T16:21:28Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel good crop 2.png |center|thumb|800px| Описание...]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14366</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=14366"/>
		<updated>2016-10-28T12:10:44Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет высокий ресурс параллелизма, а алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того, параллелизм БПФ можно увеличить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; вектор действительных/комплексных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор действительных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;b = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор действительных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \ddots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановке первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13871</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13871"/>
		<updated>2016-10-26T08:34:00Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения).Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет весьма высокий ресурс параллелизма, а простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того параллелизм БПФ можно усилить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор вещественных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;Y = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор вещественных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 0&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13870</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13870"/>
		<updated>2016-10-26T08:33:35Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения).Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет весьма высокий ресурс параллелизма, а простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того параллелизм БПФ можно усилить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор вещественных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;Y = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор вещественных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  E = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13869</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13869"/>
		<updated>2016-10-26T08:32:13Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения).Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет весьма высокий ресурс параллелизма, а простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того параллелизм БПФ можно усилить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Есть несколько ситуаций, при которых количество потребляемой памяти для хранения входного вектора &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и выходного вектора &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; тможет быть снижено вдвое: &lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор вещественных чисел, тогда &amp;lt;math&amp;gt;b = E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел, тогда &amp;lt;math&amp;gt;Y = -E\bar{b}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = E\bar{a}&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор вещественных чисел;&lt;br /&gt;
# Если &amp;lt;math&amp;gt;a = -E\bar{b}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — вектор чисто-мнимых чисел.&lt;br /&gt;
&lt;br /&gt;
Где матрица &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Q = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp; 0 &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13861</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13861"/>
		<updated>2016-10-26T08:06:25Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения).Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет весьма высокий ресурс параллелизма, а простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
&lt;br /&gt;
Кроме того параллелизм БПФ можно усилить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13860</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13860"/>
		<updated>2016-10-26T08:06:09Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения).Здесь стоит отметить, что все рекурсивные вызовы БПФ на каждом шаге выполняются независимо, это приводит к тому, что их можно распределить по доступным вычислительным узлам. Аналогичная ситуация обстоит и с умножением на поворотные множетели, что позволяет независимо присоединять его к любому шагу. Таким образом алгоритм БПФ имеет весьма высокий ресурс параллелизма, а простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности. &lt;br /&gt;
Кроме того параллелизм БПФ можно усилить, если брать &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; по возможности близкими к кратным доступному количеству вычислительных узлов (а не брать просто один из множителей близким к нулю, как в традиционной реализации), это позволит равномерно распределить работу между вычислительными узлами, что сильно повысит эффективность алгоритма благодаря лишь одной внутренней пересылке данных.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13854</id>
		<title>Обсуждение участника:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13854"/>
		<updated>2016-10-26T07:52:06Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Отсутствующие части */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановки первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Указали в шапке статьи&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13853</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13853"/>
		<updated>2016-10-26T07:51:30Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]] Вклад каждого участника равномерный, поскольку каждый пункт вычитывался и исправлялся обоими участниками. При сильном приближении можно сказать,что больший вклад [[Участник:Ivanov.kir.m | К.М.Иванов]] внес в заполнение теоретической части, а [[Участник:Pumpkin | А.C.Сапин]] в заполнение практической части. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13849</id>
		<title>Обсуждение участника:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13849"/>
		<updated>2016-10-26T07:43:01Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Замечания к статье */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановки первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13847</id>
		<title>Обсуждение участника:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13847"/>
		<updated>2016-10-26T07:42:01Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Замечания к статье */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановки первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13843</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13843"/>
		<updated>2016-10-26T07:36:14Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; Согласно постановки первого домашнего задание по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16]) срок сдачи данного пункта отложен до 1&#039;&#039;&#039;5 ноября 2016 года&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13842</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13842"/>
		<updated>2016-10-26T07:34:07Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* ЧАСТЬ. Программная реализация алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13839</id>
		<title>Обсуждение участника:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13839"/>
		<updated>2016-10-26T07:30:48Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Замечания к статье */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13838</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=13838"/>
		<updated>2016-10-26T07:30:08Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Рекурсивное описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
=== Рекурсивное описание ===&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=7801</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=7801"/>
		<updated>2016-10-14T17:09:10Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=7800</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=7800"/>
		<updated>2016-10-14T17:08:26Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Литература */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;br /&gt;
&lt;br /&gt;
[3] FFTW [Электронный ресурс]. Тема: One-Dimensional DFTs of Real Data – Электрон. дан. – URL [http://www.fftw.org/doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data One-Dimensional DFTs of Real Data] (последняя дата обращения 14.19.2016)&lt;br /&gt;
&lt;br /&gt;
[4] FFTW [Электронный ресурс]. – Электрон. дан. – URL [http://www.fftw.org/ FFTW Lib] (последняя дата обращения 14.19.2016)&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=6321</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=6321"/>
		<updated>2016-10-13T09:44:45Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Литература */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=6319</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=6319"/>
		<updated>2016-10-13T09:44:25Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Литература */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
[2] Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=6317</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=6317"/>
		<updated>2016-10-13T09:42:26Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* ЧАСТЬ. Свойства и структура алгоритмов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке &#039;&#039;&#039;FFTW&#039;&#039;&#039;, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;x = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;X = (X_1,X_2,...,X_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_k = \sum_{n=0}^{N-1} x_n e^{-\frac{2\pi i}{N} nk}, k = \overline{0,N-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В простейшем случае(для степеней двойки) алгоритм Кули-Тьюки рассчитывает ДПФ для четных &amp;lt;math&amp;gt;(x_{2m}=x_0, x_2, \ldots, x_{N-2})&amp;lt;/math&amp;gt; и нечетных элементов отдельно &amp;lt;math&amp;gt;(x_{2m+1}=x_1, x_3, \ldots, x_{N-1})&amp;lt;/math&amp;gt;. Затем результаты рассчетов объединяются для получения ДПФ всей последовательности: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{matrix} X_k &amp;amp; =&lt;br /&gt;
&amp;amp; \sum \limits_{m=0}^{N/2-1} x_{2m}e^{-\frac{2\pi i}{N} (2m)k}   +   \sum \limits_{m=0}^{N/2-1} x_{2m+1} e^{-\frac{2\pi i}{N} (2m+1)k}&lt;br /&gt;
  \end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, получается рекурсивный алгоритм, работающий по принципу [https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D1%8F%D0%B9_%D0%B8_%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B2%D1%83%D0%B9_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) разделяй и влавствуй].&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m&amp;diff=5635</id>
		<title>Участник:Ivanov.kir.m</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m&amp;diff=5635"/>
		<updated>2016-10-12T09:39:45Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: Создана пустая страница&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5626</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5626"/>
		<updated>2016-10-12T09:28:54Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополняют&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3 \text{   } (N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5625</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5625"/>
		<updated>2016-10-12T09:27:20Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополняют&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(N = n_1 \cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5624</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5624"/>
		<updated>2016-10-12T09:27:05Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополняют&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 21&amp;lt;/math&amp;gt;. Исходные данные обозначены зеленым, результат — синим.  Преобразования Фурье для &amp;lt;math&amp;gt;n_1 = 7&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_2 = 3&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(N = n_1 \ cdot n_2)&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты (множители) представлено коричневыми ромбами.]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5622</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5622"/>
		<updated>2016-10-12T09:24:19Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополняют&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Приведем информационный граф для БПФ в простом случае - для степени двойки. &lt;br /&gt;
[[Файл:InfoGraph Ivanov Sapin.png |center|thumb|800px| Информационный граф для 16-ти точечного быстрого преобразования Фурье.   ]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:InfoGraph_Ivanov_Sapin.png&amp;diff=5620</id>
		<title>Файл:InfoGraph Ivanov Sapin.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:InfoGraph_Ivanov_Sapin.png&amp;diff=5620"/>
		<updated>2016-10-12T09:23:17Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5600</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5600"/>
		<updated>2016-10-12T08:16:01Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополняют&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Приведем информационный граф для БПФ в простом случае - для степени двойки. &lt;br /&gt;
[[Файл:Radix2.jpg |center|thumb|600px| Информационный граф для 16-ти точечного быстрого преобразования Фурье.   ]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5598</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5598"/>
		<updated>2016-10-12T08:06:53Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C++.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополняют&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Приведем информационный граф для БПФ в простом случае - для степени двойки. &lt;br /&gt;
[[Файл:Radix2.jpg |center|thumb|600px| Информационный граф для 16-ти точечного быстрого преобразования Фурье.   ]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5597</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5597"/>
		<updated>2016-10-12T08:03:09Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; в библиотеке FFTW, написанная на языке C++.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - алгоритм Кули-Тьюки быстрого преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополнять&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Приведем информационный граф для БПФ в простом случае - для степени двойки. &lt;br /&gt;
[[Файл:Radix2.jpg |center|thumb|600px| Информационный граф для 16-ти точечного быстрого преобразования Фурье.   ]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5595</id>
		<title>Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ivanov.kir.m/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=5595"/>
		<updated>2016-10-12T08:00:54Z</updated>

		<summary type="html">&lt;p&gt;Ivanov.kir.m: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм Кули-Тьюки одномерного преобразования Фурье для действительных чисел&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O (N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O (\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; действительных чисел&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\lfloor N/2 \rfloor+1&amp;lt;/math&amp;gt; комплексных чисел&lt;br /&gt;
}}&lt;br /&gt;
Авторы описания [[Участник:Ivanov.kir.m | К.М.Иванов]], [[Участник:Pumpkin | А.C.Сапин]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Быстрое преобразование Фурье (БПФ, FFT)&#039;&#039;&#039; — алгоритм быстрого  вычисления дискретного преобразования Фурье (ДПФ). То есть, алгоритм вычисления за количество действий, меньшее чем &amp;lt;math&amp;gt;O(N^{2})&amp;lt;/math&amp;gt;, требуемых для прямого (по формуле) вычисления ДПФ. Иногда под БПФ понимается один из быстрых алгоритмов, называемый алгоритмом прореживания по частоте/времени или алгоритмом по основанию 2, имеющий сложность &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt;. Cуществует несколько различных алгоритмов для вычисления ДПФ считающимся быстрым преобразование Фурье:&lt;br /&gt;
* Алгоритм Кули-Тьюки [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm]&lt;br /&gt;
* Алгоритм Гуда-Томаса [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm]&lt;br /&gt;
* Алгоритм Бруна  [https://en.wikipedia.org/wiki/Bruun%27s_FFT_algorithm]&lt;br /&gt;
* Алгоритм Блюштейна [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm]&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Быстрое дискретное преобразование Фурье&#039;&#039;&#039; (&#039;&#039;БПФ&#039;&#039;) - популярный в современном мире математический метод преобразования вектора/матрицы комплексных/действительных чисел (&#039;&#039;&#039;&#039;&#039;сигнала&#039;&#039;&#039;&#039;&#039;) в вектор/матрицу комплексных чисел, т.н. &#039;&#039;&#039;&#039;&#039;спектр&#039;&#039;&#039;&#039;&#039;. Область применения данного метода простирается от обработки аудио информации до собственно спектрального анализа.&lt;br /&gt;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести &#039;&#039;дополнительную оптимизацию&#039;&#039;. Одной из таких подзадач является быстрое преобразование Фурье вектора &#039;&#039;&#039;&#039;&#039;действительных&#039;&#039;&#039;&#039;&#039; чисел, которому и посвящена данная статья. В качестве тестируемой реализации бала выбрана библиотека FFTW, написанная на языке C++.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание&#039;&#039;&#039;: Поскольку общие описание алгоритма БПФ весьма сложно в некоторых пунктах данная статья будет ссылаться на простой случай - быстрое преобразование Фурье для векторов &#039;&#039;с размерностью равной степени двойки&#039;&#039; - &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039;. Отличительной особенностью данного алгоритма является то, что он обходится без использования специфических приемов, использующихся именно для степеней четверки, восьмерки и т.п. Однако благодаря тому, что на вход данному алгоритму подается вектор чисто вещественных чисел, выходной вектор удовлетворяет &#039;&#039;&#039;эрмитовой избыточности&#039;&#039;&#039; (&#039;&#039;Hermitian redundancy&#039;&#039;) , т.е.  &amp;lt;math&amp;gt;out[i]&amp;lt;/math&amp;gt; является сопряженным с &amp;lt;math&amp;gt;out[n-i]&amp;lt;/math&amp;gt;. Это обстоятельство позволяет достичь роста скорости и снижения затрат памяти примерно в 2 раза по сравнению с &#039;&#039;комплексным&#039;&#039; аналогом алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Замечание:&#039;&#039;&#039;&#039;&#039; В простейшем случае &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; применяется к векторам &#039;&#039;&#039;размерности степени двойки&#039;&#039;&#039;, поэтому на практике вектора иной размерности часто &#039;&#039;&#039;дополнять&#039;&#039;&#039; до ближайшей степени двойки. Такой подход делает &#039;&#039;&#039;алгоритм Кули-Тьюки&#039;&#039;&#039; не самым эффективным алгоритмом БПФ, поскольку дополнение до степени двойки может сильно усложнить задачу.&lt;br /&gt;
&lt;br /&gt;
== Рекурсивное описание ==&lt;br /&gt;
Алгоритм:&lt;br /&gt;
# Входной вектор &amp;lt;math&amp;gt;a = (a_0,a_2,...,a_{N-1})&amp;lt;/math&amp;gt; преобразуется в матрицу &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n_1 \times n_2 &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;N=n_1 \cdot n_2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; A = \begin{pmatrix}&lt;br /&gt;
  a_0 &amp;amp; a_1 &amp;amp; \cdots &amp;amp; a_{n_1-1} \\&lt;br /&gt;
  a_{n_1} &amp;amp; a_{n_1+1} &amp;amp; \cdots &amp;amp; a_{2n_1-1} \\&lt;br /&gt;
  \vdots  &amp;amp; \vdots  &amp;amp; \ddots &amp;amp; \vdots  \\&lt;br /&gt;
  a_{(n_2-1)\cdot n_1} &amp;amp; a_{(n_2-1)\cdot n_1+1} &amp;amp; \cdots &amp;amp; a_{n_2\cdot n_1-1} &lt;br /&gt;
 \end{pmatrix} &amp;lt;/math&amp;gt;&lt;br /&gt;
# К каждой строке полученной матрицы применяется быстрое дискретное преобразование Фурье (БПФ) порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Каждый элемент полученный после применения БПФ умножается на &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; &amp;lt;math&amp;gt;exp (2 \pi i(m-1)(j-1)/N)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - номер строки, а &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; - номер столбца&lt;br /&gt;
# Полученная после шагов 1-2 матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; транспонируется&lt;br /&gt;
# К каждой строке матрицы &amp;lt;math&amp;gt;A^T&amp;lt;/math&amp;gt; применяется БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Замечание:&#039;&#039;&#039; Как правило все &#039;&#039;&#039;&#039;&#039;поворотные множители&#039;&#039;&#039;&#039;&#039; вычисляются заранее и хранятся в специальном массиве.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
В случае размерности входа равной степени двойки, вычислительным ядром алгоритма является, так называемая, &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot;. В простейшем случае &amp;quot;&#039;&#039;бабочка&#039;&#039;&amp;quot; представляет из себя двухточечное преобразование. Рассмотрим этот случай:&lt;br /&gt;
&lt;br /&gt;
На вход алгоритму подается двухэлементный вектор ‒ &amp;lt;math&amp;gt; v = (v[0], v[1]) &amp;lt;/math&amp;gt;. Тогда для вычисления будут происходить по следующим формулам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0] = W_2^0 v[0] + W_2^0 v[1] = v[0] + W_2^0 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1] = W_2^0 v[0] + W_2^1 v[1] = v[0] + W_2^1 v[1] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный процесс удобно изобразить с помощью следующей схемы:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly.png | Рис. 1. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для двумерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для 4-х элеметного вектора &amp;lt;math&amp;gt;v=(v[0],v[1],v[2],v[3])&amp;lt;/math&amp;gt;, алгоритм строится похожим образом. Сначала создаются простейшие &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot;, а потом их результаты соединяются с противоположеной &amp;quot;бабочкой&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[0]=v[0]+W_2^0 v[2]+W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[1]=v[0]-W_2^0 v[2]+W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[2]=v[0]+W_2^0 v[2]-W_4^0(v[1]+W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;V[3]=v[0]-W_2^0 v[2]-W_4^1(v[1]-W_2^0 v[3])&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схема в таком случае будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
[[file:Fftbutterfly4.png| Рис. 2. &amp;quot;&#039;&#039;Бабочка&#039;&#039;&amp;quot; для четырёхмерного входа ]]&lt;br /&gt;
&lt;br /&gt;
Для случая, когда вход не является степенью двойки, &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; будут &amp;quot;&#039;&#039;несимментричными&#039;&#039;&amp;quot;, но в остальном вычисления будут проходить схожим образом.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Для исходного вектора &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;N = n_1 \cdot n_2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_1 &amp;lt; n_2&amp;lt;/math&amp;gt; БПФ представляется как:&lt;br /&gt;
#&amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1 \cdot n_2&amp;lt;/math&amp;gt; умножение комплексных чисел &lt;br /&gt;
#&amp;lt;math&amp;gt;n_1&amp;lt;/math&amp;gt; БПФ порядка &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Схема организации состоит в том, что на каждом шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; для выполнения &amp;quot;&#039;&#039;бабочки&#039;&#039;&amp;quot; все элементы разбиваются на &amp;lt;math&amp;gt;n_{i_1}&amp;lt;/math&amp;gt; векторов по &amp;lt;math&amp;gt;n_{i_2}&amp;lt;/math&amp;gt; элементов, причем &amp;lt;math&amp;gt;n_{i_1} \cdot n_{i_2} = n_i&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; длина входного вектора на текущем шаге &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. В случае если такое разбиение невозможно, по причине того что &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; простое число, исходный вектор на текущем шаге дополняется элементом. В зависимости от номера шага, разница координат для каждой пары элементов увеличивается соразмерно разбиению  &amp;lt;math&amp;gt;(n_{i_1},n_{i_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
При этом результат суммы записывается в элемент с меньшим номером, а результат вычитания с последующим умножением - в элемент с большим.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Быстрое дискретное преобразование Фурье выполнимо за &amp;lt;math&amp;gt;O(N(n_1+\cdots+n_m))&amp;lt;/math&amp;gt; действий при &amp;lt;math&amp;gt;N=n_1n_2\cdots n_m&amp;lt;/math&amp;gt; (в простом случае, при &amp;lt;math&amp;gt;N=2^m&amp;lt;/math&amp;gt; необходимо &amp;lt;math&amp;gt;O(N\log_2(N))&amp;lt;/math&amp;gt; действий). Дискретное преобразование Фурье преобразует вектор &amp;lt;math&amp;gt;a = (a_0, \dots, a_{N-1})&amp;lt;/math&amp;gt; в вектор &#039;&#039;&#039;&#039;&#039;комплексных&#039;&#039;&#039;&#039;&#039; чисел &amp;lt;math&amp;gt;b = (b_0, \dots, b_{N-1})&amp;lt;/math&amp;gt;, такой, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{N-1}a_j\varepsilon^{ij}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon^n=1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\varepsilon^k\neq 1&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;0&amp;lt;k&amp;lt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основной шаг алгоритма состоит в сведении задачи для &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; чисел к задаче для &amp;lt;math&amp;gt;n_1=N/n_2&amp;lt;/math&amp;gt; числам, где &amp;lt;math&amp;gt;n_2&amp;lt;/math&amp;gt; — делитель &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть мы уже умеем решать задачу для &amp;lt;math&amp;gt;N/n_2&amp;lt;/math&amp;gt; чисел. Применим преобразование Фурье к векторам &amp;lt;math&amp;gt;a_i,a_{n_2+i}, \dots, a_{n_2(n_1-1)+i}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i=0,1,\dots,n_2-1&amp;lt;/math&amp;gt;. Покажем теперь, что за &amp;lt;math&amp;gt;O(Np)&amp;lt;/math&amp;gt; действий можно решить исходную задачу. Заметим, что &amp;lt;math&amp;gt;b_i=\sum_{j=0}^{n_2-1} \varepsilon^{ij} \left(\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2}\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\mod p)&amp;lt;/math&amp;gt;-тое число после преобразования Фурье &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го вектора. Таким образом, для вычисления каждого &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; нужно &amp;lt;math&amp;gt;O(n_2)&amp;lt;/math&amp;gt; действий, а для вычисления всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;O(Nn_2)&amp;lt;/math&amp;gt; действий, что и требовалось.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;общем случае&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;4N&amp;gt;2^k\ge2N&amp;lt;/math&amp;gt;. Заметим, что тогда &amp;lt;math&amp;gt;b_i=\varepsilon^{-i^2/2}\sum_{j=0}^{N-1}\varepsilon^{(i+j)^2/2}\varepsilon^{-j^2/2}a_j&amp;lt;/math&amp;gt;. Обозначим &amp;lt;math&amp;gt;\bar{a}_i=\varepsilon^{-i^2/2}a_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\bar{b}_i=\varepsilon^{i^2/2}b_i&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;c_i=\varepsilon^{(2N-2-i)^2/2}&amp;lt;/math&amp;gt;. Тогда &amp;lt;math&amp;gt;\bar{b}_i=\sum_{j=0}^{2N-2-i}\bar{a}_jc_{2N-2-i-j}&amp;lt;/math&amp;gt;, если положить &amp;lt;math&amp;gt;\bar{a}_i=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;i\ge N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом задача сведена к вычислению &#039;&#039;&#039;&#039;&#039;свёртки&#039;&#039;&#039;&#039;&#039;, а это можно сделать с помощью трёх преобразований Фурье для &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt; элементов. Выполняем прямое преобразование Фурье для &amp;lt;math&amp;gt;(\bar{a_0}, \cdots, \bar{a}_{2^k-1})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(c_1,\cdots,c_{2^k-1})&amp;lt;/math&amp;gt;, перемножаем поэлементно результаты и выполняем обратное преобразование Фурье.&lt;br /&gt;
&lt;br /&gt;
Вычисления всех &amp;lt;math&amp;gt;\bar{a}_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt; требуют &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, три преобразования Фурье требуют &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; действий, перемножение результатов преобразований Фурье требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий, вычисление всех &amp;lt;math&amp;gt;b_i&amp;lt;/math&amp;gt; зная значения свертки требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; действий. &#039;&#039;&#039;Итого для дискретного преобразования Фурье требуется&#039;&#039;&#039; &amp;lt;math&amp;gt;O(N\log(N))&amp;lt;/math&amp;gt; &#039;&#039;&#039;действий для любого&#039;&#039;&#039; &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Приведем информационный граф для БПФ в простом случае - для степени двойки. &lt;br /&gt;
[[Файл:Radix2.jpg |center|thumb|600px| Информационный граф для 16-ти точечного быстрого преобразования Фурье.   ]]&lt;br /&gt;
Как видно из рисунка, размер этого графа линейно-логарифмический, а формулы дуг имеют экспоненциальные компоненты.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Если считать только главные члены выражений, то простой алгоритм Кули-Тьюки имеет критический путь, состоящий из &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного сложения/вычитания и &amp;lt;math&amp;gt;log(N)&amp;lt;/math&amp;gt; операций комплексного умножения (основание &amp;lt;math&amp;gt;log&amp;lt;/math&amp;gt; целиком зависит от выбранного на каждом шаге алгоритма разбиения). Таким образом, простой алгоритм БПФ в общем случае может быть отнесён к &#039;&#039;логарифмическому&#039;&#039; классу по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: вектор действительных чисел &amp;lt;math&amp;gt;a = (a_1,a_2,...,a_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (b_1,b_2,...,b_{\lfloor N/2 \rfloor+1})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Таким образом в общем у алгоритма БПФ в случае неограниченных ресурсов:&lt;br /&gt;
*последовательная сложность - &#039;&#039;линейно-логарифмическая&#039;&#039;&lt;br /&gt;
*параллельная сложность  - &#039;&#039;логарифмическая&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
При этом алгоритм полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
В качестве тестируемой реализации была выбрана библиотека FFTW, а именно функция, выполняющая БПФ одномерного вектора действительных чисел.&lt;br /&gt;
==  Особенности последовательной реализации алгоритма ==&lt;br /&gt;
Простейший вариант алгоритма для степеней двойки на языке C:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
#include &amp;lt;complex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef double complex cplx;&lt;br /&gt;
 &lt;br /&gt;
void _fft(cplx *buf, cplx *out, int n, int step)&lt;br /&gt;
{&lt;br /&gt;
    if (step &amp;lt; n) {&lt;br /&gt;
	_fft(out, buf, n, step * 2); //рекурсивный рассчет&lt;br /&gt;
	_fft(out + step, buf + step, n, step * 2);&lt;br /&gt;
 &lt;br /&gt;
	for (int i = 0; i &amp;lt; n; i += 2 * step) {&lt;br /&gt;
	    cplx t = cexp(-I * PI * i / n) * out[i + step];&lt;br /&gt;
	    buf[i / 2]     = out[i] + t;&lt;br /&gt;
	    buf[(i + n)/2] = out[i] - t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void fft(cplx *buf, int n)&lt;br /&gt;
{&lt;br /&gt;
    cplx out[n];&lt;br /&gt;
    for (int i = 0; i &amp;lt; n; i++) out[i] = buf[i];&lt;br /&gt;
 &lt;br /&gt;
    _fft(buf, out, n, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [ ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [http://www.fftw.org/ FFTW], разработанная Матео Фриго и Стивеном Джонсоном в Массачусетском технологическом институте. Для преобразования небольших объемов данных используется алгоритм [https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Кули - Тьюки], в случае входа больших размеров используется алгоритм [https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Радера] или [https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Блюштейна]. Библиотека считается самой быстрой реализацией БПФ и на входах любых размеров и размерностей сохраняет ассимптотическую сложность &amp;lt;math&amp;gt;O(N\cdot log(N))&amp;lt;/math&amp;gt;. Также, существует версия FFTW с поддержкой [http://www.fftw.org/doc/Distributed_002dmemory-FFTW-with-MPI.html#Distributed_002dmemory-FFTW-with-MPI MPI], которая позволяет с объемами данных не умещающимися в память.&lt;br /&gt;
&lt;br /&gt;
FFTW изначально написана на языке C, однако имеет API для нескольких языков, в том числе Fortran и Ada. Открытая версия библиотеки распространяется под лицензией [https://en.wikipedia.org/wiki/GNU_General_Public_License GNU General Public License]. Также существует реализация для коммерческого использования распространяемая под лицензией [https://en.wikipedia.org/wiki/MIT MIT].&lt;br /&gt;
&lt;br /&gt;
Существует несколько реализаций алгоритма, для рассчета на GPU:&lt;br /&gt;
* [https://developer.nvidia.com/cufft cuFFT] для рассчета на графических ускорителях от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft/ clFFT] для рассчета на GPU и CPU.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Википедия [Электронный ресурс]. Тема: Быстрое преобразование Фурье – Электрон. дан. – URL [https://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5 Быстрое преобразование Фурье] (дата обращения 17.09.2016)&lt;br /&gt;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Ivanov.kir.m</name></author>
	</entry>
</feed>