<?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=Pumpkin</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=Pumpkin"/>
	<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/Pumpkin"/>
	<updated>2026-10-22T18:15:43Z</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=17387</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=17387"/>
		<updated>2016-11-15T19:22:38Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Общее описание алгоритма */&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;
Поскольку в общем случае алгоритм БПФ применим к произвольным векторам чисел, выделяют отдельные подзадачи, для которых можно ввести дополнительную оптимизацию. Одной из таких подзадач является быстрое преобразование Фурье вектора действительных чисел, которому и посвящена данная статья. В качестве тестируемой реализации был выбран алгоритм Кули-Тьюки в библиотеке 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;
&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>Pumpkin</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=17386</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=17386"/>
		<updated>2016-11-15T19:21:46Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Последовательная сложность алгоритма */&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; действий. Итого для дискретного преобразования Фурье требуется &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;
&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>Pumpkin</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=17381</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=17381"/>
		<updated>2016-11-15T19:18:40Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Рекурсивное описание */&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;
&#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>Pumpkin</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=17375</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=17375"/>
		<updated>2016-11-15T19:12:32Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Общее описание алгоритма */&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;
== Вычислительное ядро алгоритма ==&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>Pumpkin</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=17373</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=17373"/>
		<updated>2016-11-15T19:11:09Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Общее описание алгоритма */&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;
== Вычислительное ядро алгоритма ==&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>Pumpkin</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=17363</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=17363"/>
		<updated>2016-11-15T19:03:50Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Математическое описание алгоритма */&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;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>Pumpkin</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=14813</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=14813"/>
		<updated>2016-10-30T13:10:36Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
* Компилятор 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>Pumpkin</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=14791</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=14791"/>
		<updated>2016-10-30T12:06:50Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{10}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{29}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&lt;br /&gt;
[[File:Parallel good crop 2.png |center|thumb|800px| Параллельная реализация библиотеки FFTW для преобразования вектора действительный чисел. Изменение времени работы алгоритма в зависимости от числа процессоров и размера входного вектора. По оси Data Size указано &amp;lt;math&amp;gt;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>Pumpkin</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=14787</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=14787"/>
		<updated>2016-10-30T12:02:42Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{10}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{24}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&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;. На данном графике показано время работы алгоритма без учета пересылок данных.]]&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>Pumpkin</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=14738</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=14738"/>
		<updated>2016-10-29T19:45:18Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^{10}&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^{24}&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&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>Pumpkin</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=14737</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=14737"/>
		<updated>2016-10-29T19:44:57Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
* Компилятор mpicc&lt;br /&gt;
* Библиотека OpenMPI версии 1.68&lt;br /&gt;
* Библиотека FFTW 3.3.5&lt;br /&gt;
Алгоритм тестировался на входных векторах размера &amp;lt;math&amp;gt;2^10&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;2^24&amp;lt;/math&amp;gt;. При этом использовались от 8 до 128 процессоров с шагом степени двойки.&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>Pumpkin</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=14722</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=14722"/>
		<updated>2016-10-29T15:27:14Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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.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>Pumpkin</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=14718</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=14718"/>
		<updated>2016-10-29T15:23:21Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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.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>Pumpkin</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=14717</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=14717"/>
		<updated>2016-10-29T15:22:16Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
[[Файл:Parallel good.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>Pumpkin</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=14716</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=14716"/>
		<updated>2016-10-29T15:19:32Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
[[Файл:Parallel good.png | 1000px]]&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>Pumpkin</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=14715</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=14715"/>
		<updated>2016-10-29T15:18:54Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
[Файл:Parallel good.png]&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>Pumpkin</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:Parallel_good.png&amp;diff=14714</id>
		<title>Файл:Parallel good.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:Parallel_good.png&amp;diff=14714"/>
		<updated>2016-10-29T15:17:48Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Pumpkin</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=13891</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=13891"/>
		<updated>2016-10-26T08:57:42Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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>Pumpkin</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=13890</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=13890"/>
		<updated>2016-10-26T08:57:08Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Отсутствующие части */&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;/div&gt;</summary>
		<author><name>Pumpkin</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=13889</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=13889"/>
		<updated>2016-10-26T08:56:29Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Замечания по тексту */&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;/div&gt;</summary>
		<author><name>Pumpkin</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=13888</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=13888"/>
		<updated>2016-10-26T08:54:23Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Ресурс параллелизма алгоритма */&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>Pumpkin</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=13887</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=13887"/>
		<updated>2016-10-26T08:53:55Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Ресурс параллелизма алгоритма */&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>Pumpkin</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=13886</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=13886"/>
		<updated>2016-10-26T08:49:47Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Входные и выходные данные алгоритма */&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>Pumpkin</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=13885</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=13885"/>
		<updated>2016-10-26T08:48:40Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Входные и выходные данные алгоритма */&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;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; 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>Pumpkin</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=13884</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=13884"/>
		<updated>2016-10-26T08:47:59Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Входные и выходные данные алгоритма */&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;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; 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>Pumpkin</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=13883</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=13883"/>
		<updated>2016-10-26T08:47:25Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Входные и выходные данные алгоритма */&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;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; 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>Pumpkin</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=13882</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=13882"/>
		<updated>2016-10-26T08:46:31Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Входные и выходные данные алгоритма */&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; 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>Pumpkin</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=13874</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=13874"/>
		<updated>2016-10-26T08:35:36Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Входные и выходные данные алгоритма */&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; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\ &lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp;        &amp;amp; 0 &amp;amp; 1 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp;        &amp;amp; 1 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 0 &amp;amp; 1 &amp;amp;        &amp;amp; 0 &amp;amp; 0 \\&lt;br /&gt;
     0 &amp;amp; 1 &amp;amp; 0 &amp;amp;        &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>Pumpkin</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=7782</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=7782"/>
		<updated>2016-10-14T16:58:29Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
&lt;br /&gt;
[[en:Fast Discrete Fourier Transform]]&lt;/div&gt;</summary>
		<author><name>Pumpkin</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=6040</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=6040"/>
		<updated>2016-10-12T20:15:34Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Математическое описание алгоритма */&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;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>Pumpkin</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=6038</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=6038"/>
		<updated>2016-10-12T20:15:02Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Математическое описание алгоритма */&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;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_{\lfloor N/2 \rfloor+1})&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>Pumpkin</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=6036</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=6036"/>
		<updated>2016-10-12T20:14:34Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Математическое описание алгоритма */&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 = (x_1,x_2,...,x_N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: вектор комплексных чисел &amp;lt;math&amp;gt;b = (X_1,X_2,...,X_{\lfloor N/2 \rfloor+1})&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>Pumpkin</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=6028</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=6028"/>
		<updated>2016-10-12T20:09:19Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: &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;
Дискретное преобразование Фурье задается следующей формулой:&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;
Входные данные: вектор действительных чисел &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;
# Входной вектор &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>Pumpkin</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=6023</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=6023"/>
		<updated>2016-10-12T20:04:45Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Математическое описание алгоритма */&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;
Дискретное преобразование Фурье задается следующей формулой:&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;
Входные данные: вектор действительных чисел &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;
# Входной вектор &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>Pumpkin</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=6014</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=6014"/>
		<updated>2016-10-12T19:45:07Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Математическое описание алгоритма */&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;
Дискретное преобразование Фурье задается следующей формулой:&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;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>Pumpkin</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=5481</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=5481"/>
		<updated>2016-10-11T20:54:02Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Последовательная сложность алгоритма */&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;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;
[[Файл: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>Pumpkin</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=5477</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=5477"/>
		<updated>2016-10-11T20:49:53Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Информационный граф */&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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл: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>Pumpkin</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:Pumpkin&amp;diff=5469</id>
		<title>Участник:Pumpkin</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:Pumpkin&amp;diff=5469"/>
		<updated>2016-10-11T20:34:11Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: Полностью удалено содержимое страницы&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Pumpkin</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:Pumpkin&amp;diff=5468</id>
		<title>Участник:Pumpkin</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:Pumpkin&amp;diff=5468"/>
		<updated>2016-10-11T20:33:51Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: Новая страница: «Сапин Александр Сергеевич.»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Сапин Александр Сергеевич.&lt;/div&gt;</summary>
		<author><name>Pumpkin</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=5467</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=5467"/>
		<updated>2016-10-11T20:33:02Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: &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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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>Pumpkin</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=5459</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=5459"/>
		<updated>2016-10-11T20:27:23Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
&#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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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>Pumpkin</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=5458</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=5458"/>
		<updated>2016-10-11T20:27:10Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
&#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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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/tree/master ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&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>Pumpkin</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=5457</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=5457"/>
		<updated>2016-10-11T20:26:44Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Масштабируемость алгоритма и его реализации */&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;
&#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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
 Масштабируемость алгоритма проверялась с помощью тестовой программы расположенной по [https://github.com/alesapin/parallel_fftw/tree/master ссылке]. Проводились замеры времени работы алгоритма, а также эффективности.&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>Pumpkin</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=5445</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=5445"/>
		<updated>2016-10-11T20:04:32Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* ЧАСТЬ. Программная реализация алгоритма */&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;
&#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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
Наиболее известной библиотекой для выполнения различных вариантов быстрого преобразования Фурье является [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>Pumpkin</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=5443</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=5443"/>
		<updated>2016-10-11T20:04:00Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Схема реализации последовательного алгоритма */&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;
&#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;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
== Масштабируемость алгоритма и его реализации ==&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>Pumpkin</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=5439</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=5439"/>
		<updated>2016-10-11T19:55:08Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Схема реализации последовательного алгоритма */&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;
&#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;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;
Простейший вариант алгоритма для степеней двойки на языке 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;
Быстрое дискретное преобразование Фурье выполнимо за &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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
== Масштабируемость алгоритма и его реализации ==&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>Pumpkin</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=5438</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=5438"/>
		<updated>2016-10-11T19:54:01Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Схема реализации последовательного алгоритма */&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;
&#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;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;
Простейший вариант алгоритма для степеней двойки на языке 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;
Быстрое дискретное преобразование Фурье выполнимо за &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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
== Масштабируемость алгоритма и его реализации ==&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>Pumpkin</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=5437</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=5437"/>
		<updated>2016-10-11T19:52:25Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Схема реализации последовательного алгоритма */&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;
&#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;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;
Простейший вариант алгоритма для степеней двойки на языке 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;
Быстрое дискретное преобразование Фурье выполнимо за &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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
== Масштабируемость алгоритма и его реализации ==&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>Pumpkin</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=5436</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=5436"/>
		<updated>2016-10-11T19:51:20Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Схема реализации последовательного алгоритма */&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;
&#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;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;
Простейший вариант алгоритма для степеней двойки на языке 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;
Быстрое дискретное преобразование Фурье выполнимо за &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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
== Масштабируемость алгоритма и его реализации ==&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>Pumpkin</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=5433</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=5433"/>
		<updated>2016-10-11T19:47:47Z</updated>

		<summary type="html">&lt;p&gt;Pumpkin: /* Схема реализации последовательного алгоритма */&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;
&#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;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;syntaxhighlight lang=&amp;quot;С&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;
double PI;&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;/syntaxhighlight&amp;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;
Пусть мы уже умеем решать задачу для &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} (\sum_{k=0}^{n_1-1}a_{kn_2+j}\varepsilon^{kin_2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Выражения в скобках нам уже известны — это &amp;lt;math&amp;gt;(i\pmod 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;
[[Файл:Graph fourier.png |center|thumb|600px| Информационный граф для БПФ где n1=6, n2=6. В левой части выполняется построчное преобразование входного массива, а в правой поколоночное методом перемножения матриц.   ]]&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;
== Масштабируемость алгоритма и его реализации ==&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>Pumpkin</name></author>
	</entry>
</feed>