<?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=Kronberg</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=Kronberg"/>
	<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/Kronberg"/>
	<updated>2026-10-22T17:26:30Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=20690</id>
		<title>Быстрое дискретное преобразование Фурье (БПФ)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=20690"/>
		<updated>2016-12-12T07:52:50Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Assignment|IgorS|Kronberg}}&lt;br /&gt;
Авторы: [[Участник:Chachba.A|Чачба А.Н.]], [[Участник:Weasel|Костоев Р.С.]] &lt;br /&gt;
&lt;br /&gt;
[[Участник:Chachba.A|Чачба А.Н.]] заполнил половину всех заполненных пунктов (а именно 1.1, 1.2, 1.6, 1.8, 1.9) и построил информационный граф алгоритма, [[Участник:Weasel|Костоев Р.С.]] написал программу и провел серию запусков на суперкомпьютере и заполнил вторую половину пунктов. Оба после заполнения исправляли найденные ошибки во всех пунктах.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье (БПФ)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Преобразование Фурье - взаимно однозначное отображение одной функции &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; вещественной переменной, называемой таргетным сигналом, с другой функцией &amp;lt;math&amp;gt;\hat{f}&amp;lt;/math&amp;gt; вещественной переменной, называемой образом Фурье, или спектром исходной функции, по формуле:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{f}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}f(x)e^{-ix\omega}\,dx&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Дискретное преобразование Фурье, в свою очередь, есть аналог непрерывного преобразования Фурье, но для дискретного сигнала, содержащего &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; отсчетов.&lt;br /&gt;
Широко применяется в цифровой обработке сигналов, теории вероятностей, криптографии и акустике.&lt;br /&gt;
Преобразование Фурье обратимо, причем обратное преобразование имеет практически ту же форму, что и прямое.&lt;br /&gt;
Преобразование Фурье имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, но существует быстрый вариант преобразование Фурье со сложностью &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Такая скорость достигается благодаря применению схемы «разделяй и властвуй». Общий вид преобразования позволяет разделить массив на несколько частей таким образом, что, применяя преобразование Фурье к отдельным его частям и объединяя специальным образом результаты (при помощи поворотных множителей и повторного применения преобразования), получать на выходе образ исходного вектора. Но благодаря разделению массива исходного размера на части суммарная асимптотическая сложность алгоритма уменьшается.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Пусть исходный сигнал имеет значения &amp;lt;math&amp;gt;x_n,\quad n = 0,\dots,N-1&amp;lt;/math&amp;gt;, тогда дискретное прямое преобразование Фурье (ДПФ) имеет вид:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
X_k = \sum_{n=0}^{N-1}x_ne^{-\frac{2\pi i}{N}kn},\quad k = 0, \dots, N-1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt; \varepsilon_{N} = e^{-\frac{2\pi i}{N}}&amp;lt;/math&amp;gt;, тогда ДПФ можно перезаписать в матричной форме:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\bar X = A\bar x&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где матрица &amp;lt;math&amp;gt;A = \{\varepsilon_{N}^{(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt;&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Пусть, для простоты &amp;lt;math&amp;gt; N = km&amp;lt;/math&amp;gt;, тогда рекурсивная реализация преобразования Фурье, за счет &amp;lt;math&amp;gt;{k}&amp;lt;/math&amp;gt; рекурсий на первом этапе и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; на последнем этапе, имеет суммарную сложность &amp;lt;math&amp;gt;O(Nk + Nm + km) = O(N(k + m))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В случае, например &amp;lt;math&amp;gt;N = 2^n&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем же случае, когда &amp;lt;math&amp;gt;N = \prod_{i=1}^np_i&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt; O(N(\sum_{i=1}^np_i))&amp;lt;/math&amp;gt;. Так как рекурсия применяется каждый раз при отщеплении одного множителя, то можно заключить, что глубина рекурсии алгоритма составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&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&amp;lt;/math&amp;gt; не факторизуется в произведение простых множителей (то есть &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; — простое число), то данный алгоритм напрямую не применим. На замену ему приходит алгоритм Радера, который в некотором смысле сводит задачу размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; к подзадаче задаче размерности &amp;lt;math&amp;gt;N\pm 1&amp;lt;/math&amp;gt; вычисления свертки, в которой допустимо и увеличение размерности до составных в том числе.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Макроструктура БПФ для случая &amp;lt;math&amp;gt;N = km&amp;lt;/math&amp;gt; описывается рекурсивно:&lt;br /&gt;
# &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; независимых преобразований векторов меньшей размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&lt;br /&gt;
#Умножение элементов на поворотные коэффициенты (&amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; умножений)&lt;br /&gt;
# &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; обратных преобразований векторов размерностей &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Рекурсивный метод для случая &amp;lt;math&amp;gt;N = 2^k&amp;lt;/math&amp;gt;, без оптимизации на C++:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;complex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
typedef complex&amp;lt;double&amp;gt; cd;&lt;br /&gt;
typedef vector&amp;lt;cd&amp;gt; vcd;&lt;br /&gt;
&lt;br /&gt;
vcd fft(const vcd &amp;amp;as) { &lt;br /&gt;
  int n = as.size();&lt;br /&gt;
  if (n == 1) return vcd(1, as[0]);&lt;br /&gt;
&lt;br /&gt;
  vcd w(n); // Calculate roots&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    double alpha = 2 * M_PI * i / n;&lt;br /&gt;
    w[i] = cd(cos(alpha), sin(alpha));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  vcd A(n / 2), B(n / 2);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n / 2; i++) {&lt;br /&gt;
    A[i] = as[i * 2];&lt;br /&gt;
    B[i] = as[i * 2 + 1];&lt;br /&gt;
  }&lt;br /&gt;
  vcd Av = fft(A);&lt;br /&gt;
  vcd Bv = fft(B);&lt;br /&gt;
  vcd res(n);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    res[i] =   Av[i % (n / 2)] +&lt;br /&gt;
        w[i] * Bv[i % (n / 2)];&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
В последовательном варианте можно бороться за улучшение константы сложности, предподсчитав значения соответствующих коэффициентов.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Алгоритм состоит из трех этапов, следовательно если &amp;lt;math&amp;gt;N = \prod_{i = 1}^np_i&amp;lt;/math&amp;gt;, то общая сложность составляет порядка &amp;lt;math&amp;gt;O(N\sum_{i=1}^np_i)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Пусть существует некоторое фиксированное число &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, такое что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
p_i \leq P \quad \forall i = 1...n,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
тогда, учитывая, что &amp;lt;math&amp;gt;n \leq \log_{P}{N}&amp;lt;/math&amp;gt;, сложность алгоритма можно записать в виде &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:FourierGraph2.png|center|thumb|640px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф преобразования Кули-Тьюки для &amp;lt;math&amp;gt;N = km = 25, k = 5, m = 5&amp;lt;/math&amp;gt;. Желтые вершины - умножение на поворотные множители.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
В силу независимости всех рекурсивных вызовов и того факта, что путь максимальной длины в графе (так называемый критический путь) имеет порядок &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;, можно утверждать, что параллельная сложность алгоритма составит &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Замечание&#039;&#039;: формально основание логарифма на каждом рекурсивном шаге зависит от множителя в факторизации &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, потому использована О-символика.&lt;br /&gt;
&lt;br /&gt;
При условии ограниченности количества доступных вычислительных узлов рекомендуется выбирать множители в разложении числа &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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
Чаще всего для обработки сигналов в качестве входных данных для БПФ подается вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вещественных элементов. Но БПФ работает и для случая элементов над комплексным полем. Таким образом, например, можно экономить на количестве применений БПФ в некоторой конкретной задаче путем приведения двух векторов вещественных чисел к одному вектору комплексных чисел с вещественной частью равной первому вектору и комплексной частью равной второму вектору:&lt;br /&gt;
&amp;lt;math&amp;gt;x, y&amp;lt;/math&amp;gt; - исходные сигналы. Обозначим &amp;lt;math&amp;gt;z = x + iy&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;F(k, x)&amp;lt;/math&amp;gt; - это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-ый коэффициент образа Фурье вектора &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. Тогда:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учитывая, что &amp;lt;math&amp;gt;F(k, \bar z) = \overline{F(N - k, z)}&amp;lt;/math&amp;gt;, получаем:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + \overline{F(N-k, z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - \overline{F(N-k,z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Такой способ позволяет приблизительно в два раза уменьшить количество вычислений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
Вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; комплексных чисел - спектр исходного сигнала.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Матрица Вандермонда преобразования &amp;lt;math&amp;gt;A = \{e^{-\frac{2\pi i}{N}(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt; такая, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
A^{-1} = \frac{1}{N}A^*&lt;br /&gt;
&amp;lt;/math&amp;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;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Все запуски программы[http://pastebin.com/raw/xajKb9JN] проводились на с/к Blue Gene/P с использованием библиотеки Intel MKL.&lt;br /&gt;
&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 2048 четырехъядерных вычислительных узлов, с пиковой производительностью 27,9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W&lt;br /&gt;
&lt;br /&gt;
Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 ядра x 3,4 GFlop/sec на ядро = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 2.&#039;&#039;&#039; Время выполнения параллельной реализации БПФ в секундах. Size - число элементов массива. Р - число использованных процессоров.]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efff.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 3.&#039;&#039;&#039; Эффективность параллельной реализации БПФ. Size - число элементов массива. Р - число использованных процессоров. Эффективность в пределах от 0 до 1.]]&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]&lt;br /&gt;
* Реализация от Intel в рамках [//software.intel.com/en-us/intel-mkl Math Kernel Library]&lt;br /&gt;
* Реализация ДПФ на графических картах от NVidia - [//developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
* Описание алгоритма на Википедии: [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 Быстрое преобразование Фурье]&lt;br /&gt;
* Материалы лекций по &amp;quot;Алгоритмам и структурам данных&amp;quot; Школы Анализа Данных Яндекса&lt;/div&gt;</summary>
		<author><name>Kronberg</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:%D0%95%D0%BA%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D0%BD%D0%B0%D0%9A%D0%BE%D0%B7%D1%8B%D1%80%D0%B5%D0%B2%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9_%D0%B8%D0%B5%D1%80%D0%B0%D1%80%D1%85%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_CHAMELEON&amp;diff=19482</id>
		<title>Участник:ЕкатеринаКозырева/Алгоритм динамической иерархической кластеризации CHAMELEON</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:%D0%95%D0%BA%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D0%BD%D0%B0%D0%9A%D0%BE%D0%B7%D1%8B%D1%80%D0%B5%D0%B2%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9_%D0%B8%D0%B5%D1%80%D0%B0%D1%80%D1%85%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_CHAMELEON&amp;diff=19482"/>
		<updated>2016-11-29T14:32:38Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Kronberg}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм динамической иерархической кластеризации CHAMELEON&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nm + n log n + m^2 log m)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;\frac{n (n - 1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n+ n log(n) + m^2 log (m) + m log(m))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания [[Участник:ЕкатеринаКозырева|Е.А.Козырева]]&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Алгоритм CHAMELEON был предложен в 1999 году тремя учеными из университета Миннесоты – George Karypis, Eui-Hong (Sam) Han и Vipin Kumar&amp;lt;ref&amp;gt;George Karypis, Eui-Hong (Sam) Han и Vipin Kumar, «Chameleon: Hierarchical Clustering Using Dynamic Modeling», 1999.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предназначен для решения задач кластеризации. Кластеризация (или кластерный анализ) — это задача разбиения множества объектов на группы, называемые кластерами. Внутри каждой группы должны оказаться «похожие» объекты, а объекты разных группы должны быть как можно более отличны.&lt;br /&gt;
&lt;br /&gt;
CHAMELEON - это агломеративный иерархический алгоритм кластеризации, ключевой особенностью которого является то, что он учитывает и взаимную связность, и сходство при определении наиболее похожей пары подкластеров, основываясь на динамической модели. Это означает, что в процессе кластеризации два кластера объединяются, только если их относительная взаимная связность и относительное взаимное сходство являются высокими по отношению к внутренней взаимосвязанности кластеров и близости элементов внутри кластеров. Кроме того, CHAMELEON использует подход для моделирования степени взаимосвязанности и близости между каждой парой кластеров, который учитывает внутренние характеристики самих кластеров. Таким образом, он может автоматически адаптироваться к внутренним характеристикам объединяемых кластеров.&lt;br /&gt;
&lt;br /&gt;
CHAMELEON находит кластеры в наборе данных с помощью трехфазного алгоритма. На первой фазе происходит построение графа, путём добавления рёбер по принципу k ближайших соседей. На второй фазе CHAMELEON группирует полученные элементы в множество относительно небольших подграфов. Во время третьей фазы применяется агломеративный иерархический алгоритм кластеризации, с помощью которого находятся естесственные кластеры путем многократного объединения подграфов, полученных на прошлом этапе.&lt;br /&gt;
&lt;br /&gt;
[[File:Chameleon.png|1000px|thumb|center|Рисунок 1. Общая структура алгоритма CHAMELEON]]&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Исходные данные&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
* Множество из n точек &amp;lt;math&amp;gt;V= {v_{ij}}&amp;lt;/math&amp;gt; в метрическом пространстве, которое задано симметрической матрицей расстояний &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n\times n&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество ближайших соседей для вершин, &amp;lt;math&amp;gt;k \in N, k \leq n&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; - наименьшее число вершин, которое может содержать наибольший подграф на 2-м этапе, &amp;lt;math&amp;gt;l \in N, l \leq n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Обозначения&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; - граф, полученный путём соединения каждой точки с её &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайшими соседями.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;K= \{K_{i}\}&amp;lt;/math&amp;gt; - разбиение множества &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на набор попарно непересекающихся связных подмножеств, полученное в результате выполнения второй фазы алгоритма.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;G_{2} = (K, E_{2})&amp;lt;/math&amp;gt; - взвешенный граф, вершинами которого являются получившиеся подграфы, а ребрами - количество ребер исходного графа, соединяющих соответствующие подграфы.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;C = \{C_{i}\}&amp;lt;/math&amp;gt; - итоговое разбиение множества вершин графа &amp;lt;math&amp;gt;G_{2}&amp;lt;/math&amp;gt; на набор кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; - абсолютная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Определяется как сумма весов ребер, соединяющих вершины, принадлежащие &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; c вершинами из &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt;. Внутренняя связность &amp;lt;math&amp;gt;EC_{(C_{i},C_{i})}&amp;lt;/math&amp;gt; вычисляется как сумма ребер, входящих в разделитель, разбивающий &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; на два равных подграфа.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} = \frac{|EC_{(C_{i},C_{j})}|}{(|EC_{C_{i}}|+|EC_{C_{j}}|)/2}&amp;lt;/math&amp;gt; - относительная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt; - абсолютное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Подсчитывается как среднее сходство между соединенными вершинами, принадлежащими &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt; соответственно. Соединения обусловлены разбиением общего графа, полученного на первом этапе алгоритма.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}= \frac{S_{EC_{(C_{i},C_{j})}}}{\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{i})}}+\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{j})}}}&amp;lt;/math&amp;gt; - относительное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Определяется как абсолютное сходство между этой парой кластеров, нормализованное с учетом их внутреннего сходства&amp;lt;ref&amp;gt;http://studopedia.ru/7_41934_algoritm-dinamicheskoy-ierarhicheskoy-klasterizatsii-CHAMELEON.html&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первый этап&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На первом этапе, согласно графо-ориентированному подходу, происходит построение графа &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; на матрице сходства объектов по принципу k ближайших соседей. Две вершины такого графа соединяет ребро, если объект, соответствующий любой из этих вершин попадает в число k наиболее близких объектов для объекта, соответствующего другой вершине из данной пары.&lt;br /&gt;
[[Файл:Снимок chameleon 2.png|2000px|thumb|Рисунок 2. Объекты в пространстве - слева, граф по принципу 1-го ближайшего соседа - по центру, граф по принципу 3-х ближайших соседей - справа ]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Второй этап&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Алгоритм разделяет полученный граф на множество сравнительно малых подграфов &amp;lt;math&amp;gt;K= \{K_{i}\}&amp;lt;/math&amp;gt;. Разделение происходит последовательно. На каждом шаге выбирается подграф, содержащий наибольшее число вершин. Этот граф разделяется на два подграфа так, что разделитель ребер графа минимален и каждый из получаемых подграфов содержит не менее 25 % вершин исходного графа. Процесс разделения останавливается, когда наибольший подграф содержит меньше некоторого заданного числа вершин. Обычно величина этого параметра задается равной значению от 1 до 5 % от числа объектов. Полученное множество связных графов считается множеством начальных кластеров, на котором требуется провести последовательное иерархическое объединение.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третий этап&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Третий этап заключается в итеративном преобразовании множества подграфов &amp;lt;math&amp;gt;K= \{K_{i}\}&amp;lt;/math&amp;gt; в множество кластеров &amp;lt;math&amp;gt;C = \{C_{i}\}&amp;lt;/math&amp;gt;. Алгоритм осуществляет агломеративную иерархическую кластеризацию на основании показателей &amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;. Существует две стратегии анализа показателей сходства. Первая подразумевает наличие некоторых пороговых значений &amp;lt;math&amp;gt;T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{RC}&amp;lt;/math&amp;gt;. В соответствии с этой стратегией, алгоритм для каждого кластера &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; проверяет, отвечают ли смежные (наиболее близкие) ему кластеры условиям:&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} \geqslant T_{RI}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;RC_{(C_{i},C_{j})} \geqslant T_{RC}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если более одного смежного кластера отвечает этим условиям, то алгоритм выбирает для объединения наиболее связный кластер (граф), то есть такой кластер &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;, с которым у кластера &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; получается наибольшая абсолютная взаимная связность. По завершению прохода по всем кластерам, созданные таким образом пары объединяются. Параметры &amp;lt;math&amp;gt;T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{RC}&amp;lt;/math&amp;gt; могут использоваться для изменения характеристик получаемых кластеров.&lt;br /&gt;
&lt;br /&gt;
Вторая стратегия заключается в использовании специальной функции, объединяющей понятия относительной взаимной связности и относительного взаимного сходства. На каждом шаге выбираются те кластеры для объединения, которые максимизируют эту функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}*RC_{(C_{i},C_{j})}^\alpha&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; выбирается пользователем. Если &amp;lt;math&amp;gt;\alpha &amp;gt; 1 &amp;lt;/math&amp;gt;, то алгоритм придает большее значение относительному взаимному сходству, а если &amp;lt;math&amp;gt;\alpha &amp;lt;  1 &amp;lt;/math&amp;gt;, то большее значение имеет относительная взаимная связность.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычисляемые данные&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U = (u_1, u_2, ..., u_n)&amp;lt;/math&amp;gt; - n-мерный вектор, где &amp;lt;math&amp;gt;u_i \in N_{[C]}&amp;lt;/math&amp;gt; - порядковый номер кластера, к которому принадлежит вершина i исходного множества V.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Алгоритм имеет три вычислительных ядра, по одному на каждый этап.&lt;br /&gt;
&lt;br /&gt;
На первом этапе вычислительным ядром является процесс нахождения &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей для каждой вершины, который заключается в анализе матрицы расстояний.&lt;br /&gt;
&lt;br /&gt;
На втором этапе вычислительным ядром является процесс поиска подходящего разбиения очередного подграфа на два графа с минимизацией разделителя ребер графа.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе вычислительным ядром является расчет величин &amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; для каждой пары смежных кластеров.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Как уже рассказывалось ранее, алгоритм включает в себя три основных этапа, каждый из которых, по сути, является отдельным алгоритмом. Этапы выполняются последовательно относительно друг друга. &lt;br /&gt;
&lt;br /&gt;
Макрооперация на первом этапе - процедура нахождения &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей, на втором - процедура разбиения наибольшего подграфа на два графа, на третьем - процедура вычисления показателей сходства, на основе которых принимается решение о слиянии подграфов в кластер.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первый этап&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
 int n; //количество вершин&lt;br /&gt;
 int k; //количество соседей&lt;br /&gt;
 int A[n][n] //матрица расстояний&lt;br /&gt;
 int D[k] //массив вершин-соседей&lt;br /&gt;
 int E[n][n] //матрица ребер графа &amp;lt;math&amp;gt;G_{2} = (K, E_{2})&amp;lt;/math&amp;gt;&lt;br /&gt;
 for i=1 to n-1 do&lt;br /&gt;
     {&lt;br /&gt;
       for j=i+1 to n do &lt;br /&gt;
            {&lt;br /&gt;
               //заполняем массив вершин-соседей вставляя новые вершины, если они ближе, чем самая далекая из массива. &lt;br /&gt;
            }&lt;br /&gt;
       for j=0 to k do &lt;br /&gt;
            {&lt;br /&gt;
               //для каждой вершины соседа заполняем соответствующую ячейку из матрицы E[n][n] единицей&lt;br /&gt;
            }&lt;br /&gt;
   &lt;br /&gt;
     }&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Второй этап&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 1. Все связные подграфы в отсортированном порядке помещаются в список;&lt;br /&gt;
 2. Берем наибольший элемент списка, вычисляем количество его вершин. Если число вершин &amp;gt; l, то идем в пункт 3. Иначе в пункт 5;&lt;br /&gt;
 3. Разбиваем подграф на два подграфа таким образом, чтобы разделитель ребер графа был минимален и каждый из получаемых подграфов содержал не менее 25 % вершин исходного графа;&lt;br /&gt;
 4. Добавляем полученные элементы в список подграфов таким образом, чтобы список оставался отсортированным. Идем в пункт 2;&lt;br /&gt;
 5. Нашли искомое множество подграфов math&amp;gt;K = \{K_{i}\}&amp;lt;/math&amp;gt;;&lt;br /&gt;
 6. Создаем и заполняем матрицу матрицу расстояний для графа &amp;lt;math&amp;gt;G_{2} = (K, E_{2})&amp;lt;/math&amp;gt;. Получаем искомый набор рёбер &amp;lt;math&amp;gt;E_{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третий этап&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 1. Заводим два массива: а) массив кластеров C, полученных на предыдущем этапе, б) массив объединений кластеров F, которые мы будем получать на этом этапе;&lt;br /&gt;
 2. Берем следующий кластер &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; (на первом шаге - первый). Если все кластеры просмотрены, идем в пункт 6;&lt;br /&gt;
 3. Берем следующий кластер &amp;lt;math&amp;gt;C_{j}, i&amp;lt;&amp;gt;j&amp;lt;/math&amp;gt;. Если все кластеры просмотрены, то идем в пункт 2;&lt;br /&gt;
 4. Проверяем смежность &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;. Если не смежны, то возвращаемся в пункт 3. Если смежны, идем в пункт 5;&lt;br /&gt;
 5. Рассчитываем показатели &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;. Проверяем, удовлетворяют ли найденные значения условиям:&lt;br /&gt;
 *&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} \geqslant T_{RI}&amp;lt;/math&amp;gt;&lt;br /&gt;
 *&amp;lt;math&amp;gt;RC_{(C_{i},C_{j})} \geqslant T_{RC}&amp;lt;/math&amp;gt;&lt;br /&gt;
 Если нет, то идем в пункт 3. Иначе если смежность пары кластеров выше, чем у предыдущих найденных, то &amp;lt;math&amp;gt;F_{i}=j&amp;lt;/math&amp;gt;. Идем в пункт 3;&lt;br /&gt;
 6. Если в списке F есть хотя бы одна пара кластеров для слияния, то выполняем слияние, обновляем массив кластеров и массив объединений кластеров. Идем в пункт 2.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
1) Для построения графа на основе метода k ближайших соседей для n вершин требуется &amp;lt;math&amp;gt;O(n log(n))&amp;lt;/math&amp;gt; операций &amp;lt;ref&amp;gt;J.H. Friedman, J.L. Bentley, and R.A. Finkel. An algorithm for ﬁnding best matches in logarithmic expected time. ACM Transactions on Mathematical Software, 3:209–226, 1977.&amp;lt;/ref&amp;gt;; для наборов данных большой размерности эта величина составляет &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; операций &amp;lt;ref&amp;gt;S. Berchtold, C. Bohm, D. Keim, and H.-P. Kriegel. Cost model for nearest neighbor search in high dimensional data space. In Proc. of Symposium on Principles of Database Systems, Tucson, Arizona, 1997.&amp;lt;/ref&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2) Для выделения множества сравнительно малых связных подграфов требуется &amp;lt;math&amp;gt;nm&amp;lt;/math&amp;gt; операций, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - количество подграфов, полученных на втором этапе алгоритма;&lt;br /&gt;
&lt;br /&gt;
3) Формирование набора кластеров, на основе множества подграфов, полученных на предыдущем этапе, требует &amp;lt;math&amp;gt;m^2log(m)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Итого, для n вершин получаем последовательную сложность &amp;lt;math&amp;gt;O(n log(n)+nm+m^2log(m))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[File:СК.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Первый этап работы алгоритма можно распределить между &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; процессами. На вход каждому процессу подается своя вершина, далее происходит процедура поиска &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей. Параллельная сложность составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Второй и третий этапы можно распараллелить частично.&lt;br /&gt;
&lt;br /&gt;
Второй этап распараллеливается путем подачи на вход свободному процессу очередного подграфа для разделения. Число итераций оценивается в &amp;lt;math&amp;gt;O(n log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Третий этап делится на расчет показателей схожести и соединение подграфов в кластеры. Процесс расчета показателей можно распараллелить, подавая каждому процессу на вход пару подграфов. Сложность этого процесса оценивается как &amp;lt;math&amp;gt;m log(m)&amp;lt;/math&amp;gt;. Соединение подграфов в кластеры выполняется последовательно, сложность процесса составляет &amp;lt;math&amp;gt;m^2 log(m)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом получаем общую параллельную сложность алгоритма: &amp;lt;math&amp;gt;O(n+ n log(n) + m^2 log (m) + m log(m))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1. Симметрическая матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; расстояний между элементами данных размера &amp;lt;math&amp;gt;n\times n&amp;lt;/math&amp;gt; с нулями на главной диагонали  (&amp;lt;math&amp;gt;a_{ii}= 0, i = 1, \ldots, N&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество ближайших соседей для вершин (рекомендуемое значение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; от 5 до 20 в зависимости от количества анализируемых объектов).&lt;br /&gt;
&lt;br /&gt;
3. &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt; - наименьшее число вершин, которое может содержать наибольший подкластер на 2-м этапе . Величина этого параметра варьируется от 1 до 5 % от общего числа объектов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{n (n - 1)}{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;n&amp;lt;/math&amp;gt; чисел &amp;lt;math&amp;gt;u_{1}, u_{2}, \ldots, u_{N}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;u_{i}&amp;lt;/math&amp;gt; - целое число, соответствующее кластеру &amp;lt;math&amp;gt;i&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;n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Соотношение последовательной и параллельной сложности на первом этапе является &#039;&#039;линейным&#039;&#039; для больших наборов данных и логарифмическим для n вершин. На втором этапе сложно оценить это соотношение - оно сильно зависит от параметров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt;. На третьем этапе соотношение равно &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Оценка &#039;&#039;вычислительной мощности&#039;&#039; выглядит следующим образом:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;                  для 1-го этапа;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O(\frac{nm}{n^2+m^2})&amp;lt;/math&amp;gt;   для 2-го этапа;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O(\frac{m^2log(m)}{m^2+n})&amp;lt;/math&amp;gt; для 3-го этапа;&lt;br /&gt;
&lt;br /&gt;
Алгоритм является &#039;&#039;недетерминированным&#039;&#039;. Хотя 1-й этап детерминирован, на 2-ом этапе могут быть найдены различные разбиения на подграфы, что повлияет и на итоговый результат работы алгоритма.&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Масштабируемость исследовалась на основе работы, презентующей пробную версию параллельной реализации алгоритма для различного числа потоков&amp;lt;ref&amp;gt;http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf&amp;lt;/ref&amp;gt;.&lt;br /&gt;
График показывает, что эффективность и скорость работы алгоритма растут с увеличением объема входных данных. Однако, в упомянутой выше работе отмечается, что эффективность алгоритма падает с ростом количества потоков.&lt;br /&gt;
&lt;br /&gt;
[[File:Масштабирование.png|700px|center]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Последовательная реализация алгоритма доступна по ссылке &amp;lt;ref&amp;gt;http://read.pudn.com/downloads36/sourcecode/math/115779/chameleon/chameleon.c__.htm&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
CHAMELEON можно реализовать с использованием графовых библиотек METIS &amp;lt;ref&amp;gt;http://glaros.dtc.umn.edu/gkhome/metis/metis/overview&lt;br /&gt;
&amp;lt;/ref&amp;gt;, hMETIS &amp;lt;ref&amp;gt;http://glaros.dtc.umn.edu/gkhome/metis/hmetis/overview&amp;lt;/ref&amp;gt; и RANN &amp;lt;ref&amp;gt;https://cran.r-project.org/web/packages/RANN/index.html&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Существует исследование, связанное с реализацией CHAMELEON посредством технологии OpenMP, оно было рассмотрено в пункте 2.4.&lt;br /&gt;
&lt;br /&gt;
Параллельных реализаций алгоритма CHAMELEON не найдено.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Kronberg</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:%D0%90%D1%80%D1%82%D0%B5%D0%BC_%D0%A2%D0%B0%D1%80%D0%B0%D0%BD/%D0%93%D1%80%D0%B0%D1%84%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_CHAMELEON&amp;diff=19481</id>
		<title>Участник:Артем Таран/Графо-ориентированный алгоритм кластеризации CHAMELEON</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:%D0%90%D1%80%D1%82%D0%B5%D0%BC_%D0%A2%D0%B0%D1%80%D0%B0%D0%BD/%D0%93%D1%80%D0%B0%D1%84%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_CHAMELEON&amp;diff=19481"/>
		<updated>2016-11-29T14:31:41Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Algoman|Kronberg}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Графо-ориентированный алгоритм кластеризации CHAMELEON&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^2+nm+m^2log(m))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n + log(n) + mlog(m) + m^2log(m))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;\frac{n (n - 1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Статью подготовили: [[Участник:Артем_Таран|Артём Таран Иванович]] (разделы 1.1 - 1.10, 2.7), [[Участник:ShimchikN|Шимчик Никита Владимирович]] (разделы 1.1 - 1.10, 2.7).&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Кластеризация — многомерная статистическая процедура, выполняющая сбор данных, содержащих информацию о выборке объектов, и затем упорядочивающая объекты в сравнительно однородные группы (кластеры) с минимизацией межкластерного сходства и максимизацией внутрикластерного сходства.&lt;br /&gt;
&lt;br /&gt;
Графо-ориентированный алгоритм кластеризации CHAMELEON&amp;lt;ref name=&amp;quot;rt&amp;quot;&amp;gt;http://www-users.cs.umn.edu/~han/dmclass/chameleon.pdf&amp;lt;/ref&amp;gt; - алгоритм, который измеряет сходство двух кластеров на основе&lt;br /&gt;
динамической модели. В процессе кластеризации два кластера объединяются, только если показатели взаимосвязанности и близости между двумя кластерами являются высокими по отношению к показателям внутренней взаимосвязанности кластеров и близости элементов внутри этих кластеров. Процесс слияния с использованием динамической модели, использующийся в данном алгоритме облегчает открытие&lt;br /&gt;
природных и однородных кластеров. Методология динамического моделирования кластеров, использующаяся в CHAMELEON,&lt;br /&gt;
применима ко всем типам данных, для которых может быть построена матрица подобия.&lt;br /&gt;
&lt;br /&gt;
Следует отметить, что конечное число кластеров не задаётся явным образом и зависит как от выбора параметров алгоритма, так и от внутренней структуры входных данных.&lt;br /&gt;
&lt;br /&gt;
Алгоритм кластеризации CHAMELEON включает в себя три этапа:&lt;br /&gt;
&lt;br /&gt;
1) Построение графа, путём добавления рёбер по принципу k ближайших соседей.&lt;br /&gt;
&lt;br /&gt;
2) Выделение множества сравнительно малых связных подграфов.&lt;br /&gt;
&lt;br /&gt;
3) Формирование набора кластеров, на основе множества подграфов, полученных на предыдущем этапе.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Графы k ближайших соседей.jpg|1000px|thumb|center|Графы k ближайших соседей: 1) объекты в пространстве, 2) граф по принципу 1-го ближайшего соседа, 3) граф по принципу 2-го ближайшего соседа, 4) граф по принципу 3-х ближайших соседей&amp;lt;ref name=&amp;quot;rt&amp;quot;&amp;gt;http://www-users.cs.umn.edu/~han/dmclass/chameleon.pdf&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&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;V = \{v_{i}\}&amp;lt;/math&amp;gt; в метрическом пространстве, которое может быть задано в виде треугольной матрицы расстояний &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n\times n&amp;lt;/math&amp;gt;; натуральное число &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, используемое в качестве параметра алгоритма поиска k ближайших соседей.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Промежуточный результат после первого этапа&#039;&#039;&#039;: граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt;, полученный путём соединения каждой точки с её &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайшими соседями.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Промежуточный результат после второго этапа&#039;&#039;&#039;: разбиение множества &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на набор &amp;lt;math&amp;gt;K = \{K_{i}\}&amp;lt;/math&amp;gt; попарно непересекающихся связных подмножеств, из которого образуется взвешенный граф &amp;lt;math&amp;gt;G_{2} = (K, E_{2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Промежуточный результат после третьего этапа&#039;&#039;&#039;: итоговое разбиение множества вершин графа &amp;lt;math&amp;gt;G_{2}&amp;lt;/math&amp;gt; на набор кластеров &amp;lt;math&amp;gt;C = \{C_{i}\}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Вычисляемые данные&#039;&#039;&#039;: n-мерный вектор &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;v_{i}&amp;lt;/math&amp;gt;), показывающий отображение исходного множества точек на набор кластеров &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Определения ====&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Абсолютная взаимная связность между парой кластеров &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;&#039;&#039; определяется как сумма весов ребер, соединяющих вершины, принадлежащие &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; с вершинами из &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;. Эти ребра, фактически, входят в разделитель ребер графа, состоящего из кластеров &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;, и разбивающим его на подграфы &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;. Обозначается эта величина как&lt;br /&gt;
&amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;. Внутреннюю связность &amp;lt;math&amp;gt;EC_{C_{i}}&amp;lt;/math&amp;gt; кластера &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; можно вычислить как сумму ребер, входящих в разделитель ребер, разбивающий &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; на два совершенно равных подграфа.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Относительная взаимная &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; связность  пары кластеров &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;&#039;&#039; определяется формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} = \frac{2*|EC_{(C_{i},C_{j})}|}{|EC_{C_{i}}|+|EC_{C_{j}}|}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учитывая значения относительной взаимной связности, можно добиться получения кластеров разного размера, формы и плотности, то есть общего внутреннего сходства.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Абсолютное взаимное сходство между парой кластеров &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;&#039;&#039; подсчитывается как среднее сходство между соединенными вершинами, принадлежащими &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt; соответственно. Эти соединения обусловлены предыдущим разбиением общего графа, полученного на матрице сходства.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Относительное взаимное сходство &amp;lt;math&amp;gt;RC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; между парой кластеров &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;&#039;&#039; определяется как абсолютное сходство между этой парой кластеров, нормализованное с учетом их внутреннего сходства и вычисляется по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}= \frac{S_{EC_{(C_{i},C_{j})}}}{\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{i})}}+\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{j})}}}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;S_{EC_{(C_{i})}}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S_{EC_{(C_{i})}}&amp;lt;/math&amp;gt; - средние веса ребер (значения сходства), принадлежащих разделителю ребер кластеров &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt; соответственно,&lt;br /&gt;
и &amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt; - средний вес ребер, соединяющих вершины &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;, причем ребра принадлежат разделителю ребер &amp;lt;math&amp;gt;EC_{(C_{i})}&amp;lt;/math&amp;gt;, определенному ранее.&lt;br /&gt;
&lt;br /&gt;
Далее, алгоритм применяет агломеративную иерархическую кластеризацию с использованием вышеозначенных показателей. Причем существует две стратегии их учета. Первая подразумевает наличие некоторых пороговых значений &amp;lt;math&amp;gt;T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{RC}&amp;lt;/math&amp;gt;. В соответствии с этой стратегией, алгоритм для каждого кластера &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; проверяет, отвечают ли смежные (наиболее близкие) ему кластеры условиям:&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} \geqslant T_{RI}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;RC_{(C_{i},C_{j})} \geqslant T_{RC}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если более одного смежного кластера отвечает этим условиям, то алгоритм выбирает для объединения наиболее связный кластер (граф), то есть такой кластер &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt;, с которым у кластера &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; получается наибольшая абсолютная взаимная связность. По завершению прохода по всем кластерам, созданные таким образом пары объединяются. Параметры &amp;lt;math&amp;gt;T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{RC}&amp;lt;/math&amp;gt; могут использоваться для изменения характеристик получаемых кластеров.&lt;br /&gt;
&lt;br /&gt;
Вторая стратегия заключается в использовании специальной функции, объединяющей понятия относительной взаимной связности и относительного взаимного сходства. На каждом шаге выбираются те кластеры для объединения, которые максимизируют эту функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}*RC_{(C_{i},C_{j})}^\alpha&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; выбирается пользователем. Если &amp;lt;math&amp;gt;\alpha &amp;gt; 1 &amp;lt;/math&amp;gt;, то алгоритм придает большее значение относительному взаимному сходству, а если &amp;lt;math&amp;gt;\alpha &amp;lt;  1 &amp;lt;/math&amp;gt;, то большее значение имеет относительная взаимная связность. Разумеется, данный подход позволяет получить полную дендрограмму кластерного анализа&amp;lt;ref name=&amp;quot;rty&amp;quot;&amp;gt;http://studopedia.ru/7_41934_algoritm-dinamicheskoy-ierarhicheskoy-klasterizatsii-CHAMELEON.html&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На первом этапе работы алгоритма вычислительным ядром можно считать процесс нахождения k ближайших соседей для каждой отдельной вершины.&lt;br /&gt;
&lt;br /&gt;
На втором этапе работы алгоритма вычислительным ядром является поиск разбиения графа на два подграфа с минимизацией разделителя рёбер графа.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе работы алгоритма вычислительным ядром является расчёт &amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;RC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; для пары смежных кластеров, а также их слияние.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм CHAMELEON выполняется в 3 этапа, последовательных относительно друг друга. Каждый этап соответствует отдельному алгоритму. &lt;br /&gt;
&lt;br /&gt;
* Макрооперацией на первом этапе является процедура выделения k ближайших соседей.&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;
  integer n; // Количество точек.&lt;br /&gt;
  integer k; // Параметр k&lt;br /&gt;
  integer A[n][n]; // Верхняя треугольная матрица расстояний между точками.&lt;br /&gt;
  &lt;br /&gt;
  integer E[n][n]; // Верхняя треугольная матрица рёбер графа G - изначально инициализирована нулями.&lt;br /&gt;
  foreach (integer i in (1,n-1) ) {&lt;br /&gt;
    integer Neighbours[k]; // матрица, используемая хранения ближайших соседей&lt;br /&gt;
    integer countN = 0;&lt;br /&gt;
    &lt;br /&gt;
    foreach (integer j in (i+1, n) ) {&lt;br /&gt;
      // Заполняем матрицу Neighbours, добавляя новые элементы и увеличивая счётчик countN.&lt;br /&gt;
      // Когда countN станет равен k, новые элементы будут вставляться на место старых, либо отбрасываться.&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
    foreach (integer j in (0, countN) ) {&lt;br /&gt;
      E[min(i,j), max(i,j)] = 1;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return E;&lt;br /&gt;
&lt;br /&gt;
* Второй этап:&lt;br /&gt;
  [[File:Диаграмма_шаг_2.png]]&lt;br /&gt;
&lt;br /&gt;
* Третий этап:&lt;br /&gt;
  [[File:Chameleon_stage_3.png|611px]]&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
1) Построение графа на основе метода k ближайших соседей в худшем случае требует &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; операций;&lt;br /&gt;
&lt;br /&gt;
2) Для выделения множества сравнительно малых связных подграфов требуется &amp;lt;math&amp;gt;nm&amp;lt;/math&amp;gt; операций;&lt;br /&gt;
&lt;br /&gt;
3) Формирование набора кластеров, на основе множества подграфов, полученных на предыдущем этапе, требует &amp;lt;math&amp;gt;m^2log(m)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Итого, получаем последовательную сложность &amp;lt;math&amp;gt;O(n^2+nm+m^2log(m))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
  [[File:Chameleon_inforgraph.png|811px]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первый этап работы алгоритма можно распределить между &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; процессами: в этом случае каждому процессу назначается своя точка и происходит поиск &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей. Таким образом, его параллельная сложность составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На втором этапе количество задействованных процессов может варьироваться в зависимости от входных данных. В худшем случае число итераций можно оценить как &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе можно распараллелить процесс расчёта метрик сходства между кластерами, однако итоговое слияние всё равно должно выполняться последовательно.&lt;br /&gt;
Поскольку процесс сравнения кластеров возможно распараллелить, сложность 3-го этапа составляет &amp;lt;math&amp;gt;mlog(m) + m^2log(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, суммарная параллельная сложность оказывается равна &amp;lt;math&amp;gt;O(n + log(n) + mlog(m) + m^2log(m))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* Входные данные: &lt;br /&gt;
&lt;br /&gt;
1) Треугольная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math&amp;gt;n\times n&amp;lt;/math&amp;gt; (где n — количество объектов), в которой элементу &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; соответствует расстояние между i-м и j-м объектами.&lt;br /&gt;
&lt;br /&gt;
2) Количество ближайших соседей &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; для вершин (рекомендуемое значение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; от 5 до 20 в зависимости от количества анализируемых объектов).&lt;br /&gt;
&lt;br /&gt;
3) Наименьшее число вершин, которое может содержать наибольший подграф на 2-м этапе &amp;lt;math&amp;gt;l&amp;lt;/math&amp;gt;. Величина этого параметра варьируется от 1 до 5 % от общего числа объектов.&lt;br /&gt;
&lt;br /&gt;
* Объём входных данных: &amp;lt;math&amp;gt;\frac{n (n - 1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Выходные данные: n-мерный вектор &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, в котором элементу &amp;lt;math&amp;gt;v_{i}&amp;lt;/math&amp;gt; соответствует номер кластера, присвоенный i-му объекту&lt;br /&gt;
&lt;br /&gt;
* Объём выходных данных: &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первый этап алгоритма обладает хорошим ресурсом параллелизма: параллельная сложность его выполнения составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, что намного лучше последовательной оценки &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
У второго и третьего этапов алгоритма сложно полностью оценить ресурсы параллелизма. Но оба этапа возможно частично распараллелить.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычислительную мощность&#039;&#039;&#039; для разных этапов алгоритма можно оценить как:&lt;br /&gt;
&lt;br /&gt;
1 этап: &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2 этап: &amp;lt;math&amp;gt;O(\frac{nm}{n^2+m^2})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3 этап: &amp;lt;math&amp;gt;O(\frac{m^2log(m)}{m^2+n})&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;b&amp;gt;Детерминированность:&amp;lt;/b&amp;gt; Важной особенностью данного алгоритма является его недетерминированность. Несмотря на то, что 1-й этап детерминирован, алгоритм в целом является недетерминированным, так как на 2-ом этапе могут быть найдены различные разбиения на подграфы, а это повлияет и на итоговый результат работы алгоритма.&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;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Проводилось исследование масштабируемости экспериментальной версии параллельной реализации алгоритма для малого числа потоков&amp;lt;ref name=&amp;quot;tr&amp;quot;&amp;gt;http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Его результаты отражены на графиках. Из графиков видно, что ускорение и эффективность алгоритма растут с увеличением объема данных.&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что с увеличением числа потоков эффективность алгоритма падает.&lt;br /&gt;
&lt;br /&gt;
  [[File:Plot cham.jpg|850px|center]]&lt;br /&gt;
 &lt;br /&gt;
 [[File:Cham_speedup.png]] [[File:Cham_eff.png]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует несколько последовательных реализаций алгоритма. С одной из них можно ознакомиться по ссылке &amp;lt;ref name=&amp;quot;tra&amp;quot;&amp;gt;http://www.codeforge.com/article/192925&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможна реализация алгоритма CHAMELEON с использованием графовых библиотек, как, например METIS &amp;lt;ref name=&amp;quot;tru&amp;quot;&amp;gt;http://glaros.dtc.umn.edu/gkhome/metis/metis/overview&amp;lt;/ref&amp;gt; , hMETIS &amp;lt;ref name=&amp;quot;tri&amp;quot;&amp;gt;http://glaros.dtc.umn.edu/gkhome/metis/hmetis/overview&amp;lt;/ref&amp;gt; и RANN &amp;lt;ref name=&amp;quot;tro&amp;quot;&amp;gt;https://cran.r-project.org/web/packages/RANN/index.html&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельных реализаций алгоритма CHAMELEON найдено не было. Однако существует исследование, связанное с реализацией CHAMELEON посредством технологии OpenMP, с которым можно ознакомиться здесь &amp;lt;ref name=&amp;quot;tr&amp;quot;&amp;gt;http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Ruslanlazovskiy/CHAMELEON&amp;diff=19480</id>
		<title>Участник:Ruslanlazovskiy/CHAMELEON</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:Ruslanlazovskiy/CHAMELEON&amp;diff=19480"/>
		<updated>2016-11-29T14:29:53Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Algoman|Kronberg}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = CHAMELEON&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n*m + n*log{n} + m*log{m})&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; - этап 1. &amp;lt;math&amp;gt;O(q)&amp;lt;/math&amp;gt; - отдельная итерация этапа 3.&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; - этап 1. &amp;lt;math&amp;gt;O(q)&amp;lt;/math&amp;gt; - отдельная итерация этапа 3.&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2 / 2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Авторы статьи: &lt;br /&gt;
[https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ruslanlazovskiy &amp;lt;b&amp;gt;Лазовский Р. А.&amp;lt;/b&amp;gt;] (разделы 1.1 - 1.4 , 1.7 , 1.8),&lt;br /&gt;
[https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%9C%D1%83%D1%81%D1%82%D0%B0%D1%84%D0%B0%D0%B5%D0%B2_%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D0%BD_%D0%AD%D0%BB%D1%8C%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%87 &amp;lt;b&amp;gt;Мустафаев Э. Э.&amp;lt;/b&amp;gt;] (разделы 1.5 , 1.6 , 1.9 , 1.10 , 2.2)&lt;br /&gt;
&lt;br /&gt;
==Свойства и структура алгоритма==&lt;br /&gt;
===Общее описание алгоритма===&lt;br /&gt;
CHAMELEON – алгоритм динамической иерархической кластеризации графа, то есть процесса разбиения графа на несколько подграфов-кластеров таким образом, что данные внутри одного кластера имеют максимально схожи, а данные в разных кластерах — максимально различны. В классических алгоритмах кластеризации схожесть объектов определяется с помощью некоторой метрики (простейший пример — евклидова метрика). Не следует путать задачу кластеризации с задачей классификации: в последней характеристики объекта, необходимые для попадания в тот или иной класс, известны заранее, в рассматриваемой же задаче они становятся известны в процессе решения. Первоначально алгоритм был описан в статье&amp;lt;ref&amp;gt;CHAMELEON: A Hierarchical Clustering Algorithm Using Dynamic Modeling. George Karypis, Eui-Hong Han, Vipin Kumar. IEEE Computer 32(8): 68-75, 1999&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм CHAMELEON работает в 3 этапа. На первом этапе исходное множество объектов организуется в граф по принципу k ближайших соседей. На втором этапе этого граф разбивается на достаточно малые подграфы-кластеры. На третьем этапе происходит агломеративная иерархическая кластеризация полученных подграфов в соответствии с одной из выбранных метрик (возможные метрики будут подробно описаны далее). Число получаемых в итоге кластеров в явном виде не задаётся.&lt;br /&gt;
&lt;br /&gt;
===Математическое описание алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Исходные данные:&amp;lt;/b&amp;gt; Первоначально исходными данными алгоритма являются &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов длины &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, каждый из которых соответствует одному объекту. На основании значений элементов этих векторов строится матрица смежности размера &amp;lt;math&amp;gt;n * n&amp;lt;/math&amp;gt;, возможно сокращение требуемого объема памяти до &amp;lt;math&amp;gt;(n*(n - 1))/ 2&amp;lt;/math&amp;gt; в соответствии с выбранной метрикой. К исходным данным также относится набор параметров. Это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество ближайших соседей на первом этапе алгоритма, &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - отношение количества вершин в наибольшем подграфе по отношению к общему числу объектов на втором этапе алгоритма, а также параметры &amp;lt;math&amp;gt;T_{RI}, T_{RI}&amp;lt;/math&amp;gt; - пороговые значения, после которых прекращается агломеративная кластеризация на третьем этапе. Следует особо отметить, что число получаемых в итоге кластеров заранее не известно, хотя на него и можно повлиять изменением значений перечисленных параметров.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Первый этап алгоритма:&amp;lt;/b&amp;gt; На первом этапе на основании матрицы смежности строится граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа (т.е. объекты), а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество ребер графа (т.е. построенные связи между ближайшими соседями). Указанный граф не обязательно является связным.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Второй этап алгоритма:&amp;lt;/b&amp;gt; Граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; итеративно разбивается на множество подграфов &amp;lt;math&amp;gt;G_{i} = (V_{i}, E_{i}), i = 1 .. m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \cup V_{i} = V, i = 1 .. m&amp;lt;/math&amp;gt;. Параметр &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; явно не задаётся, выполнение этого этапа алгоритма прекращается по выполнении описанных далее условий. На каждой итерации выбирается &amp;lt;math&amp;gt;G_{b}&amp;lt;/math&amp;gt; с числом вершин, наибольшим среди всех подграфов, имеющихся на данной итерации. Этот граф разбивается на 2 подграфа &amp;lt;math&amp;gt;G_{bi}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;G_{bj}&amp;lt;/math&amp;gt; таких, что 1) &amp;lt;math&amp;gt;min(V_{bi},V_{bj}) &amp;gt;= 0.25 * V_{b}&amp;lt;/math&amp;gt; (имеется ввиду количество вершин) и 2) &amp;lt;math&amp;gt;\sum{E_{kl}}, E_{kl} = (v_{k}, v_{l}), v_{k} \in V_{bi} , v_{l} \in V_{bj} &amp;lt;/math&amp;gt; минимальна среди всех разбиений, удовлетворяющих 1). Итеративный процесс прекращается, когда &amp;lt;math&amp;gt;\sum{v_{b}} &amp;lt;= q * \sum{v} , v_{b} \in V_{b} , e \in E&amp;lt;/math&amp;gt; (имеется ввиду количество вершин). Здесь &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - задаваемый параметр, на практике он варьируется от &amp;lt;math&amp;gt;0.01&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.05&amp;lt;/math&amp;gt;. Полученные подграфы называются малыми кластерами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Третий этап алгоритма:&amp;lt;/b&amp;gt; Множество кластеров &amp;lt;math&amp;gt;G_{i} = (V_{i}, E_{i}), i = 1 .. m&amp;lt;/math&amp;gt; итеративно преобразуется в множество кластеров &amp;lt;math&amp;gt;G_{j} = (V_{j}, E_{j}), j = 1 .. l&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l &amp;lt;= m&amp;lt;/math&amp;gt;. Для этого вводятся следующие понятия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; - абсолютная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Это суть сумма весов ребер, соединяющих вершины, принадлежащие &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; c вершинами из &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt;. &amp;lt;math&amp;gt;EC_{(C_{i},C_{i})}&amp;lt;/math&amp;gt; вычисляется как сумма ребер, входящих в разделитель, разбивающий &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; на два равных подграфа.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt; - абсолютное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Это суть среднее сходство между соединенными вершинами, принадлежащими &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt; соответственно. Соединения обусловлены разбиением общего графа, полученного на первом этапе алгоритма.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} = \frac{2*|EC_{(C_{i},C_{j})}|}{|EC_{C_{i}}|+|EC_{C_{j}}|}&amp;lt;/math&amp;gt; - относительная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}= \frac{S_{EC_{(C_{i},C_{j})}}}{\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{i})}}+\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{j})}}}&amp;lt;/math&amp;gt; - относительное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На основании этих показателей осуществляется агломеративная иерерхическая кластеризация. Существует две стратегии анализа показателей сходства. В первом случае для каждой пары кластеров &amp;lt;math&amp;gt;C{i}, C{j}&amp;lt;/math&amp;gt; проверяется истинность выражений &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} &amp;gt;= T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;RC_{(C_{i},C_{j})} &amp;gt;= T_{RC}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{RI}, T_{RI}&amp;lt;/math&amp;gt; - заранее заданные пороговые значения. Если для некоторого &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; таких &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt; несколько, то конкретная пара определяется по максимуму значения &amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;. Во втором случае выбираются пары кластеров, максимизирующие функцию вида &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}*RC_{(C_{i},C_{j})}^\alpha&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; - заданный пользователем параметр. В обоих случаях процесс останавливается либо когда на очередной итерации не находится подходящих пар кластеров, либо когда остаётся только один кластер.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Вычисляемые данные:&amp;lt;/b&amp;gt; В процессе агломеративной кластеризации для всех объектов, т.е. всех вершин всех кластеров указывается индекс &amp;lt;math&amp;gt;j&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;
В алгоритме 3 основных вычислительных ядра, по одному на каждый этап алгоритма.&lt;br /&gt;
&lt;br /&gt;
На первом этапе алгоритма основная вычислительная нагрузка приходится на анализ матрицы смежности, необходимого для построения первоначального графа &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt;, так как для каждого объекта необходимо перебрать всех его соседей и выделить &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших по заданной метрике.&lt;br /&gt;
&lt;br /&gt;
На втором этапе наибольшее время занимает поиск подходящего разбиения очередного подграфа. В элементарном варианте этой процедуры необходимо перебрать все комбинации вершин в этом подграфе, удовлетворяющие условию 1) из математического описания алгоритма, и для каждой из них подсчитать значение, используемое в 2). &lt;br /&gt;
&lt;br /&gt;
На третьем этапе алгоритма вычислительное ядро полностью совпадает с самим этапом, то есть итерационным процессов агломеративной иерархической кластеризации, включающем в себя подсчёт выбранной метрики для каждой пары кластеров, имеющихся на очередной итерации.&lt;br /&gt;
&lt;br /&gt;
===Макроструктура алгоритма===&lt;br /&gt;
Как уже понятно из предыдущих разделов описания, алгоритм CHAMELEON выполняется в 3 этапа, последовательных относительно друг друга. Каждый этап, строго говоря, соответствует отдельному алгоритму. В рамках этого описания макрооперациями будут считаться процедуры разбиения наибольшего подграфа на каждой итерации второго этапа алгоритма, а также процедура вычисления метрики для принятия решения об объединении кластеров на каждой итерации третьего этапа.&lt;br /&gt;
&lt;br /&gt;
===Схема реализации последовательного алгоритма===&lt;br /&gt;
&lt;br /&gt;
Схема представлена в виде C++-подобного псевдокода, макрооперации в алгоритме выделены в соответствии с предыдущим разделом описания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // First stage.&lt;br /&gt;
 int k // Number of neighbors.&lt;br /&gt;
 float v[n][p] // Initial vectors.&lt;br /&gt;
 float M[n][n] // Adjacency matrix.&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; ++i) {&lt;br /&gt;
  for (int l = 0; l &amp;lt; n; ++l) {&lt;br /&gt;
   M[i][l] = Compute_metric(v[i],v[l]);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
 Graph max_graph // Structure/class that represents a graph.&lt;br /&gt;
 Neighbor_list list // Here neighbors are initially stored.&lt;br /&gt;
 max_graph.add_vertices // Vertices ~ initial vectors.&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; ++i) {&lt;br /&gt;
  for (int l = i + 1; i &amp;lt; n; ++i) { // &amp;quot;Smart&amp;quot; version - we do not repeat same vertex combinations.&lt;br /&gt;
   if (neighbor_number &amp;lt; k) {&lt;br /&gt;
     add_neighbor(v[i],v[l]) // Just add.&lt;br /&gt;
   }else{&lt;br /&gt;
     substitute_neighbor(v[i],v[l]) // Check whether a new element is closer than some of the oldies.&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  max_graph.add_neighbors // Looks at neighbor lists of current vector and adds them&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // Second stage. &lt;br /&gt;
 float percent // Determines when to stop splitting graphs. &lt;br /&gt;
 Graph graphs[n] // GRAPHS WILL MOST LIKELY TAKE LESS SPACE, IT&#039;S JUST THE UPPER LIMIT. &lt;br /&gt;
 max_graph = find_max_graph() // Biggest graph by number of vertices available. &lt;br /&gt;
 while (graph_size(max_graph) &amp;gt; n * percent) {&lt;br /&gt;
   Split_graph(max_graph); // Split the graph - a macro operation that represents the second phase.&lt;br /&gt;
 } &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // Third stage. &lt;br /&gt;
 Graph graphs[m] // That&#039;s how many graphs we got. m &amp;lt;= n. &lt;br /&gt;
 bool pairs_exist // Check if the&#039;re pairs of clusters to unite.&lt;br /&gt;
 while (num_graphs &amp;gt; 1 &amp;amp;&amp;amp; pairs_exist){&lt;br /&gt;
  pairs_exist = false // So we don&#039;t repeat if it fails.&lt;br /&gt;
  for (int i = 0; i &amp;lt; m; ++i) {&lt;br /&gt;
   for (int l = i + 1; l &amp;lt; m; ++l) { // Also a &amp;quot;Smart&amp;quot; version.&lt;br /&gt;
     check_pair(pairs_exist, graphs[i], graphs[l]); // Check whether graphs are similar enough by some metrics.&lt;br /&gt;
     if (pairs_exist) { Unite_graphs(graphs[i], graphs[l]) }; // Pairs_exist is set to true if something&#039;s found.&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Последовательная сложность алгоритма===&lt;br /&gt;
&lt;br /&gt;
Общая временная сложность последовательного варианта алгоритма CHAMELEON определяется как &amp;lt;math&amp;gt;O(nm + nlog{n} + mlog{m})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число исходных объектов, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число малых кластеров, полученных после первого этапа работы. В случае &amp;quot;предельного&amp;quot; разбиения &amp;lt;math&amp;gt;m = n&amp;lt;/math&amp;gt; эта оценка превращается в &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;. В любом случае эта оценка не учитывает необходимости построения матрицы смежности по исходному набору векторов (авторы оригинальной статьи в своих исследованиях подразумевают, что матрица смежности уже построена), что означает дополнительные &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; операций. Тогда оценка будет составлять &amp;lt;math&amp;gt;O(n^2 + nm + nlog{n} + mlog{m})&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;
Следует также обратить внимание, что это оценка по числу не сложений и умножений, а достаточно крупных операций. Например, вычисление метрики между двумя векторами рассматривается как одна операция, а она включает в себя &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;2n&amp;lt;/math&amp;gt; сложений (для вычисления квадратичной нормы разности векторов).&lt;br /&gt;
&lt;br /&gt;
===Информационный граф===&lt;br /&gt;
&lt;br /&gt;
Информационный граф дан в соответствии с макроструктурой алгоритма. Следует отметить, что имеются успешные попытки распараллелить 2 и 3 этапы алгоритма CHAMELEON (см. раздел &amp;quot;Существующие реализации&amp;quot;). Однако они включают в себя модификацию исходного алгоритма. Также возможно распараллеливание выборки пар кластеров и подсчёта их метрик внутри каждой из вершин stage_3_iter. Структура информационного графа внутри этих итераций аналогична структуре информационного графа первого этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
[[File:CHAMELEON ruslanlazovskiy.png]]&lt;br /&gt;
&lt;br /&gt;
===Ресурс параллелизма алгоритма===&lt;br /&gt;
&lt;br /&gt;
Параллельную сложность алгоритма CHAMELEON следует оценивать отдельно для каждого этапа.&lt;br /&gt;
На первом этапе алгоритм поиска &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей может быть выполнен за &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; вычислений значения метрики между двумя векторами.&lt;br /&gt;
На втором этапе итерации алгоритма выполняются строго последовательно, а распараллеливание алгоритма внутри итерации не относится к теме статьи.&lt;br /&gt;
На третьем этапе итерации также выполняются последовательно, однако возможно распараллеливание обработки пар кластеров внутри итерации. Каждая итерация может быть выполнена за &amp;lt;math&amp;gt;q - 1&amp;lt;/math&amp;gt; вычислений значения метрики между двумя кластерами, где q - количество кластеров на этой итерации.&lt;br /&gt;
&lt;br /&gt;
===Входные и выходные данные алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Входные данные:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt; - набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов длины &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число, определяющиее количество ближайших соседей, по которым строится первоначальный граф. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Выходные данные:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; - вектор длины &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, значение &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го элемента указывает на принадлежность соответствующего вектора одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
===Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Соотношение последовательной и параллельной сложности:&amp;lt;/b&amp;gt; Различные этапы алгоритма строго последовательны относительно друг друга. На первом этапе отношение последовательной сложности алгоритма к параллельной есть &amp;lt;math&amp;gt;n/2&amp;lt;/math&amp;gt;. На втором и третьем этапах различные итерации последовательны относительно друг друга. Однако внутри итераций допускается некоторый параллелизм. Например, для итераций третьего этапа в наилучшем случае отношение сложности последовательной реализации к параллельной есть &amp;lt;math&amp;gt;q/2&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - число кластеров на конкретной итерации.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Вычислительная мощность:&amp;lt;/b&amp;gt; графовые алгоритмы, вообще говоря, обладают низкой вычислительной мощностью. В случае CHAMELEON вычислительная мощность первого этапа равна &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;. Для второго и третьего этапов оценка вычислительной мощности по сложениям и умножениям затруднительна, однако в терминах макроопераций можно дать грубую оценку. Вычислительная мощность каждой итерации второго этапа - &amp;lt;math&amp;gt;1 / \sum C_n^k&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число вершин в выбранном подграфе, а &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; варьируется от &amp;lt;math&amp;gt;0.25 * n&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.5 * n&amp;lt;/math&amp;gt;. Вычислительная мощность каждой итерации третьего этапа в этих терминах - &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Детерминированность:&amp;lt;/b&amp;gt; Важной особенностью описываемого алгоритма является его недетерминированность. Условия завершения как 2-го, так и 3-го этапа позволяют обозначить CHAMELEON как итерационный алгоритм с выходом по точности. При этом детерминированность 2-го этапа алгоритма можно обеспечить указанием вырожденных условий кластеризации (такая кластеризация, очевидно, не будет иметь практического смысла), в то время как 2-ый этап является недетерминированным всегда.&lt;br /&gt;
&lt;br /&gt;
==Программная реализация алгоритма==&lt;br /&gt;
===Особенности реализации последовательного алгоритма===&lt;br /&gt;
===Локальность данных и вычислений===&lt;br /&gt;
===Возможные способы и особенности параллельной реализации алгоритма===&lt;br /&gt;
===Масштабируемость алгоритма и его реализации===&lt;br /&gt;
&lt;br /&gt;
Как можно увидеть из раздела &amp;quot;существующие реализации&amp;quot;, параллельной реализации алгоритма CHAMELEON на система с распределенной памятью найдено не было. Однако в &amp;lt;ref name = &amp;quot;exp&amp;quot;&amp;gt;Parallel Algorithm for the Chameleon Clustering Algorithm using Dynamic Modeling.  Rajnish Dashora, Harsh Bajaj, Akshat Dube, Geetha Mary .A. VIT University,Vellore. International Journal of Computer Applications (0975 – 8887) Volume 79 – No8, October 2013 &amp;lt;/ref&amp;gt;. приводится экспериментальный вариант параллельной реализации посредством OpenMP для систем с общей памятью, включающий в себя значительную модификацию исходного алгоритма, но не приводится рабочая реализация.&lt;br /&gt;
&lt;br /&gt;
В силу вышескасновного основное внимание будет уделено исследованию масштабируемости алгоритма по размеру задачи, в пределах одного узла. Далее приводятся графики производительности в Гфлопс для различных размеров задачи. Эффективность работы алгоритма убывает на всей рассматриваемой области. Темпы убывания эффективности снижаются с ростом размера задачи.&lt;br /&gt;
&lt;br /&gt;
Для исследования такой масштабируемости вширь использовалась референсная последовательная реализация алгоритма (cм. раздел &amp;quot;Существующие реализации&amp;quot;). Соответствующая программа запускалась на системе BG/P на одном вычислительном узле. Характеристики вычислительного узла, а также микропроцессорного ядра - элемента такого узла, приводятся в &amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt; Таким образом, удостоверенными экспериментальными данными являются только значения, полученные для случая 1 процесса. Значения для случаев 2 и 4 процессов являются ОЦЕНОЧНЫМИ и выводятся из результатов работы модифицированной версии алгоритма, исследуемой в &amp;lt;ref name = &amp;quot;exp&amp;quot;&amp;gt;Parallel Algorithm for the Chameleon Clustering Algorithm using Dynamic Modeling.  Rajnish Dashora, Harsh Bajaj, Akshat Dube, Geetha Mary .A. VIT University,Vellore. International Journal of Computer Applications (0975 – 8887) Volume 79 – No8, October 2013 &amp;lt;/ref&amp;gt;. К сожалению, указанной реализации авторы не предоставляют. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:CHAMELEON performance.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===Динамические характеристики и эффективность реализации алгоритма===&lt;br /&gt;
===Выводы для классов архитектур===&lt;br /&gt;
===Существующие реализации алгоритма===&lt;br /&gt;
С копией референсной реализации иерархической кластеризации, взятой с данного ресурса [http://us.codeforge.com/article/192925 Sequental], в случае неработоспособности последнего, можно ознакомиться здесь [http://pastebin.com/PhC4X0Cy Copy]&amp;lt;br/&amp;gt;&lt;br /&gt;
Последовательная реализация алгоритма CHAMELEON использовалась в статье &amp;lt;ref&amp;gt;Clustering Of Web Usage Data Using&lt;br /&gt;
Chameleon Algorithm, T.Vijaya Kumar, Dr. H.S.Guruprasad, International Journal of Innovative Research in Computer&lt;br /&gt;
and Communication Engineering, Vol. 2, Issue 6, June 2014&amp;lt;/ref&amp;gt;&lt;br /&gt;
Теоретически возможна реализация алгоритма CHAMELEON с использованием графовых библиотек, например [http://glaros.dtc.umn.edu/gkhome/metis/metis/overview METIS ], [http://glaros.dtc.umn.edu/gkhome/metis/hmetis/overview hMETIS] и [https://cran.r-project.org/web/packages/RANN/index.html RANN].&amp;lt;br/&amp;gt;&lt;br /&gt;
Параллельных реализаций алгоритма CHAMELEON в графовых библиотеках мною найдено не было. Однако существует исследование, связанное с реализацией CHAMELEON посредством технологии OpenMP, с которым можно ознакомиться здесь [http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf параллельная реализация].&lt;br /&gt;
&lt;br /&gt;
==Литература==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Краткий обзор алгоритма [http://studopedia.ru/7_41934_algoritm-dinamicheskoy-ierarhicheskoy-klasterizatsii-CHAMELEON.html|]&amp;lt;br/&amp;gt;&lt;br /&gt;
Первоначальная статья от авторов алгоритма [http://www-users.cs.umn.edu/~han/dmclass/chameleon.pdf|]&amp;lt;br/&amp;gt;&lt;br /&gt;
Исследования возможной параллелизации алгоритма[http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf|]&amp;lt;br/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:JuliaA/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=19479</id>
		<title>Участник:JuliaA/Алгоритм кластеризации с использованием представлений</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:JuliaA/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=19479"/>
		<updated>2016-11-29T14:27:20Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Основные авторы описания: &amp;lt;br&amp;gt;&lt;br /&gt;
[[Участник:JuliaA|Ю.А. Антохина]], разделы [[#Общее описание алгоритма|1.1]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]] - [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]]&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Участник:Cookie|С.А. Шатков]], разделы [[#Математическое описание алгоритма|1.2]] - [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Масштабируемость алгоритма и его реализации|2.4]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Пусть дан набор точек и заданы правила определения расстояния между ними. Задача кластеризации сгруппировать эти точки в определенное число кластеров. Члены одного кластера похожи, а члены разных классов различны.&lt;br /&gt;
&lt;br /&gt;
В алгоритме CURE применяется иерархическая кластеризация. Есть два подхода к иерархической кластеризации. В одном случае один большой кластер на каждом шаге разбивается на несколько, пока число кластеров не будет равно заданному заранее числу кластеров. В другом случае наоборот кластеры, состоящие каждый из одной точки объединяются в большие. Второй метод используется в CURE.&lt;br /&gt;
&lt;br /&gt;
Результат кластеризации зависит от того, как будут представлены кластеры. Например, средним всех точек кластера, или всеми точками кластера. Применение среднего возможно только в евклидовом пространстве. А использование всех точек неустойчиво к случайным выбросам. В CURE используется другой метод: для каждого кластера выбираются точки-представители. Выбирается постоянное число точек, которые будут представлять каждый кластер. Кластеры с наиболее похожими наборами репрезентативных точек объединяются на каждом шаге алгоритма.&lt;br /&gt;
&lt;br /&gt;
За счет использования точек-представителей алгоритм CURE устойчив к выбросам и может выделять кластеры сложной формы и различных размеров.&lt;br /&gt;
&lt;br /&gt;
Описание шагов&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1&#039;&#039;&#039;. Если все данные использовать сразу как входные для CURE, то эффективность алгоритма будет низкая, а время выполнения большим. Поэтому на первом шаге мы случайным образом выбираем часть точек которые помещаются в память, затем группируем наиболее похожие с помощью иерархического метода в заранее заданное число кластеров.&lt;br /&gt;
Дальше работаем с кластерами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2&#039;&#039;&#039;. Для каждого кластера выбираем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек-представителей, максимально удаленных друг от друга. Чисто &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; остается постоянным.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3&#039;&#039;&#039;. Объединяем кластеры с наиболее похожими наборами точек-представителей.&lt;br /&gt;
Если не достигнуто нужное число кластеров, то перейти на шаг 2.&lt;br /&gt;
Чтобы при объединении кластеров не выбирать каждый раз c точек-представителей из всех точек, мы выбираем их только из &amp;lt;math&amp;gt;2c&amp;lt;/math&amp;gt; точек объединенных кластеров.&lt;br /&gt;
&lt;br /&gt;
Выбранные точки сдвигаются на следующем шаге на &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; к центроиду кластера. Алгоритм становится основанным на методе поиска центроида при &amp;lt;math&amp;gt;\alpha = 1&amp;lt;/math&amp;gt;, и основанным на всех точках кластера при &amp;lt;math&amp;gt;\alpha = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Exampleofalgorithmparametrs1.png|600px|thumb|center|Пример работы алгоритма для разных параметров &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; [1].]]&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Пусть дана матрица &amp;lt;math&amp;gt;x \in R^{m\times n}&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;, число точек-представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt;. Каждая строка матрицы представляет собой точку в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - мерном пространстве.&lt;br /&gt;
&lt;br /&gt;
m число точек для кластеризации, n размерность.&lt;br /&gt;
&lt;br /&gt;
Расстояние &amp;lt;math&amp;gt;\rho(a,b)&amp;lt;/math&amp;gt; между точками &amp;lt;math&amp;gt;a = (a_{1},\dots,a_{n})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b = (b_{1},\dots,b_{n})&amp;lt;/math&amp;gt; будем вычислять по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rho(a,b) = \sqrt{\sum_{i=1}^n (a_{i} - b_{i})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Инициализация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Выбираются &amp;lt;math&amp;gt;\lceil \gamma m\rceil&amp;lt;/math&amp;gt; строк матрицы случайным образом. Например, можно использовать [https://ru.wikipedia.org/wiki/Reservoir_sampling Алгоритм R]. &lt;br /&gt;
&lt;br /&gt;
В основной памяти выделяется куча, часть которой используется для размещения дерева, листьями которого являются выбранные строки, соединённые ребром с вершиной дерева.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
В самом начале шага считаем каждую выбранную точку одноточечным кластером с центроидой в этой же точке.&lt;br /&gt;
&lt;br /&gt;
Далее выполняем следующие шаги 1-2, пока количество кластеров не станет равно &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Шаг 1:&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го кластера вычисляется центроида &amp;lt;math&amp;gt;\mu_{i}&amp;lt;/math&amp;gt; по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mu_{i} = \frac{1}{s_{i}}\sum_{j\in S_{i}} x^{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S_{i}&amp;lt;/math&amp;gt; - номера строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, входящие в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й кластер; &amp;lt;math&amp;gt;s_{i}&amp;lt;/math&amp;gt; - количество элементов в множестве &amp;lt;math&amp;gt;S_{i}&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;x^{j}&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-я строка матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Шаг 2:&lt;br /&gt;
&lt;br /&gt;
Выполняется поиск пары кластеров, &amp;quot;наиболее подходящих&amp;quot; для объединения. В качестве критерия выбора можно рассматривать: наименьшее расстояние между центроидами кластеров или наименьшее расстояние между элементами, входящими в рассматриваемую пару кластеров. Существуют также другие критерии выбора &amp;quot;подходящих&amp;quot; классов, например, сравнение плотности кластеров до объединения и после. Далее выполняется слияние найденных кластеров. Для дерева это означает добавление новой вершины, соединённой ребром с корнем; поддеревья кластеров становятся ветвями этой новой вершины.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге для каждого кластера определяются &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек-представителей. Для наилучшего отображения формы множества предполагается использование точек, максимально удалённых друг от друга.&lt;br /&gt;
&lt;br /&gt;
Для этого используем следующий алгоритм:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - текущее количество представителей, &amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt; - множество номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, входящие в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-й кластер, &amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt; - множество номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, назначенных представителями &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го кластера, а &amp;lt;math&amp;gt;x^{j}&amp;lt;/math&amp;gt; - вектор-строка матрицы &amp;lt;math&amp;gt;x&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;R_{k}&amp;lt;/math&amp;gt; = [] % Пустое множество&lt;br /&gt;
  a = Choose(&amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt;) % Выбор произвольного номера точки, входящей в кластер &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
  Add(&amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;,a) % Добавление точки а во множество представителей&lt;br /&gt;
  i = 1 % Текущее количество представителей кластера&lt;br /&gt;
  While (i&amp;lt;c) do % Хотим найти с представителей для кластера&lt;br /&gt;
  - rmax = 0&lt;br /&gt;
  - For ( (y &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt;) and (y &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;P_{k}&amp;lt;/math&amp;gt;) ) do % Поиск следующего представителя среди оставшихся точек кластера&lt;br /&gt;
  - - rmin = &amp;lt;math&amp;gt;\rho(x^{a}&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x^{y})&amp;lt;/math&amp;gt;&lt;br /&gt;
  - - For ( (j &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;) and (j &amp;lt;math&amp;gt;\neq&amp;lt;/math&amp;gt; a) ) do % Поиск расстояния от точки с номером y до ближайшего представителя &lt;br /&gt;
  - - - d = &amp;lt;math&amp;gt;\rho(x^{j}&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x^{y})&amp;lt;/math&amp;gt; % вычисление расстояния&lt;br /&gt;
  - - - If (d &amp;lt; rmin) then&lt;br /&gt;
  - - - - rmin = d&lt;br /&gt;
  - - - endif&lt;br /&gt;
  - - enddo&lt;br /&gt;
  - - If (rmin &amp;gt; rmax) % Поиск точки, для которой расстояние до ближайшего представителя является максимальным&lt;br /&gt;
  - - - rmax = rmin&lt;br /&gt;
  - - - b = y % искомая точка&lt;br /&gt;
  - - endif&lt;br /&gt;
  - enddo&lt;br /&gt;
  - Add(&amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;,b) % Добавление новой точки в множество представителей&lt;br /&gt;
  - i = i + 1 % текущее число представителей&lt;br /&gt;
  enddo&lt;br /&gt;
  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Для каждого представителя &amp;lt;math&amp;gt;x^{y}&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го кластера выполняется сдвиг к его центроиде &amp;lt;math&amp;gt;\mu_{k}&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; раз: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\hat{x}^{y} = \alpha\mu_{k} + (1-\alpha)x^{y}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Получаем множество &amp;lt;math&amp;gt;\hat{R}_{k}&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек, которые будут далее использоваться как представители этого кластера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге происходит присвоение неиспользованных точек (строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;) к кластерам. Для точки-строки &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычисляется расстояние до каждого представителя. Результат: строка &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; добавляется в поддерево кластера, которому принадлежит наиближайший представитель.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Результатом работы алгоритма является дерево, ветвями которого являются отдельные кластеры. Листьями кластеров являются строки из матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма представляет собой итерационное построение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров с помощью иерархического метода, вычисление их центроид &amp;lt;math&amp;gt;\mu_{k}&amp;lt;/math&amp;gt; и назначение точек-представителей. В ходе работы вычислительного ядра формируется дерево, в котором точки, выбранные на этапе &#039;&#039;&#039;Инициализации&#039;&#039;&#039; собраны в &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; поддеревьев. Точки-представители хранятся в отдельной структуре. Эти точки будут использованы для выбора кластеров для неиспользованных (на этапе &#039;&#039;&#039;Инициализации&#039;&#039;&#039;) точек после работы вычислительного ядра.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации состоит в формировании &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; кластеров из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек; число &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; задано заранее.&lt;br /&gt;
&lt;br /&gt;
На начальном этапе выбирается часть точек, для которых выполняется иерархический алгоритм кластеризации. На начальном этапе каждая точка считается отдельным кластером с центроидой в данной точке. Для каждого кластера считается расстояние для всех остальных. Похожие кластеры объединяются и их точки тоже. На каждом шаге алгоритма происходит вычисление центроиды для новообразованного кластера. Алгоритм выполняется, пока не будет достигнуто необходимое число кластеров. &lt;br /&gt;
&lt;br /&gt;
После этого происходит назначение точек-представителей для каждого кластера и их сдвиг к центроиде. &lt;br /&gt;
&lt;br /&gt;
Далее для неиспользованных точек &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; происходит вычисление расстояний до всех точек-представителей. Точка помещается в тот кластер, точка-представитель которого находится ближе остальных.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:SubCURE4.jpg|1100px|thumb|center|Схема работы алгоритма кластеризации CURE]]&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные для кластеризации - это &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; репрезентативных точек в d мерном пространстве и необходимое число &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; кластеров. Для каждой точки необходимо вычислить расстояние до другой. Сложность этого этапа &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;. Если искать ближайший кластер простым перебором, то сложность поиска будет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для каждого кластера. Но для эффективного поиска сложность составляет &amp;lt;math&amp;gt;log_2{n}&amp;lt;/math&amp;gt;. Эффективный метод описан в статье.&amp;lt;ref&amp;gt;Sudipto Guha (Stanford), Rajeev Rastogi (Bell Labs), Kyuseok Shim (Korea Institute of Technology). CURE: an efficient clustering algorithm for large databases. SIGMOD &#039;98 Proceedings of the 1998 ACM SIGMOD international conference on Management of data, pp 73-84 &amp;lt;/ref&amp;gt; Далее каждую точку необходимо вдвинуть к центроиде, сложность этой операции &amp;lt;math&amp;gt;O(2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Таким образом худшее время выполнения алгоритма &amp;lt;math&amp;gt;O(n^2 log_2{n})&amp;lt;/math&amp;gt;. Для двумерного пространства временная сложность может быть &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;. Так что сложность алгоритма не хуже чем у центроидного. То есть при такой размерности входных данных CURE выполняется за полиномиальное время.&amp;lt;math&amp;gt;~^{[1]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Инициализация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
В зависимости от алгоритма случайная выборка номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; может производится либо зависимо, либо независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Описанный в разделе иерархический алгоритм кластеризации является последовательным, так как на каждом шаге сравниваются расстояния между центроидами кластеров, некоторые из которых меняются при объединении кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Файл:Points c.png|800px|thumb|center|Последовательный информационный граф для точек-представителей]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Данный шаг выполняется независимо для каждой точки-представителя каждого кластера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Вычисление расстояний до точек-представителей можно выполнять независимо между собой.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Основные шаги алгоритма информационно зависят от предыдущих шагов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:InfoCURE.jpg|1100px|thumb|center|Общий информационный граф алгоритма. В прямоугольниках - шаги алгоритма, в эллипсах - данные.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Шаг 1 (вычисление центроид):&lt;br /&gt;
&lt;br /&gt;
Вычисления центроид для кластеров выполняется независимо, возможно их распараллеливание.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Шаг 2 (поиск &amp;quot;наилучших кластеров для объединения):&lt;br /&gt;
&lt;br /&gt;
На данном этапе вычисляются расстояния между кластерами, что можно сделать независимо друг от друга. Если для определения расстояния между кластерами использовать минимальное расстояние между парами элементов (по одному элементу из каждого кластера), то эти расстояния вычисляются опять же независимо друг от друга, и их можно &amp;quot;распараллелить&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Представители внутри каждого кластера определяются независимо от других кластеров и поддаются распараллеливанию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Изменение положения представителей можно сделать независимо как от представителей данного кластера, так и представителей других кластеров. Это также хорошо поддаётся распараллеливанию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#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 \in R^{m\times n}&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;m&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;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера. В оригинальной статье авторы показывают что &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;0.2&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.7&amp;lt;/math&amp;gt; помогает хорошо выявлять несферические кластеры и подавлять редкие выбросы.&lt;br /&gt;
&lt;br /&gt;
Параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; задаёт какая часть от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек будет использована при случайной выборке точек на шаге &amp;quot;Инициализация&amp;quot;. Слишком малое значение &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; может привести к объединению удалённых кластеров (если ни одна из точек предполагаемого кластера не была выбрана, они были присоединены к другому кластеру на этапе присваивания), а также к разделению кластеров на части (так как конечное количество кластеров фиксировано, а некоторые кластеры могут не набрать достаточное количество мест в выборке, чтобы их кластер сформировался). С другой стороны, скорость вычислений зависит от величины выборки: увеличение выборки при фиксированном числе кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; приведёт к дополнительным шагам на этапе кластеризации, а также к дополнительным вычислениям при нахождении кластеров, &amp;quot;наиболее подходящих&amp;quot; для объединения на каждом шаге. Кроме того, увеличение выборки приводит к уменьшению числа оставшихся точек, присвоение которых не входит в [[#Вычислительное ядро алгоритма|Вычислительное ядро алгоритма]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;m\times n + 4&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Число точек представителей выбирается в зависимости от данных. Если возможно появление кластеров сложной формы, то точек должно быть больше, чтобы выявить их форму. В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность, в то время как при &amp;lt;math&amp;gt;c = 5&amp;lt;/math&amp;gt; большой кластер был разделён на несколько частей.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Exampleofalgorithmparameters2.png|400px|thumb|center|Пример работы алгоритма для разных параметров &amp;lt;math&amp;gt;c&amp;lt;/math&amp;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;
&lt;br /&gt;
- &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; листьев (векторов из &amp;lt;math&amp;gt;R^{1 \times n}&amp;lt;/math&amp;gt;);&lt;br /&gt;
 &lt;br /&gt;
- вершины-кластеры представляют собой массивы указателей на листья (число таких указателей равно &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;); &lt;br /&gt;
&lt;br /&gt;
- корень представляет собой массив указателей на вершины-кластеры (число таких указателей равно числу кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;); &lt;br /&gt;
&lt;br /&gt;
- также имеется указатель на корень дерева.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;m\times n + kp*(m + h + 1)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;kp&amp;lt;/math&amp;gt; - коэффициент, равный отношению размера элемента из &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; к размеру указателя, зависящему от архитектуры платформы.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Алгоритм CURE выполняет иерархическую кластеризацию с использованием набора определяющих точек. Он действует только в евклидовом пространстве и предназначен для кластеризации очень больших наборов числовых данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Преимущества&#039;&#039;&#039;: выполняет кластеризацию на высоком уровне даже при наличии выбросов, выделяет кластеры сложной формы и различных размеров, обладает линейно зависимыми требованиями к месту хранения данных и квадратичную временную сложность для данных высокой размерности.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Недостатки&#039;&#039;&#039;: есть необходимость в задании пороговых значений и количества кластеров, работает только на числовых данных, эффективен только для данных низкой размерности&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;
&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;
== Существующие реализации алгоритма ==&lt;br /&gt;
Последовательная реализация CURE-алгоритма для кластеризации для Python/C++ представлена в пакете: [https://github.com/annoviko/pyclustering pyclustering].&lt;br /&gt;
&lt;br /&gt;
Также существует параллельная реализация алгоритма, описанная в сборнике &amp;lt;ref&amp;gt;Panagiotis E. Hadjidoukas, Laurent Amsaleg. OpenMP Shared Memory Parallel Programming.&lt;br /&gt;
Volume 4315 of the series Lecture Notes in Computer Science pp 289-299&lt;br /&gt;
Parallelization of a Hierarchical Data Clustering Algorithm Using OpenMP&amp;lt;/ref&amp;gt;. Алгоритм предложенный в статье использует массив данных состоящий из размеров кластера, его центроида и репрезентативных точек, а при объединении кластеров информацию о новом кластере просто записывают в строчку предыдущего. Также для каждого кластера требуется считать индекс и расстояние до ближайшего кластера. Именно их поиск авторы статьи и предлагают распараллелить.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B92&amp;diff=19478</id>
		<title>Участник:Лукьянова Анна/Алгоритм кластеризации с использованием представлений2</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B92&amp;diff=19478"/>
		<updated>2016-11-29T14:27:04Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Основные авторы описания: [[U:Лукьянова Анна|А.Я. Лукьянова]]&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмa =&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;
#Отбор выборки объектов для кластеризации.&lt;br /&gt;
#Определение множества переменных, по которым будут оцениваться объекты в выборке. При необходимости – нормализация значений переменных.&lt;br /&gt;
#Вычисление значений меры сходства между объектами.&lt;br /&gt;
#Применение метода кластерного анализа для создания групп сходных объектов (кластеров).&lt;br /&gt;
#Представление результатов анализа.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Классификация алгоритмов&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по способу обработки данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Иерархические методы:&#039;&#039; строят не одно разбиение выборки на непересекающиеся кластеры, а систему вложенных разбиений. Т.о. на выходе мы получаем дерево кластеров, корнем которого является вся выборка, а листьями — наиболее мелкие кластера.&lt;br /&gt;
&lt;br /&gt;
*Агломеративные методы AGNES (Agglomerative Nesting) - характеризуются последовательным объединением исходных элементов и соответствующим уменьшением числа кластеров. В начале работы алгоритма все объекты являются отдельными кластерами. На первом шаге наиболее похожие объекты объединяются в кластер. На последующих шагах объединение продолжается до тех пор,&lt;br /&gt;
пока все объекты не будут составлять один кластер. &lt;br /&gt;
&lt;br /&gt;
*Дивизимные методы DIANA (Divisive Analysis) - характеризуются последовательным разделением исходного кластера, состоящего из всех объектов, и соответствующим увеличением числа кластеров. В начале работы алгоритма все объекты принадлежат одному кластеру, который на последующих шагах делится на меньшие кластеры, в результате образуется последовательность расщепляющих групп. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Неиерархические методы:&#039;&#039; строят одно разбиение объектов на кластеры.&lt;br /&gt;
*Итеративные&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритмы по способу анализа данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;Четкие&#039;&#039; (непересекающиеся) алгоритмы каждому объекту выборки ставят в соответствие номер кластера, т.е. каждый объект принадлежит только одному кластеру;&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;Нечеткие&#039;&#039; (пересекающиеся) алгоритмы каждому объекту ставят в соответствие набор вещественных значений, показывающих степень отношения объекта к кластерам. Т.е. каждый объект относится к каждому кластеру с некоторой вероятностью. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по количеству применений алгоритмов кластеризации:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*С одноэтапной кластеризацией;&lt;br /&gt;
&lt;br /&gt;
*С многоэтапной кластеризацией. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по возможности расширения объема обрабатываемых данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*Масштабируемые;&lt;br /&gt;
&lt;br /&gt;
*Немасштабируемые&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по времени выполнения кластеризации:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*Потоковые (on-line);&lt;br /&gt;
&lt;br /&gt;
*Не потоковые (off-line).&lt;br /&gt;
&lt;br /&gt;
===CURE===&lt;br /&gt;
&lt;br /&gt;
Алгоритм &#039;&#039;&#039;CURE (Clustering Using REpresentatives)&#039;&#039;&#039; выполняет иерархическую кластеризацию с использованием набора определяющих точек для определения объекта в кластер. Относится к алгомеративным методам.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Назначение&#039;&#039;: кластеризация очень больших наборов числовых данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Достоинства&#039;&#039;: &lt;br /&gt;
*выполняет кластеризацию на высоком уровне даже при наличии выбросов;&lt;br /&gt;
*выделяет кластеры сложной формы и различных размеров, &lt;br /&gt;
*обладает линейно зависимыми требованиями к месту хранения данных и временную сложность для данных высокой размерности.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Недостатки&#039;&#039;: есть необходимость в задании пороговых значений и количества кластеров. &lt;br /&gt;
&lt;br /&gt;
Описание алгоритма :&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
[[file:Page1.png|thumb|center|1400px|Рисунок 1: Обзор CURE ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; Построение дерева кластеров, состоящего из каждой строки входного набора данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; Формирование «кучи» в оперативной памяти, расчет расстояния до ближайшего кластера (строки данных) для каждого кластера. При формировании кучи кластеры сортируются по возрастанию дистанции от кластера до ближайшего кластера. Расстояние между кластерами определяется по двум ближайшим элементам из соседних кластеров. Для определения расстояния между кластерами используются «манхеттенская», «евклидова» метрики или похожие на них функции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; Слияние ближних кластеров в один кластер. Новый кластер получает все точки входящих в него входных данных. Расчет расстояния до остальных кластеров для новообразованного кластера. Для расчета расстояния кластеры делятся на две группы: первая группа – кластеры, у которых ближайшими кластерами считаются кластеры, входящие в новообразованный кластер, остальные кластеры – вторая группа. И при этом для кластеров из первой группы, если расстояние до новообразованного кластера меньше чем до предыдущего ближайшего кластера, то ближайший кластер меняется на новообразованный кластер. В противном случае ищется новый ближайший кластер, но при этом не берутся кластеры, расстояния до которых больше, чем до новообразованного кластера. Для кластеров второй группы выполняется следующее: если расстояние до новообразованного  кластера ближе, чем предыдущий ближайший кластер, то ближайший кластер меняется. В противном случае ничего не происходит.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; Переход на шаг 3, если не получено требуемое количество кластеров.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Прежде всего, алгоритм использует случайное разбиение выборки из &amp;lt;math&amp;gt; s &amp;lt;/math&amp;gt; элементов на &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; частей, которые могут поместиться в память отдельной машины кластера. Затем во время первого прохода алгоритм уменьшает число кластеров в каждой подвыборке до &amp;lt;math&amp;gt; \frac{s}{pq} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; q \geq 1 &amp;lt;/math&amp;gt; – некоторая константа (происходит объединение ближайших кластеров). Для определения расстояния между кластерами используются «манхеттенская», «евклидова» метрики или похожие на них функции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Метрика Евклида:&#039;&#039; &amp;lt;math&amp;gt;\rho(p,k) = \sqrt{\sum_{i=1}^n (p_{i} - k_{i})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;«Манхеттенская» метрика:&#039;&#039; &amp;lt;math&amp;gt;\rho(p,k) =\sum_{i=1}^n |p_{i} - k_{i}|&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если расстояние между ближайшей парой кластеров больше некоторого парогового(критического) значения, то объединения не происходит.&lt;br /&gt;
 &lt;br /&gt;
Для последующего шага на вход кластеризации подаются только представительные точки &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;  кластеров, полученных на предыдущем этапе. В ходе второго прохода алгоритма кластеризации остаются представительные точки для заданного количества кластеров. Алгоритм использует представительные точки, как метки, задающие форму и размер кластера.&lt;br /&gt;
&lt;br /&gt;
Число точек представителей выбирается в зависимости от данных. Если возможно появление кластеров сложной формы, то точек должно быть больше, чтобы выявить их форму. В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Param_c.png|400px|thumb|center| Рисунок 2: Результат работы алгоритка в зависимости от количесва представлений &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Из рисунка видно, что при меньших значениях &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; качество работы алгоритма ухудшается&lt;br /&gt;
&lt;br /&gt;
Выбранные точки сдвигаются на &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; к центроиду кластера. Алгоритм становится основанным на методе поиска центроида при &amp;lt;math&amp;gt;\alpha = 1&amp;lt;/math&amp;gt;, и основанным на всех точках кластера при &amp;lt;math&amp;gt;\alpha = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Factor_a.png|400px|thumb|center| Рисунок 3: Чувствительность алгоритма к параметру &amp;lt;math&amp;gt;\alpha &amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма представляет собой итерационное построение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров, вычисление их центроид и назначение точек-представителей.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Основную часть метода составляют разбиение случайной выборки строк на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров и нахождение точек-представителей для каждого кластера.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные параметры: набор входных данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, содержащий &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-мерном пространстве&lt;br /&gt;
и требуемое количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;. Начиная с отдельных точек в виде отдельных кластеров, на каждом шаге ближайшая пара кластеров объединяются, чтобы сформировать новый кластер.Процесс повторяется до тех пор, пока не достигнет &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; cluster(S,k)&lt;br /&gt;
 &#039;&#039;&#039;begin&#039;&#039;&#039;&lt;br /&gt;
 1. T:=build_kd_tree(S)&lt;br /&gt;
 2. Q:-build_heap(S)&lt;br /&gt;
 3. &#039;&#039;&#039;while&#039;&#039;&#039; size(Q)&amp;gt;k &#039;&#039;&#039;do&#039;&#039;&#039; {&lt;br /&gt;
 4.     u:=extract_min(Q)&lt;br /&gt;
 5.     v:= u.closest&lt;br /&gt;
 6.     delete(Q, V)&lt;br /&gt;
 7.     w := merge(u,v) &lt;br /&gt;
 8.     delete_rep(T, u); delete_rep(T, v); insert_rep(T, w) &lt;br /&gt;
 9.     w.closest := x /* x is an arbitrary cluster in Q */&lt;br /&gt;
 10.    &#039;&#039;&#039;for each&#039;&#039;&#039; x \in Q&#039;&#039;&#039;do&#039;&#039;&#039; {&lt;br /&gt;
 11.       &#039;&#039;&#039;if&#039;&#039;&#039; dist(w, x) &amp;lt; dist(w, w.closest) &lt;br /&gt;
 12.               w.closest := x &lt;br /&gt;
 13.       &#039;&#039;&#039;if&#039;&#039;&#039; x.closest is either u or w {  &lt;br /&gt;
 14.            &#039;&#039;&#039; if&#039;&#039;&#039; dist(x, x.closest) &amp;lt; dist(x, w)&lt;br /&gt;
 15.                 x.closest := closest_cluster(T, x, dist(x, w)) &lt;br /&gt;
 16.            &#039;&#039;&#039;else&#039;&#039;&#039;&lt;br /&gt;
 17.               x.closest := w&lt;br /&gt;
 18.             relocate(Q, x) &lt;br /&gt;
 19.        }&lt;br /&gt;
 20.       &#039;&#039;&#039;else if&#039;&#039;&#039; dist(x, x.closest) &amp;gt; d&amp;amp;(x, w) { &lt;br /&gt;
 21.             x.closest := w &lt;br /&gt;
 22.             relocate(Q, x)&lt;br /&gt;
 23.        }&lt;br /&gt;
 24.      }&lt;br /&gt;
 25.      insert(Q, w)&lt;br /&gt;
 26. }&lt;br /&gt;
 &#039;&#039;&#039;end&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Являясь представителем иерархических алгоритмов кластеризации, имеет сложность &amp;lt;math&amp;gt; O(n^2 log(n)) &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; O(n^2) &amp;lt;/math&amp;gt; для признаковых пространств малой размерности &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма все объекты являются отдельными кластерами. На первом шаге наиболее похожие объекты объединяются в кластер. На последующих шагах объединение продолжается до тех пор, пока все объекты не будут составлять один кластер.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Metod.png|400px|thumb|center| Рисунок 4: Дендрограмма агломеративных методов]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Т.к все действия алгорима (вычисления центроид для кластеров, расстояния между кластерами, представители кластеров и изменение положения представителей) на каждом шаге можно делать независимо (как от представителей данного кластера, так и представителей других кластеров), можно сделать вывод, что возможно распараллеливание&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; набор данных &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt; , количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&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;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; требуемое количество &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; крастеров&lt;br /&gt;
&lt;br /&gt;
===Оптимальный выбор параметров для алгоритма===&lt;br /&gt;
&lt;br /&gt;
*размер случайной выборки &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt; (&#039;&#039;а&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
При запуске CURE со случайными размерами выборки в пределах от 500 до 5000 видно, что при размерах до 2000 были найдены кластеры низкого качества, начиная с 2500 точек и выше (2,5% от установленного размера данных), CURE всегда правильно определяет кластеры.&lt;br /&gt;
&lt;br /&gt;
*число точек представителей  &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt; (&#039;&#039;b&#039;&#039;)&lt;br /&gt;
В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность.&lt;br /&gt;
&lt;br /&gt;
*число разбиений выборки &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; (&#039;&#039;&#039;c&#039;&#039;&#039;)&lt;br /&gt;
&amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; должно быть выбрано таким, что &amp;lt;math&amp;gt; \frac{s}{pq} &amp;lt;/math&amp;gt; &amp;gt; &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*размерность (&#039;&#039;d&#039;&#039;)&lt;br /&gt;
CURE хорошо себя чувствует при больших размерностях&lt;br /&gt;
&lt;br /&gt;
*сжимающий фактор &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; &lt;br /&gt;
В оригинальной статье авторы показывают что  &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; от 0.2 до 0.7 помогает хорошо выявлять несферические кластеры и подавлять редкие выбросы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:4pic.png|800px|thumb|center| Рисунок 5: Влияние параметров &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; p &amp;lt;/math&amp;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;
*работает только на числовых данных&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;
&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;
&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Последовательная реализация CURE-алгоритма для кластеризации для Python/C++ представлена в пакете: [https://github.com/annoviko/pyclustering pyclustering].&lt;br /&gt;
&lt;br /&gt;
Также существует параллельная реализация алгоритма, описанная в сборнике [4]. Алгоритм предложенный в статье использует массив данных состоящий из размеров кластера, его центроида и репрезентативных точек, а при объединении кластеров информацию о новом кластере просто записывают в строчку предыдущего. Также для каждого кластера требуется считать индекс и расстояние до ближайшего кластера. Именно их поиск авторы статьи и предлагают распараллелить.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
[1] Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. - СПб.: БХВ-Петербург, 2002. - 608 с.&lt;br /&gt;
&lt;br /&gt;
[2] Theodoridis, Sergios; Koutroumbas, Konstantinos (2006). Pattern recognition. Academic Press. pp. 572–574. ISBN 978-0-12-369531-4.&lt;br /&gt;
&lt;br /&gt;
[3] Guha, Sudipto; Rastogi, Rajeev; Shim, Kyuseok (2001). &amp;quot;CURE: An Efficient Clustering Algorithm for Large Databases&amp;quot; (PDF). Information Systems. 26 (1): 35–58. doi:10.1016/S0306-4379(01)00008-4&lt;br /&gt;
&lt;br /&gt;
[4] Panagiotis E. Hadjidoukas, Laurent Amsaleg. OpenMP Shared Memory Parallel Programming.&lt;br /&gt;
Volume 4315 of the series Lecture Notes in Computer Science pp 289-299&lt;br /&gt;
Parallelization of a Hierarchical Data Clustering Algorithm Using OpenMP&lt;/div&gt;</summary>
		<author><name>Kronberg</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B92&amp;diff=19477</id>
		<title>Участник:Лукьянова Анна/Алгоритм кластеризации с использованием представлений2</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B92&amp;diff=19477"/>
		<updated>2016-11-29T14:26:28Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Kronberg}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[U:Лукьянова Анна|А.Я. Лукьянова]]&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмa =&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;
#Отбор выборки объектов для кластеризации.&lt;br /&gt;
#Определение множества переменных, по которым будут оцениваться объекты в выборке. При необходимости – нормализация значений переменных.&lt;br /&gt;
#Вычисление значений меры сходства между объектами.&lt;br /&gt;
#Применение метода кластерного анализа для создания групп сходных объектов (кластеров).&lt;br /&gt;
#Представление результатов анализа.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Классификация алгоритмов&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по способу обработки данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Иерархические методы:&#039;&#039; строят не одно разбиение выборки на непересекающиеся кластеры, а систему вложенных разбиений. Т.о. на выходе мы получаем дерево кластеров, корнем которого является вся выборка, а листьями — наиболее мелкие кластера.&lt;br /&gt;
&lt;br /&gt;
*Агломеративные методы AGNES (Agglomerative Nesting) - характеризуются последовательным объединением исходных элементов и соответствующим уменьшением числа кластеров. В начале работы алгоритма все объекты являются отдельными кластерами. На первом шаге наиболее похожие объекты объединяются в кластер. На последующих шагах объединение продолжается до тех пор,&lt;br /&gt;
пока все объекты не будут составлять один кластер. &lt;br /&gt;
&lt;br /&gt;
*Дивизимные методы DIANA (Divisive Analysis) - характеризуются последовательным разделением исходного кластера, состоящего из всех объектов, и соответствующим увеличением числа кластеров. В начале работы алгоритма все объекты принадлежат одному кластеру, который на последующих шагах делится на меньшие кластеры, в результате образуется последовательность расщепляющих групп. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Неиерархические методы:&#039;&#039; строят одно разбиение объектов на кластеры.&lt;br /&gt;
*Итеративные&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритмы по способу анализа данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;Четкие&#039;&#039; (непересекающиеся) алгоритмы каждому объекту выборки ставят в соответствие номер кластера, т.е. каждый объект принадлежит только одному кластеру;&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;Нечеткие&#039;&#039; (пересекающиеся) алгоритмы каждому объекту ставят в соответствие набор вещественных значений, показывающих степень отношения объекта к кластерам. Т.е. каждый объект относится к каждому кластеру с некоторой вероятностью. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по количеству применений алгоритмов кластеризации:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*С одноэтапной кластеризацией;&lt;br /&gt;
&lt;br /&gt;
*С многоэтапной кластеризацией. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по возможности расширения объема обрабатываемых данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*Масштабируемые;&lt;br /&gt;
&lt;br /&gt;
*Немасштабируемые&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Методы по времени выполнения кластеризации:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
*Потоковые (on-line);&lt;br /&gt;
&lt;br /&gt;
*Не потоковые (off-line).&lt;br /&gt;
&lt;br /&gt;
===CURE===&lt;br /&gt;
&lt;br /&gt;
Алгоритм &#039;&#039;&#039;CURE (Clustering Using REpresentatives)&#039;&#039;&#039; выполняет иерархическую кластеризацию с использованием набора определяющих точек для определения объекта в кластер. Относится к алгомеративным методам.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Назначение&#039;&#039;: кластеризация очень больших наборов числовых данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Достоинства&#039;&#039;: &lt;br /&gt;
*выполняет кластеризацию на высоком уровне даже при наличии выбросов;&lt;br /&gt;
*выделяет кластеры сложной формы и различных размеров, &lt;br /&gt;
*обладает линейно зависимыми требованиями к месту хранения данных и временную сложность для данных высокой размерности.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Недостатки&#039;&#039;: есть необходимость в задании пороговых значений и количества кластеров. &lt;br /&gt;
&lt;br /&gt;
Описание алгоритма :&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
[[file:Page1.png|thumb|center|1400px|Рисунок 1: Обзор CURE ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; Построение дерева кластеров, состоящего из каждой строки входного набора данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; Формирование «кучи» в оперативной памяти, расчет расстояния до ближайшего кластера (строки данных) для каждого кластера. При формировании кучи кластеры сортируются по возрастанию дистанции от кластера до ближайшего кластера. Расстояние между кластерами определяется по двум ближайшим элементам из соседних кластеров. Для определения расстояния между кластерами используются «манхеттенская», «евклидова» метрики или похожие на них функции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; Слияние ближних кластеров в один кластер. Новый кластер получает все точки входящих в него входных данных. Расчет расстояния до остальных кластеров для новообразованного кластера. Для расчета расстояния кластеры делятся на две группы: первая группа – кластеры, у которых ближайшими кластерами считаются кластеры, входящие в новообразованный кластер, остальные кластеры – вторая группа. И при этом для кластеров из первой группы, если расстояние до новообразованного кластера меньше чем до предыдущего ближайшего кластера, то ближайший кластер меняется на новообразованный кластер. В противном случае ищется новый ближайший кластер, но при этом не берутся кластеры, расстояния до которых больше, чем до новообразованного кластера. Для кластеров второй группы выполняется следующее: если расстояние до новообразованного  кластера ближе, чем предыдущий ближайший кластер, то ближайший кластер меняется. В противном случае ничего не происходит.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; Переход на шаг 3, если не получено требуемое количество кластеров.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Прежде всего, алгоритм использует случайное разбиение выборки из &amp;lt;math&amp;gt; s &amp;lt;/math&amp;gt; элементов на &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; частей, которые могут поместиться в память отдельной машины кластера. Затем во время первого прохода алгоритм уменьшает число кластеров в каждой подвыборке до &amp;lt;math&amp;gt; \frac{s}{pq} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; q \geq 1 &amp;lt;/math&amp;gt; – некоторая константа (происходит объединение ближайших кластеров). Для определения расстояния между кластерами используются «манхеттенская», «евклидова» метрики или похожие на них функции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Метрика Евклида:&#039;&#039; &amp;lt;math&amp;gt;\rho(p,k) = \sqrt{\sum_{i=1}^n (p_{i} - k_{i})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;«Манхеттенская» метрика:&#039;&#039; &amp;lt;math&amp;gt;\rho(p,k) =\sum_{i=1}^n |p_{i} - k_{i}|&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если расстояние между ближайшей парой кластеров больше некоторого парогового(критического) значения, то объединения не происходит.&lt;br /&gt;
 &lt;br /&gt;
Для последующего шага на вход кластеризации подаются только представительные точки &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;  кластеров, полученных на предыдущем этапе. В ходе второго прохода алгоритма кластеризации остаются представительные точки для заданного количества кластеров. Алгоритм использует представительные точки, как метки, задающие форму и размер кластера.&lt;br /&gt;
&lt;br /&gt;
Число точек представителей выбирается в зависимости от данных. Если возможно появление кластеров сложной формы, то точек должно быть больше, чтобы выявить их форму. В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Param_c.png|400px|thumb|center| Рисунок 2: Результат работы алгоритка в зависимости от количесва представлений &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Из рисунка видно, что при меньших значениях &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; качество работы алгоритма ухудшается&lt;br /&gt;
&lt;br /&gt;
Выбранные точки сдвигаются на &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; к центроиду кластера. Алгоритм становится основанным на методе поиска центроида при &amp;lt;math&amp;gt;\alpha = 1&amp;lt;/math&amp;gt;, и основанным на всех точках кластера при &amp;lt;math&amp;gt;\alpha = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Factor_a.png|400px|thumb|center| Рисунок 3: Чувствительность алгоритма к параметру &amp;lt;math&amp;gt;\alpha &amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма представляет собой итерационное построение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров, вычисление их центроид и назначение точек-представителей.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Основную часть метода составляют разбиение случайной выборки строк на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров и нахождение точек-представителей для каждого кластера.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные параметры: набор входных данных &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, содержащий &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-мерном пространстве&lt;br /&gt;
и требуемое количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;. Начиная с отдельных точек в виде отдельных кластеров, на каждом шаге ближайшая пара кластеров объединяются, чтобы сформировать новый кластер.Процесс повторяется до тех пор, пока не достигнет &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; cluster(S,k)&lt;br /&gt;
 &#039;&#039;&#039;begin&#039;&#039;&#039;&lt;br /&gt;
 1. T:=build_kd_tree(S)&lt;br /&gt;
 2. Q:-build_heap(S)&lt;br /&gt;
 3. &#039;&#039;&#039;while&#039;&#039;&#039; size(Q)&amp;gt;k &#039;&#039;&#039;do&#039;&#039;&#039; {&lt;br /&gt;
 4.     u:=extract_min(Q)&lt;br /&gt;
 5.     v:= u.closest&lt;br /&gt;
 6.     delete(Q, V)&lt;br /&gt;
 7.     w := merge(u,v) &lt;br /&gt;
 8.     delete_rep(T, u); delete_rep(T, v); insert_rep(T, w) &lt;br /&gt;
 9.     w.closest := x /* x is an arbitrary cluster in Q */&lt;br /&gt;
 10.    &#039;&#039;&#039;for each&#039;&#039;&#039; x \in Q&#039;&#039;&#039;do&#039;&#039;&#039; {&lt;br /&gt;
 11.       &#039;&#039;&#039;if&#039;&#039;&#039; dist(w, x) &amp;lt; dist(w, w.closest) &lt;br /&gt;
 12.               w.closest := x &lt;br /&gt;
 13.       &#039;&#039;&#039;if&#039;&#039;&#039; x.closest is either u or w {  &lt;br /&gt;
 14.            &#039;&#039;&#039; if&#039;&#039;&#039; dist(x, x.closest) &amp;lt; dist(x, w)&lt;br /&gt;
 15.                 x.closest := closest_cluster(T, x, dist(x, w)) &lt;br /&gt;
 16.            &#039;&#039;&#039;else&#039;&#039;&#039;&lt;br /&gt;
 17.               x.closest := w&lt;br /&gt;
 18.             relocate(Q, x) &lt;br /&gt;
 19.        }&lt;br /&gt;
 20.       &#039;&#039;&#039;else if&#039;&#039;&#039; dist(x, x.closest) &amp;gt; d&amp;amp;(x, w) { &lt;br /&gt;
 21.             x.closest := w &lt;br /&gt;
 22.             relocate(Q, x)&lt;br /&gt;
 23.        }&lt;br /&gt;
 24.      }&lt;br /&gt;
 25.      insert(Q, w)&lt;br /&gt;
 26. }&lt;br /&gt;
 &#039;&#039;&#039;end&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Являясь представителем иерархических алгоритмов кластеризации, имеет сложность &amp;lt;math&amp;gt; O(n^2 log(n)) &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; O(n^2) &amp;lt;/math&amp;gt; для признаковых пространств малой размерности &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма все объекты являются отдельными кластерами. На первом шаге наиболее похожие объекты объединяются в кластер. На последующих шагах объединение продолжается до тех пор, пока все объекты не будут составлять один кластер.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Metod.png|400px|thumb|center| Рисунок 4: Дендрограмма агломеративных методов]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Т.к все действия алгорима (вычисления центроид для кластеров, расстояния между кластерами, представители кластеров и изменение положения представителей) на каждом шаге можно делать независимо (как от представителей данного кластера, так и представителей других кластеров), можно сделать вывод, что возможно распараллеливание&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; набор данных &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt; , количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&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;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; требуемое количество &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; крастеров&lt;br /&gt;
&lt;br /&gt;
===Оптимальный выбор параметров для алгоритма===&lt;br /&gt;
&lt;br /&gt;
*размер случайной выборки &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt; (&#039;&#039;а&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
При запуске CURE со случайными размерами выборки в пределах от 500 до 5000 видно, что при размерах до 2000 были найдены кластеры низкого качества, начиная с 2500 точек и выше (2,5% от установленного размера данных), CURE всегда правильно определяет кластеры.&lt;br /&gt;
&lt;br /&gt;
*число точек представителей  &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt; (&#039;&#039;b&#039;&#039;)&lt;br /&gt;
В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность.&lt;br /&gt;
&lt;br /&gt;
*число разбиений выборки &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; (&#039;&#039;&#039;c&#039;&#039;&#039;)&lt;br /&gt;
&amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; должно быть выбрано таким, что &amp;lt;math&amp;gt; \frac{s}{pq} &amp;lt;/math&amp;gt; &amp;gt; &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*размерность (&#039;&#039;d&#039;&#039;)&lt;br /&gt;
CURE хорошо себя чувствует при больших размерностях&lt;br /&gt;
&lt;br /&gt;
*сжимающий фактор &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; &lt;br /&gt;
В оригинальной статье авторы показывают что  &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; от 0.2 до 0.7 помогает хорошо выявлять несферические кластеры и подавлять редкие выбросы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:4pic.png|800px|thumb|center| Рисунок 5: Влияние параметров &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; p &amp;lt;/math&amp;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;
*работает только на числовых данных&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;
&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;
&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Последовательная реализация CURE-алгоритма для кластеризации для Python/C++ представлена в пакете: [https://github.com/annoviko/pyclustering pyclustering].&lt;br /&gt;
&lt;br /&gt;
Также существует параллельная реализация алгоритма, описанная в сборнике [4]. Алгоритм предложенный в статье использует массив данных состоящий из размеров кластера, его центроида и репрезентативных точек, а при объединении кластеров информацию о новом кластере просто записывают в строчку предыдущего. Также для каждого кластера требуется считать индекс и расстояние до ближайшего кластера. Именно их поиск авторы статьи и предлагают распараллелить.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
[1] Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. - СПб.: БХВ-Петербург, 2002. - 608 с.&lt;br /&gt;
&lt;br /&gt;
[2] Theodoridis, Sergios; Koutroumbas, Konstantinos (2006). Pattern recognition. Academic Press. pp. 572–574. ISBN 978-0-12-369531-4.&lt;br /&gt;
&lt;br /&gt;
[3] Guha, Sudipto; Rastogi, Rajeev; Shim, Kyuseok (2001). &amp;quot;CURE: An Efficient Clustering Algorithm for Large Databases&amp;quot; (PDF). Information Systems. 26 (1): 35–58. doi:10.1016/S0306-4379(01)00008-4&lt;br /&gt;
&lt;br /&gt;
[4] Panagiotis E. Hadjidoukas, Laurent Amsaleg. OpenMP Shared Memory Parallel Programming.&lt;br /&gt;
Volume 4315 of the series Lecture Notes in Computer Science pp 289-299&lt;br /&gt;
Parallelization of a Hierarchical Data Clustering Algorithm Using OpenMP&lt;/div&gt;</summary>
		<author><name>Kronberg</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0&amp;diff=19473</id>
		<title>Обсуждение участника:Лукьянова Анна</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0&amp;diff=19473"/>
		<updated>2016-11-29T14:23:22Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Замечания к статье ===&lt;br /&gt;
&lt;br /&gt;
* содержание не соответствуют шаблону описания алгоритма: отсутствуют п.п. во 2 части--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 16:01, 25 октября 2016 (MSK)&lt;br /&gt;
* п.п. 2.4 не заполнен--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:00, 16 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Алгоритм кластеризации с использованием представлений&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* п.1.1.1. Если взялись за классификацию алгоритмов, лучше разъяснить, что такое агломеративные, дивизивные, &amp;quot;четкие&amp;quot; и &amp;quot;нечеткие&amp;quot; алгоритмы. Было бы здорово даже сделать это в отдельной статье про задачу кластеризации, но это не обязательно [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* п.1.1.2. Метрики и некоторые подробности лучше перенести ниже, к &amp;quot;математическому описанию&amp;quot; [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* п.1.2 выглядит дописанным не до конца. Там нужно разъяснить те неформальные конструкции, которые встречались ранее в п. 1.1.2. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* п.1.5. Псевдокод слишком псевдо, его следует сделать больше похожим на код, с соответствующим форматированием [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* следует добавить информацию об оптимальном выборе параметров алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* по-прежнему не хватает второй части...&lt;/div&gt;</summary>
		<author><name>Kronberg</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:JuliaA/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=19472</id>
		<title>Участник:JuliaA/Алгоритм кластеризации с использованием представлений</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:JuliaA/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=19472"/>
		<updated>2016-11-29T14:21:53Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Kronberg}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: &amp;lt;br&amp;gt;&lt;br /&gt;
[[Участник:JuliaA|Ю.А. Антохина]], разделы [[#Общее описание алгоритма|1.1]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]] - [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]]&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Участник:Cookie|С.А. Шатков]], разделы [[#Математическое описание алгоритма|1.2]] - [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Масштабируемость алгоритма и его реализации|2.4]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Пусть дан набор точек и заданы правила определения расстояния между ними. Задача кластеризации сгруппировать эти точки в определенное число кластеров. Члены одного кластера похожи, а члены разных классов различны.&lt;br /&gt;
&lt;br /&gt;
В алгоритме CURE применяется иерархическая кластеризация. Есть два подхода к иерархической кластеризации. В одном случае один большой кластер на каждом шаге разбивается на несколько, пока число кластеров не будет равно заданному заранее числу кластеров. В другом случае наоборот кластеры, состоящие каждый из одной точки объединяются в большие. Второй метод используется в CURE.&lt;br /&gt;
&lt;br /&gt;
Результат кластеризации зависит от того, как будут представлены кластеры. Например, средним всех точек кластера, или всеми точками кластера. Применение среднего возможно только в евклидовом пространстве. А использование всех точек неустойчиво к случайным выбросам. В CURE используется другой метод: для каждого кластера выбираются точки-представители. Выбирается постоянное число точек, которые будут представлять каждый кластер. Кластеры с наиболее похожими наборами репрезентативных точек объединяются на каждом шаге алгоритма.&lt;br /&gt;
&lt;br /&gt;
За счет использования точек-представителей алгоритм CURE устойчив к выбросам и может выделять кластеры сложной формы и различных размеров.&lt;br /&gt;
&lt;br /&gt;
Описание шагов&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1&#039;&#039;&#039;. Если все данные использовать сразу как входные для CURE, то эффективность алгоритма будет низкая, а время выполнения большим. Поэтому на первом шаге мы случайным образом выбираем часть точек которые помещаются в память, затем группируем наиболее похожие с помощью иерархического метода в заранее заданное число кластеров.&lt;br /&gt;
Дальше работаем с кластерами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2&#039;&#039;&#039;. Для каждого кластера выбираем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек-представителей, максимально удаленных друг от друга. Чисто &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; остается постоянным.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3&#039;&#039;&#039;. Объединяем кластеры с наиболее похожими наборами точек-представителей.&lt;br /&gt;
Если не достигнуто нужное число кластеров, то перейти на шаг 2.&lt;br /&gt;
Чтобы при объединении кластеров не выбирать каждый раз c точек-представителей из всех точек, мы выбираем их только из &amp;lt;math&amp;gt;2c&amp;lt;/math&amp;gt; точек объединенных кластеров.&lt;br /&gt;
&lt;br /&gt;
Выбранные точки сдвигаются на следующем шаге на &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; к центроиду кластера. Алгоритм становится основанным на методе поиска центроида при &amp;lt;math&amp;gt;\alpha = 1&amp;lt;/math&amp;gt;, и основанным на всех точках кластера при &amp;lt;math&amp;gt;\alpha = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Exampleofalgorithmparametrs1.png|600px|thumb|center|Пример работы алгоритма для разных параметров &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; [1].]]&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Пусть дана матрица &amp;lt;math&amp;gt;x \in R^{m\times n}&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;, число точек-представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt;. Каждая строка матрицы представляет собой точку в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - мерном пространстве.&lt;br /&gt;
&lt;br /&gt;
m число точек для кластеризации, n размерность.&lt;br /&gt;
&lt;br /&gt;
Расстояние &amp;lt;math&amp;gt;\rho(a,b)&amp;lt;/math&amp;gt; между точками &amp;lt;math&amp;gt;a = (a_{1},\dots,a_{n})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b = (b_{1},\dots,b_{n})&amp;lt;/math&amp;gt; будем вычислять по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rho(a,b) = \sqrt{\sum_{i=1}^n (a_{i} - b_{i})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Инициализация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Выбираются &amp;lt;math&amp;gt;\lceil \gamma m\rceil&amp;lt;/math&amp;gt; строк матрицы случайным образом. Например, можно использовать [https://ru.wikipedia.org/wiki/Reservoir_sampling Алгоритм R]. &lt;br /&gt;
&lt;br /&gt;
В основной памяти выделяется куча, часть которой используется для размещения дерева, листьями которого являются выбранные строки, соединённые ребром с вершиной дерева.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
В самом начале шага считаем каждую выбранную точку одноточечным кластером с центроидой в этой же точке.&lt;br /&gt;
&lt;br /&gt;
Далее выполняем следующие шаги 1-2, пока количество кластеров не станет равно &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Шаг 1:&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го кластера вычисляется центроида &amp;lt;math&amp;gt;\mu_{i}&amp;lt;/math&amp;gt; по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mu_{i} = \frac{1}{s_{i}}\sum_{j\in S_{i}} x^{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S_{i}&amp;lt;/math&amp;gt; - номера строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, входящие в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й кластер; &amp;lt;math&amp;gt;s_{i}&amp;lt;/math&amp;gt; - количество элементов в множестве &amp;lt;math&amp;gt;S_{i}&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;x^{j}&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-я строка матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Шаг 2:&lt;br /&gt;
&lt;br /&gt;
Выполняется поиск пары кластеров, &amp;quot;наиболее подходящих&amp;quot; для объединения. В качестве критерия выбора можно рассматривать: наименьшее расстояние между центроидами кластеров или наименьшее расстояние между элементами, входящими в рассматриваемую пару кластеров. Существуют также другие критерии выбора &amp;quot;подходящих&amp;quot; классов, например, сравнение плотности кластеров до объединения и после. Далее выполняется слияние найденных кластеров. Для дерева это означает добавление новой вершины, соединённой ребром с корнем; поддеревья кластеров становятся ветвями этой новой вершины.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге для каждого кластера определяются &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек-представителей. Для наилучшего отображения формы множества предполагается использование точек, максимально удалённых друг от друга.&lt;br /&gt;
&lt;br /&gt;
Для этого используем следующий алгоритм:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - текущее количество представителей, &amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt; - множество номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, входящие в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-й кластер, &amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt; - множество номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, назначенных представителями &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го кластера, а &amp;lt;math&amp;gt;x^{j}&amp;lt;/math&amp;gt; - вектор-строка матрицы &amp;lt;math&amp;gt;x&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;R_{k}&amp;lt;/math&amp;gt; = [] % Пустое множество&lt;br /&gt;
  a = Choose(&amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt;) % Выбор произвольного номера точки, входящей в кластер &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
  Add(&amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;,a) % Добавление точки а во множество представителей&lt;br /&gt;
  i = 1 % Текущее количество представителей кластера&lt;br /&gt;
  While (i&amp;lt;c) do % Хотим найти с представителей для кластера&lt;br /&gt;
  - rmax = 0&lt;br /&gt;
  - For ( (y &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt;) and (y &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;P_{k}&amp;lt;/math&amp;gt;) ) do % Поиск следующего представителя среди оставшихся точек кластера&lt;br /&gt;
  - - rmin = &amp;lt;math&amp;gt;\rho(x^{a}&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x^{y})&amp;lt;/math&amp;gt;&lt;br /&gt;
  - - For ( (j &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;) and (j &amp;lt;math&amp;gt;\neq&amp;lt;/math&amp;gt; a) ) do % Поиск расстояния от точки с номером y до ближайшего представителя &lt;br /&gt;
  - - - d = &amp;lt;math&amp;gt;\rho(x^{j}&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x^{y})&amp;lt;/math&amp;gt; % вычисление расстояния&lt;br /&gt;
  - - - If (d &amp;lt; rmin) then&lt;br /&gt;
  - - - - rmin = d&lt;br /&gt;
  - - - endif&lt;br /&gt;
  - - enddo&lt;br /&gt;
  - - If (rmin &amp;gt; rmax) % Поиск точки, для которой расстояние до ближайшего представителя является максимальным&lt;br /&gt;
  - - - rmax = rmin&lt;br /&gt;
  - - - b = y % искомая точка&lt;br /&gt;
  - - endif&lt;br /&gt;
  - enddo&lt;br /&gt;
  - Add(&amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;,b) % Добавление новой точки в множество представителей&lt;br /&gt;
  - i = i + 1 % текущее число представителей&lt;br /&gt;
  enddo&lt;br /&gt;
  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Для каждого представителя &amp;lt;math&amp;gt;x^{y}&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го кластера выполняется сдвиг к его центроиде &amp;lt;math&amp;gt;\mu_{k}&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; раз: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\hat{x}^{y} = \alpha\mu_{k} + (1-\alpha)x^{y}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Получаем множество &amp;lt;math&amp;gt;\hat{R}_{k}&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек, которые будут далее использоваться как представители этого кластера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге происходит присвоение неиспользованных точек (строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;) к кластерам. Для точки-строки &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычисляется расстояние до каждого представителя. Результат: строка &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; добавляется в поддерево кластера, которому принадлежит наиближайший представитель.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Результатом работы алгоритма является дерево, ветвями которого являются отдельные кластеры. Листьями кластеров являются строки из матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма представляет собой итерационное построение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров с помощью иерархического метода, вычисление их центроид &amp;lt;math&amp;gt;\mu_{k}&amp;lt;/math&amp;gt; и назначение точек-представителей. В ходе работы вычислительного ядра формируется дерево, в котором точки, выбранные на этапе &#039;&#039;&#039;Инициализации&#039;&#039;&#039; собраны в &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; поддеревьев. Точки-представители хранятся в отдельной структуре. Эти точки будут использованы для выбора кластеров для неиспользованных (на этапе &#039;&#039;&#039;Инициализации&#039;&#039;&#039;) точек после работы вычислительного ядра.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации состоит в формировании &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; кластеров из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек; число &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; задано заранее.&lt;br /&gt;
&lt;br /&gt;
На начальном этапе выбирается часть точек, для которых выполняется иерархический алгоритм кластеризации. На начальном этапе каждая точка считается отдельным кластером с центроидой в данной точке. Для каждого кластера считается расстояние для всех остальных. Похожие кластеры объединяются и их точки тоже. На каждом шаге алгоритма происходит вычисление центроиды для новообразованного кластера. Алгоритм выполняется, пока не будет достигнуто необходимое число кластеров. &lt;br /&gt;
&lt;br /&gt;
После этого происходит назначение точек-представителей для каждого кластера и их сдвиг к центроиде. &lt;br /&gt;
&lt;br /&gt;
Далее для неиспользованных точек &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; происходит вычисление расстояний до всех точек-представителей. Точка помещается в тот кластер, точка-представитель которого находится ближе остальных.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:SubCURE4.jpg|1100px|thumb|center|Схема работы алгоритма кластеризации CURE]]&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные для кластеризации - это &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; репрезентативных точек в d мерном пространстве и необходимое число &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; кластеров. Для каждой точки необходимо вычислить расстояние до другой. Сложность этого этапа &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;. Если искать ближайший кластер простым перебором, то сложность поиска будет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для каждого кластера. Но для эффективного поиска сложность составляет &amp;lt;math&amp;gt;log_2{n}&amp;lt;/math&amp;gt;. Эффективный метод описан в статье.&amp;lt;ref&amp;gt;Sudipto Guha (Stanford), Rajeev Rastogi (Bell Labs), Kyuseok Shim (Korea Institute of Technology). CURE: an efficient clustering algorithm for large databases. SIGMOD &#039;98 Proceedings of the 1998 ACM SIGMOD international conference on Management of data, pp 73-84 &amp;lt;/ref&amp;gt; Далее каждую точку необходимо вдвинуть к центроиде, сложность этой операции &amp;lt;math&amp;gt;O(2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Таким образом худшее время выполнения алгоритма &amp;lt;math&amp;gt;O(n^2 log_2{n})&amp;lt;/math&amp;gt;. Для двумерного пространства временная сложность может быть &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;. Так что сложность алгоритма не хуже чем у центроидного. То есть при такой размерности входных данных CURE выполняется за полиномиальное время.&amp;lt;math&amp;gt;~^{[1]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Инициализация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
В зависимости от алгоритма случайная выборка номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; может производится либо зависимо, либо независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Описанный в разделе иерархический алгоритм кластеризации является последовательным, так как на каждом шаге сравниваются расстояния между центроидами кластеров, некоторые из которых меняются при объединении кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Файл:Points c.png|800px|thumb|center|Последовательный информационный граф для точек-представителей]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Данный шаг выполняется независимо для каждой точки-представителя каждого кластера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Вычисление расстояний до точек-представителей можно выполнять независимо между собой.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Основные шаги алгоритма информационно зависят от предыдущих шагов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:InfoCURE.jpg|1100px|thumb|center|Общий информационный граф алгоритма. В прямоугольниках - шаги алгоритма, в эллипсах - данные.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Шаг 1 (вычисление центроид):&lt;br /&gt;
&lt;br /&gt;
Вычисления центроид для кластеров выполняется независимо, возможно их распараллеливание.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Шаг 2 (поиск &amp;quot;наилучших кластеров для объединения):&lt;br /&gt;
&lt;br /&gt;
На данном этапе вычисляются расстояния между кластерами, что можно сделать независимо друг от друга. Если для определения расстояния между кластерами использовать минимальное расстояние между парами элементов (по одному элементу из каждого кластера), то эти расстояния вычисляются опять же независимо друг от друга, и их можно &amp;quot;распараллелить&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Представители внутри каждого кластера определяются независимо от других кластеров и поддаются распараллеливанию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Изменение положения представителей можно сделать независимо как от представителей данного кластера, так и представителей других кластеров. Это также хорошо поддаётся распараллеливанию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#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 \in R^{m\times n}&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;m&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;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера. В оригинальной статье авторы показывают что &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;0.2&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.7&amp;lt;/math&amp;gt; помогает хорошо выявлять несферические кластеры и подавлять редкие выбросы.&lt;br /&gt;
&lt;br /&gt;
Параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; задаёт какая часть от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек будет использована при случайной выборке точек на шаге &amp;quot;Инициализация&amp;quot;. Слишком малое значение &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; может привести к объединению удалённых кластеров (если ни одна из точек предполагаемого кластера не была выбрана, они были присоединены к другому кластеру на этапе присваивания), а также к разделению кластеров на части (так как конечное количество кластеров фиксировано, а некоторые кластеры могут не набрать достаточное количество мест в выборке, чтобы их кластер сформировался). С другой стороны, скорость вычислений зависит от величины выборки: увеличение выборки при фиксированном числе кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; приведёт к дополнительным шагам на этапе кластеризации, а также к дополнительным вычислениям при нахождении кластеров, &amp;quot;наиболее подходящих&amp;quot; для объединения на каждом шаге. Кроме того, увеличение выборки приводит к уменьшению числа оставшихся точек, присвоение которых не входит в [[#Вычислительное ядро алгоритма|Вычислительное ядро алгоритма]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;m\times n + 4&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Число точек представителей выбирается в зависимости от данных. Если возможно появление кластеров сложной формы, то точек должно быть больше, чтобы выявить их форму. В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность, в то время как при &amp;lt;math&amp;gt;c = 5&amp;lt;/math&amp;gt; большой кластер был разделён на несколько частей.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Exampleofalgorithmparameters2.png|400px|thumb|center|Пример работы алгоритма для разных параметров &amp;lt;math&amp;gt;c&amp;lt;/math&amp;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;
&lt;br /&gt;
- &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; листьев (векторов из &amp;lt;math&amp;gt;R^{1 \times n}&amp;lt;/math&amp;gt;);&lt;br /&gt;
 &lt;br /&gt;
- вершины-кластеры представляют собой массивы указателей на листья (число таких указателей равно &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;); &lt;br /&gt;
&lt;br /&gt;
- корень представляет собой массив указателей на вершины-кластеры (число таких указателей равно числу кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;); &lt;br /&gt;
&lt;br /&gt;
- также имеется указатель на корень дерева.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;m\times n + kp*(m + h + 1)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;kp&amp;lt;/math&amp;gt; - коэффициент, равный отношению размера элемента из &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; к размеру указателя, зависящему от архитектуры платформы.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Алгоритм CURE выполняет иерархическую кластеризацию с использованием набора определяющих точек. Он действует только в евклидовом пространстве и предназначен для кластеризации очень больших наборов числовых данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Преимущества&#039;&#039;&#039;: выполняет кластеризацию на высоком уровне даже при наличии выбросов, выделяет кластеры сложной формы и различных размеров, обладает линейно зависимыми требованиями к месту хранения данных и квадратичную временную сложность для данных высокой размерности.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Недостатки&#039;&#039;&#039;: есть необходимость в задании пороговых значений и количества кластеров, работает только на числовых данных, эффективен только для данных низкой размерности&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;
&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;
== Существующие реализации алгоритма ==&lt;br /&gt;
Последовательная реализация CURE-алгоритма для кластеризации для Python/C++ представлена в пакете: [https://github.com/annoviko/pyclustering pyclustering].&lt;br /&gt;
&lt;br /&gt;
Также существует параллельная реализация алгоритма, описанная в сборнике &amp;lt;ref&amp;gt;Panagiotis E. Hadjidoukas, Laurent Amsaleg. OpenMP Shared Memory Parallel Programming.&lt;br /&gt;
Volume 4315 of the series Lecture Notes in Computer Science pp 289-299&lt;br /&gt;
Parallelization of a Hierarchical Data Clustering Algorithm Using OpenMP&amp;lt;/ref&amp;gt;. Алгоритм предложенный в статье использует массив данных состоящий из размеров кластера, его центроида и репрезентативных точек, а при объединении кластеров информацию о новом кластере просто записывают в строчку предыдущего. Также для каждого кластера требуется считать индекс и расстояние до ближайшего кластера. Именно их поиск авторы статьи и предлагают распараллелить.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=19470</id>
		<title>Участник:Vagabond/Алгоритм быстрого дискретного пребразования Фурье</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=19470"/>
		<updated>2016-11-29T14:20:56Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS|Kronberg}}&lt;br /&gt;
&#039;&#039;&#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_{n-1})^{T}&amp;lt;/math&amp;gt; - исходный сигнал, который необходимо преобразовать умножением на тензор второго ранга &amp;lt;math&amp;gt;E_{pk}=e^{\frac{-2kpi\pi}{n}} &amp;lt;/math&amp;gt;. Результатом данного преобразования (&amp;lt;math&amp;gt;E*X&amp;lt;/math&amp;gt;) будет вектор &amp;lt;math&amp;gt;Y=(y_{1},...,y_{n-1})^{T}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2kpi\pi}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;. Данная формула является классической формулой ДПФ. Известно, что в общем случае умножение матрицы на вектор требует &amp;lt;math&amp;gt;O(n^{2})&amp;lt;/math&amp;gt; арифметических операций, поэтому возникает вопрос о сокращении данного количества.&lt;br /&gt;
&lt;br /&gt;
Главная идея БПФ состоит в сокращении многочисленных повторов сложения и умножения, которые выполняются в ДПФ (повторяющиеся множители: &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; Например при условии, что  &amp;lt;math&amp;gt;(pk)mod(n)=const&amp;lt;/math&amp;gt;, при всех &amp;lt;math&amp;gt;k=\overline{0,n-1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p=\overline{0,n-1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
За счет использования данного факта, происходит значительное сокращение количества вычислительных операций. В данной статье используется алгоритм Cooley – Tukey, который предполагает собой разбиение исходного множества входных сигналов на два равных подмножества. При этом множество входных сигналов должно быть степенью двойки. Далее операция деления множества продолжается для образовавшихся подмножеств, и так далее. В результате над каждым из полученных подмножеств выполняется БПФ. Данные вычисления можно выполнять независимо друг от друга. Этот факт используется для реализации параллельной версии алгоритма. &lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
*Пусть &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;\omega _{n}^{kp}&amp;lt;/math&amp;gt;.&lt;br /&gt;
*Разобьем исходный сигнал &amp;lt;math&amp;gt;x(n)&amp;lt;/math&amp;gt; на два подмножества, с четными &amp;lt;math&amp;gt;x_{1}(2n)&amp;lt;/math&amp;gt; и нечетными &amp;lt;math&amp;gt;x_{2}(2n+1)&amp;lt;/math&amp;gt; номерами.&lt;br /&gt;
Тогда получаем: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}\omega _{n}^{kp}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{(2k+1)p}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выпишем два свойства омеги:&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{\frac{n}{2}}=e^{2(\frac{-2i\pi}{n})}=\omega_{n}^{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{n}^{\frac{n}{2}}=e^{\frac{-2ni\pi}{2n}}=e^{-i\pi}=-1&amp;lt;/math&amp;gt;&lt;br /&gt;
Воспользуемся первым: &amp;lt;math&amp;gt;y_{p}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{2kp}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;. Или, если обозначить &amp;lt;math&amp;gt;y_{1}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;y_{2}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, то получим формулу для вычисления координат изображения БПФ при &amp;lt;math&amp;gt;p=\overline{0...n/2}&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;y_(p)=y_{1}(p)+\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;math&amp;gt;y(p); y_{1}(p); y_{2}(p)&amp;lt;/math&amp;gt; обладают свойством периодичности, с периодом &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;. Покажем это для &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;y_{1}(p+\frac{n}{2})=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{k(p+\frac{n}{2})}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}\omega_{\frac{n}{2}}^{\frac{kn}{2}}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как: &amp;lt;math&amp;gt;\omega_{\frac{n}{2}}^{\frac{kn}{2}}=e^{\frac{-4kni\pi}{2n}}=e^{-2ki\pi}=1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее необходимо определить &amp;lt;math&amp;gt;y(p)&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;p\geq \frac{n}{2}&amp;lt;/math&amp;gt;. Теперь воспользуемся свойством периодичности обнаруженными нами ранее: &amp;lt;math&amp;gt;y(p+\frac{n}{2})=y_{1}(p+\frac{n}{2})+\omega_{n}^{(p+\frac{n}{2})}y_{2}(p+\frac{n}{2})&amp;lt;/math&amp;gt;. Воспользуемся вторым свойством омеги: &amp;lt;math&amp;gt;y(p)=y_{1}(p)-\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;. Все выкладки, приведенные выше, относятся ко второму этапу алгоритма Cooley – Tukey, который был использован в данной статье. Последующие шаги данного этапа делят подмножества полученные на предыдущем шаге вычисления БПФ.&lt;br /&gt;
&lt;br /&gt;
В конечном итоге вышеупомянутый алгоритм сводится к двум этапам:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Бит-реверсирование&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Бит-реверсирование – это преобразование двоичного числа путем изменения порядка следования бит в нем на противоположный. Бит-реверсирование применимо только к индексам элементов массива и предназначено для изменения порядка следования этих элементов, при этом значения самих элементов не изменяются. Так, к примеру, четырехмерный вектор &amp;lt;math&amp;gt;(x_{0},x_{1},x_{2},x_{3})&amp;lt;/math&amp;gt; изменится на &amp;lt;math&amp;gt;(x_{0},x_{2},x_{1},x_{3})&amp;lt;/math&amp;gt;. Данная процедура необходима для разделения исходного сигнала на элементы подмножеств с четными и нечетными номерами, которые необходимы для вычисления &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычисление БПФ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
После выполнения этапа бит-реверсирования выполняются вычисления проиллюстрированные &#039;&#039;на рисунке 1&#039;&#039; в разделе [[#Информационный граф|Информационный граф]]. Рисунок 1 приведен для случая четырехмерного входного сигнала. Он предполагает совершение двух этапов. В общем же количество этапов равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Каждый этап содержит в себе вычисление одной и той же операции &amp;quot;бабочки&amp;quot;, где на вход подаются два числа &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, на выходе же получаются &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;- это &#039;&#039;коэффициент поворота&#039;&#039;. Общая формула для коэффициента поворота: &amp;lt;math&amp;gt;e^{\frac{-ji\pi}{k}}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- размер шага &amp;quot;бабочки&amp;quot;, &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;- номер итерации вычислений над группой элементов, размером &amp;lt;math&amp;gt;2k&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0\leq j &amp;lt; k )&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из двух вышеописанных этапов БПФ, бит-реверсирование является самым быстровыполнимым. Поэтому вычислительное ядро алгоритма будет лежать именно во втором этапе. А именно, им будет являться вычисление &amp;quot;бабочек&amp;quot;. Количество этапов выполнения БПФ, как описывалось выше, равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Учитывая количество этапов, размерность входного сигнала и идею деления на подмножества, всего &amp;quot;бабочек&amp;quot; получается &amp;lt;math&amp;gt;\frac{nlog_{2}n}{2}&amp;lt;/math&amp;gt;. При этом операция умножения выполняется &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; раз. Это связано с тем, что результаты вычисления &amp;quot;бабочки&amp;quot; &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из всего вышесказанного заключаем, что макроструктуру алгоритма легче всего описать при помощи рекурсии: &lt;br /&gt;
*&amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; преобразований Фурье порядка 2&lt;br /&gt;
*умножение &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; пар комплексных чисел&lt;br /&gt;
*&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; комплексных сложений &lt;br /&gt;
*снова преобразование Фурье&lt;br /&gt;
Также необходимо заметить, что количеством рекурсий будет количество этапов выполнения БПФ, то есть глубина рекурсии будет равняться &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;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;
*процесс очистки памяти&lt;br /&gt;
*выполнение операции &amp;quot;бабочка&amp;quot;&lt;br /&gt;
*выполнение БПФ&lt;br /&gt;
*функция вывода результата (для тестирования)&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;главный программный код БПФ:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; *inputSignal = NULL;   &lt;br /&gt;
	complex&amp;lt;double&amp;gt; *outputSignal = NULL; &lt;br /&gt;
	int size = 0;&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;FFT. Serial version.&amp;quot; &amp;lt;&amp;lt; endl;       &lt;br /&gt;
	ProcessInitialization(inputSignal, outputSignal, size);&lt;br /&gt;
	bit(inputSignal, outputSignal, size);&lt;br /&gt;
	SerialFFTCalculation(outputSignal, size);&lt;br /&gt;
	PrintSignal(outputSignal, size); //в случае тестирования&lt;br /&gt;
	ProcessTermination(inputSignal, outputSignal);&lt;br /&gt;
	cin.get();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== функция бит-реверсирования ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void bit(complex&amp;lt;double&amp;gt; inputSignal[], complex&amp;lt;double&amp;gt; *outputSignal, int size)&lt;br /&gt;
{ // процесс бит-реверсирования&lt;br /&gt;
	int j = 0, i = 0;&lt;br /&gt;
	while (i &amp;lt; size) &lt;br /&gt;
	{&lt;br /&gt;
		if (j &amp;gt; i) &lt;br /&gt;
		{ &lt;br /&gt;
			outputSignal[i] = inputSignal[j];       &lt;br /&gt;
			outputSignal[j] = inputSignal[i]; &lt;br /&gt;
		}&lt;br /&gt;
		else       &lt;br /&gt;
			if (j == i)          &lt;br /&gt;
				outputSignal[i] = inputSignal[i];&lt;br /&gt;
		&lt;br /&gt;
		int m = size &amp;gt;&amp;gt; 1;     &lt;br /&gt;
		while ((m &amp;gt;= 1) &amp;amp;&amp;amp; (j &amp;gt;= m)) &lt;br /&gt;
		{ &lt;br /&gt;
			j -= m;       &lt;br /&gt;
			m = m &amp;gt;&amp;gt; 1; &lt;br /&gt;
		}     &lt;br /&gt;
		j += m;     &lt;br /&gt;
		i++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация размерности входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessInitialization(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal, int &amp;amp;size) &lt;br /&gt;
{&lt;br /&gt;
	// инициализация размера входного сигнала  &lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		cout &amp;lt;&amp;lt; &amp;quot;Enter the input signal length: &amp;quot;;&lt;br /&gt;
		cin &amp;gt;&amp;gt; size;&lt;br /&gt;
		if (size &amp;lt; 4)&lt;br /&gt;
			cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be &amp;gt;= 4&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			int tmpSize = size;&lt;br /&gt;
			while (tmpSize != 1)&lt;br /&gt;
			{&lt;br /&gt;
				if (tmpSize % 2 != 0)&lt;br /&gt;
				{&lt;br /&gt;
					cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be powers of two&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
					size = -1;&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				tmpSize /= 2;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while (size &amp;lt; 4);  &lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Input signal length = &amp;quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl; &lt;br /&gt;
	inputSignal = new complex&amp;lt;double&amp;gt;[size];   &lt;br /&gt;
	outputSignal = new complex&amp;lt;double&amp;gt;[size];&lt;br /&gt;
	DummyDataInitialization(inputSignal, size);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void DummyDataInitialization(complex&amp;lt;double&amp;gt;* mas, int size)&lt;br /&gt;
{	&lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)&lt;br /&gt;
	{&lt;br /&gt;
		mas[i] = int(rand() % 100);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== процесс очистки памяти ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessTermination(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal) &lt;br /&gt;
{ &lt;br /&gt;
	//очистка памяти&lt;br /&gt;
	delete[] inputSignal;   &lt;br /&gt;
	inputSignal = NULL;   &lt;br /&gt;
	delete[] outputSignal;   &lt;br /&gt;
	outputSignal = NULL; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение операции &amp;quot;бабочка&amp;quot; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void Butterfly(complex&amp;lt;double&amp;gt; *signal, complex&amp;lt;double&amp;gt; &amp;amp;u,int offset, int butterflySize)&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; tem = signal[offset + butterflySize] * u;&lt;br /&gt;
	signal[offset + butterflySize] = signal[offset] - tem;   &lt;br /&gt;
	signal[offset] += tem;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение БПФ ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void SerialFFTCalculation(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	int m = 0;   &lt;br /&gt;
	for (int tmp_size = size; tmp_size &amp;gt; 1; tmp_size /= 2, m++);&lt;br /&gt;
	for (int p = 0; p &amp;lt; m; p++)&lt;br /&gt;
	{&lt;br /&gt;
		int butterflyOffset = 1 &amp;lt;&amp;lt; (p + 1);&lt;br /&gt;
		int butterflySize = butterflyOffset &amp;gt;&amp;gt; 1;&lt;br /&gt;
		double coeff = M_PI / butterflySize;&lt;br /&gt;
&lt;br /&gt;
		for (int i = 0; i &amp;lt; size / butterflyOffset; i++)&lt;br /&gt;
			for (int j = 0; j &amp;lt; butterflySize; j++)&lt;br /&gt;
				Butterfly(signal,complex&amp;lt;double&amp;gt;(cos(-j*coeff),sin(-j*coeff)),j+i*butterflyOffset,butterflySize);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== функция вывода результата (для тестирования) ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void PrintSignal(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Result signal&amp;quot; &amp;lt;&amp;lt; endl;   &lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)     &lt;br /&gt;
		cout &amp;lt;&amp;lt; signal[i] &amp;lt;&amp;lt; endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Рассмотренный алгоритм  Cooley – Tukey выполняется за &amp;lt;math&amp;gt;nlog_{2}n&amp;lt;/math&amp;gt; операций комплексного сложения и &amp;lt;math&amp;gt;\frac{n}{2}log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения. Таким образом последовательная сложность алгоритма является &#039;&#039;&#039;линейно-логарифмической&#039;&#039;&#039;.&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[file:Граф.jpg|center|thumb|800px|Рисунок 1. Алгоритм Cooley – Tukey для n=4, при предварительно сделанном бит-реверсировании]]&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
[[file:Парал.jpg|center|thumb|800px|Рисунок 2. Распараллеливание алгоритма Cooley – Tukey для n=8]]&lt;br /&gt;
Рисунок 2 получен из информационного графа, главной идеей которого является процедура деления множеств полученных на предыдущем шаге на два равных по мощности подмножества и применение к ним БПФ. То есть процесс вычисления распределяется между &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; процессорами. И так как при последовательной реализации алгоритма было &amp;lt;math&amp;gt;\frac{n}{2}\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;n\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. Получается, что при параллельной реализации алгоритма мы будем иметь &amp;lt;math&amp;gt;\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;2\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. При этом, если используется функция HT(Hyper threading), то параллельная сложность быстрого преобразования Фурье (базовый алгоритм Кули-Тьюки) для векторов с длиной, равной степени двойки составляет &amp;lt;math&amp;gt;\log_{2}n&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;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{R}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
*&#039;&#039;&#039;Комплексное ДФП&#039;&#039;&#039;&lt;br /&gt;
**входные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
*Соотношение последовательной и параллельной сложности является &#039;&#039;линейным&#039;&#039;. Это хорошо видно из пункта [[#Ресурс параллелизма алгоритма|ресурс параллелизма алгоритма]].&lt;br /&gt;
*Вычислительная мощность алгоритма является &#039;&#039;логарифмической&#039;&#039;.&lt;br /&gt;
*Устойчивость. &lt;br /&gt;
**Рассмотрим вопрос об устойчивости поближе. Так как БПФ отличается от ДПФ простым лишь сокращением одинаковых операций, то вопрос устойчивости можно рассматривать на примере ДПФ. Так, к примеру, возьмем формулу ДПФ и добавим возмущение: &amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}(x_{k}+\varepsilon )e^{\frac{-2*\pi*k*p*i}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;. После чего преобразуем ее: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2*\pi*k*p*i}{n}}+\varepsilon\sum_{k=0}^{n-1} e^{\frac{-2*\pi*k*p*i}{n}}&amp;lt;/math&amp;gt; и оценим вторую сумму, которая является геометрической прогрессией со знаменателем &amp;lt;math&amp;gt;q=e^{\frac{-2*\pi*p*i}{n}}&amp;lt;/math&amp;gt;. Таким образом оцениваемая сумма будет равняться: &amp;lt;math&amp;gt;\varepsilon\frac{1-e^{\frac{-2*\pi*p*i*(n-1))}{n}}}{1-e^{\frac{-2*\pi*p*i}{n}}}&amp;lt;/math&amp;gt;. При достаточно больших размерностях входного сигнала данное выражение сводится к: &amp;lt;math&amp;gt;\varepsilon (1-e^{-2*\pi*p*i})&amp;lt;/math&amp;gt;. В тригонометрической форме данного выражения: &amp;lt;math&amp;gt;\varepsilon (cos(\frac{2*\pi*p*(n-1)}{n})-i*sin(\frac{2*\pi*p*(n-1)}{n}))&amp;lt;/math&amp;gt; видно, что принимая во внимание целочисленность индексов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; получается 0. Таким образом, мы можем говорить об устойчивости алгоритма в целом, и абсолютной устойчивости алгоритма в асимптотике.&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;
Вычисления проводились на суперкомпьютере «Ломоносов». &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Основные технические характеристики суперкомпьютера &amp;quot;Ломоносов&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
*Пиковая производительность: 1,7 Пфлопс&lt;br /&gt;
*Производительность на тесте Linpack: 901.9 Тфлопс&lt;br /&gt;
*Число вычислительных узлов х86: 5 104&lt;br /&gt;
*Число графических вычислительных узлов: 1 065&lt;br /&gt;
*Число вычислительных узлов PowerXCell: 30&lt;br /&gt;
*Число процессоров/ядер x86: 12 346 / 52 168&lt;br /&gt;
*Число графических ядер: 954 240&lt;br /&gt;
*Оперативная память: 92 ТБ&lt;br /&gt;
*Общий объем дисковой памяти вычислителя: 1,75 ПБ&lt;br /&gt;
*Основной тип процессора: Intel Xeon  X5570/Intel Xeon 5670, Nvidia X2070&lt;br /&gt;
*Число типов вычислительных узлов: 8&lt;br /&gt;
*Основной тип вычислительных узлов: TB2-XN&lt;br /&gt;
*Все узлы связаны тремя независимыми сетями:&lt;br /&gt;
**Системная сеть - QDR InfiniBand, 40 Гбит/сек&lt;br /&gt;
**Сервисная сеть - Ethernet, 10 Гбит/сек, 1 Гбит/сек и 100 Мбит/сек &lt;br /&gt;
**Управляющая сеть - Ethernet, 10 Гбит/сек и 1 Гбит/сек &lt;br /&gt;
**Сеть барьерной синхронизации и сеть глобальных прерываний, Т-Платформы&lt;br /&gt;
При работе была использована библиотека FFTW последней версии (3.3.5). Скрипт mpicxx с компилятором Intel. Вычислительные узлы х86. Код исследуемой программы представлен в разделе [[#Существующие реализации алгоритма|Существующие реализации алгоритма]]. &lt;br /&gt;
[[file:масш.jpg|center|thumb|1000px|Рисунок 3. Параллельная реализация алгоритма. Изменение эффективности(%)в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
Как описывалось выше, каждый процессор получает достаточно низкий объем работы, что приводит к неэффективному распараллеливанию алгоритма (высокие затраты на передачу данных между процессорами по сравнению с вычислениями на них). Поэтому необходимо находить оптимальное количество процессоров для каждой отдельно взятой задачи. В данном примере, при размерности входного сигнала менее 262144, оптимальное количество процессоров составляет 1. При размерности 262144-4194304 оптимум достигался при 8 процессорах. И при размерности более 4194304- 16 процессоров. &lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Самой распространенной и известной реализацией дискретного БПФ является библиотека [http://www.fftw.org/ FFTW], разработанная Маттео Фриго и Стивеном Г. Джонсоном в Массачусетском технологическом институте. Данная библиотека существует для языков C и FORTRAN и позволяет производить вычисления в одном или нескольких измерениях. Входной сигнал может быть как действительным так и комплексным, а его размерность не обязательно должна быть степенью двойки. На данный момент последняя версия FFTW 3.3.5. Другие особенности библиотеки:&lt;br /&gt;
*Скорость. (Поддержка SSE / SSE2 / Altivec, начиная с версии 3.0. Версия 3.3.1 поддерживает AVX и ARM Neon.)&lt;br /&gt;
*Параллельные вычисления (OpenMP, MPI).&lt;br /&gt;
*[http://www.fftw.org/fftw3_doc/ Документация] в форматах HTML, PDF.&lt;br /&gt;
*Данное программное обеспечение является свободным и распространяется по лицензии GNU (General Public License).&lt;br /&gt;
Здесь приводится пример реализации прямого БПФ для комплексных данных входного сигнала на языке C++. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fftw3-mpi.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
# include &amp;lt;time.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	const ptrdiff_t N0 = 4194304;&lt;br /&gt;
	fftw_plan planForw;&lt;br /&gt;
	fftw_complex *data, *dataOut;&lt;br /&gt;
	ptrdiff_t alloc_local, local_ni, local_i_start, i, j, local_no, local_o_start;&lt;br /&gt;
	int index, size;&lt;br /&gt;
	double startwtime, endwtime;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	fftw_mpi_init();&lt;br /&gt;
	MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;index);&lt;br /&gt;
	MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;size);&lt;br /&gt;
&lt;br /&gt;
	/* get local data size and allocate */&lt;br /&gt;
	alloc_local = fftw_mpi_local_size_1d(N0, MPI_COMM_WORLD, FFTW_FORWARD, FFTW_ESTIMATE,&lt;br /&gt;
		&amp;amp;local_ni, &amp;amp;local_i_start, &amp;amp;local_no, &amp;amp;local_o_start);&lt;br /&gt;
	data = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	dataOut = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	/* create plan  */&lt;br /&gt;
	planForw = fftw_mpi_plan_dft_1d(N0, data, dataOut, MPI_COMM_WORLD,&lt;br /&gt;
		FFTW_FORWARD, FFTW_ESTIMATE);&lt;br /&gt;
	/* initialize data to some function my_function(x,y) */&lt;br /&gt;
	for (i = 0; i &amp;lt; local_ni; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		data[i][0] = rand() / (double)RAND_MAX;&lt;br /&gt;
		data[i][1] = rand() / (double)RAND_MAX;&lt;br /&gt;
	}&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		startwtime = MPI_Wtime();&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_execute(planForw);&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		endwtime = MPI_Wtime();&lt;br /&gt;
		printf(&amp;quot;wall clock time = %f\n&amp;quot;,&lt;br /&gt;
			endwtime - startwtime);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_destroy_plan(planForw);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[http://www.fftw.org/download.html Скачав] библиотеку и установив ее на рабочий компьютер, можно поэкспериментировать, используя вышеописанный исходный код. При этом, на стадии компиляции необходимо указывать директорию, где находятся заголовочные файлы и директорию, где находятся библиотечные файлы. Пример: mpicxx name.cpp -I/path/to/include/files -L/path/to/lib/files  -lfftw3_mpi -lfftw3 -lm. Для версии 2.1.4 описание на русском языке приводится [https://parallel.ru/cluster/fftw.html здесь].&lt;br /&gt;
&lt;br /&gt;
Остальные реализации:&lt;br /&gt;
*[http://www.gnu.org/software/gsl/ GSL - GNU Scientific Library]&lt;br /&gt;
*[https://developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
*[http://www-03.ibm.com/systems/power/software/essl/ Engineering and Scientific Subroutine Library (ESSL) and Parallel ESSL]&lt;br /&gt;
=Литература=&lt;br /&gt;
[1] Бахвалов Н. С., Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний &lt;br /&gt;
&lt;br /&gt;
[2] Г. Нуссбаумер. Быстрое преобразование Фурье и алгоритмы вычисления сверток &lt;br /&gt;
&lt;br /&gt;
[3] А.О. Лапаев Параллельное вычисление дискретного преобразования Фурье полинома в простом поле&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Victoria_Stepanischeva/%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_(FFT)&amp;diff=19469</id>
		<title>Участник:Victoria Stepanischeva/Быстрое преобразование Фурье (FFT)</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:Victoria_Stepanischeva/%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_(FFT)&amp;diff=19469"/>
		<updated>2016-11-29T14:19:44Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Kronberg}}&lt;br /&gt;
Авторы:&lt;br /&gt;
&lt;br /&gt;
* [[Участник:Victoria_Stepanischeva|Степанищева В.С.]], группа 601 (Общее описание алгоритма, Вычислителное ядро алгоритма, Макроструктура алгоритма, Информационный граф, Ресурс параллелизма алгоритма)&lt;br /&gt;
* [[Участник:Igor_Munkin|Мунькин И.А.]], группа 601 (Математическое описание алгоритма, Схема реализации последовательного алгоритма, Последовательная сложность алгоритма, Входные и выходные данные алгоритма, Свойства алгоритма, Существующие реализации алгоритма)&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразования Фурье&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \; log \, N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;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;
}}&lt;br /&gt;
&lt;br /&gt;
==Свойства и структура алгоритма==&lt;br /&gt;
&lt;br /&gt;
===Общее описание алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм &#039;&#039;&#039;быстрого преобразования Фурье&#039;&#039;&#039;, включая его рекурсивную&lt;br /&gt;
реализацию, был использован&lt;br /&gt;
[https://en.wikipedia.org/wiki/Carl_Friedrich_Gauss Карлом Фридрихом Гауссом]&lt;br /&gt;
еще в 1805 году для интерполяции траекторий астероидов, однако эта работа не&lt;br /&gt;
получила широкого признания. Алгоритм быстрого преобразования Фурье обрел свою&lt;br /&gt;
популярность благодаря работе&lt;br /&gt;
[https://en.wikipedia.org/wiki/James_Cooley James Cooley], работающего в то&lt;br /&gt;
время в [https://en.wikipedia.org/wiki/IBM IBM], и&lt;br /&gt;
[https://en.wikipedia.org/wiki/John_Tukey John Tukey], параллельно принимающего&lt;br /&gt;
участие в разработках [https://en.wikipedia.org/wiki/Bell_Labs Bell Labs] и&lt;br /&gt;
работающего в&lt;br /&gt;
[https://en.wikipedia.org/wiki/Princeton_University Принстонском университетe].&lt;br /&gt;
В их статье, изданной в 1965 году, они представили подробное описание алгоритма&lt;br /&gt;
и его программной реализации.&lt;br /&gt;
&lt;br /&gt;
====Разновидности алгоритма быстрого преобразования Фурье====&lt;br /&gt;
Помимо традиционного и наиболее распространенного алгоритма&lt;br /&gt;
[https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Cooley-Tukey]&lt;br /&gt;
существует также несколько других часто используемых реализаций алгоритма&lt;br /&gt;
быстрого преобразования Фурье.&lt;br /&gt;
&lt;br /&gt;
=====PFA-алгоритм=====&lt;br /&gt;
Алгоритм [https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm prime-factor]&lt;br /&gt;
(PFA), также называемый алгоритмом Good-Thomas, представляет собой алгоритм&lt;br /&gt;
быстрого преобразования Фурье, который представляет дискретное преобразование&lt;br /&gt;
Фурье размера &amp;lt;math&amp;gt;N = N_1 \cdot N_2&amp;lt;/math&amp;gt; в виде двумерного&lt;br /&gt;
&amp;lt;math&amp;gt;N_1 \times 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;
применением PFA либо используя какой-либо иной метод быстрого преобразования&lt;br /&gt;
Фурье.&lt;br /&gt;
&lt;br /&gt;
=====Алгоритм Bluestein=====&lt;br /&gt;
Алгоритм&lt;br /&gt;
[https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Bluestein],&lt;br /&gt;
обычно называемый &amp;lt;math&amp;gt;\mathcal{Z}&amp;lt;/math&amp;gt;-чирп алгоритмом, представляет собой&lt;br /&gt;
алгоритм быстрого преобразования Фурье, который вычисляет дискретное&lt;br /&gt;
преобразование Фурье различных размеров с помощью представления оного в виде&lt;br /&gt;
линейной свертки. Другой алгоритм быстрого преобразования Фурье, использующий&lt;br /&gt;
свойства простых чисел, алгоритм&lt;br /&gt;
[https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Rader], также вычисляет&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;
* Численное интегрирование&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;
пространственных фильтров достаточно много: линейные, медианные, изотропные и&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;
области. Для этого с помощью преобразования Фурье происходит получение спектра&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;
изображения, повышение резкости и др.&lt;br /&gt;
&lt;br /&gt;
=====Цифровое сжатие=====&lt;br /&gt;
Одним из первых форматов файла-контейнера для хранения записи оцифрованного&lt;br /&gt;
аудипотока является формат WAV. Этот формат чаще всего используется в качестве&lt;br /&gt;
оболочки для несжатого звука. При таком кодировании в WAV-файле хранится полная&lt;br /&gt;
информация об исходном звуке, оцифрованном и проквантованном с некоторой&lt;br /&gt;
частотой, например 44 кГц. Этой информации абсолютно достаточно для&lt;br /&gt;
воспроизведения всех частот исходного сигнала, меньших половины частоты&lt;br /&gt;
квантования (по [https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%9A%D0%BE%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0 теореме Котельникова] ). При этом одна&lt;br /&gt;
минута записи занимает около 15 Мб, поэтому в настоящее время очень популярным&lt;br /&gt;
стал формат кодирования MPEG Layer-3 со сжатием.&lt;br /&gt;
&lt;br /&gt;
При сжатии кодеком MPEG Layer-3 исходный звуковой сигнал разбивается на&lt;br /&gt;
фрагменты, длительностью по 50 миллисекунд, каждый из которых анализируется&lt;br /&gt;
отдельно. При анализе фрагмент раскладывается на гармоники по методу Фурье, из&lt;br /&gt;
которых в соответствии с теорией восприятия звука человеческим ухом&lt;br /&gt;
выбрасываются те гармоники, которые человек хуже воспринимает на фоне остальных:&lt;br /&gt;
* более тихие гармоники на фоне более громких&lt;br /&gt;
* звуки, замаскированные вследствие инертности слуха (так, например, если за очень громким хлопком, с задержкой в долю секунды, пойдет какой-то иной кратковременный сигнал, то его слышно не будет)&lt;br /&gt;
&lt;br /&gt;
При воспроизведении файла закодированного MPEG Layer-3 производится обратное&lt;br /&gt;
преобразование, при котором оставшиеся гармоники вновь преобразуются в звуковую&lt;br /&gt;
волну. Поскольку часть информации об исходном сигнале была удалена, звук&lt;br /&gt;
получается не совпадающий с исходным. Одной из основных характеристик качества&lt;br /&gt;
аудио потока является битрейт.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Битрейт&#039;&#039; - это объем информации на каждую секунду записи:&lt;br /&gt;
* чем он меньше, тем меньший размер имеют файлы с одинаковой по времени длине&lt;br /&gt;
* чем он меньше, тем большее количество &amp;quot;лишних&amp;quot; гармоник приходится удалять&lt;br /&gt;
&lt;br /&gt;
===Математическое описание алгоритма===&lt;br /&gt;
Пусть задан вектор действительных чисел&lt;br /&gt;
&amp;lt;math&amp;gt;\left(f_1, f_2, f_3, \dots, f_{N-1} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дискретное преобразование Фурье определено следующим образом&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
F_k = \sum_{n = 0}^{N - 1}\, f_n\, e^{\left(\frac{i2\pi kn}{N}\right)}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt;W_N = e^{\left(\frac{i2\pi}{N}\right)}&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;-ый&lt;br /&gt;
корень единицы. Таким образом &amp;lt;math&amp;gt;(W_N)^N = 1&amp;lt;/math&amp;gt;. Несложные математические&lt;br /&gt;
преобразования приводят формулу дискретного преобразования Фурье к следующему&lt;br /&gt;
виду&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
F_k = \sum_{n = 0}^{N - 1}\, f_n\, W_n^{-nk}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, алгоритм вычисления дискретного преобразования Фурье состоит из&lt;br /&gt;
2 шагов:&lt;br /&gt;
# Вычисление поворотных коэффициентов &amp;lt;math&amp;gt;W_n^{-nk},\; n,k = 0, 1, \dots, N-1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Вычисление коэффициентов Фурье &amp;lt;math&amp;gt;F_k,\; k = 0, 1, \dots, N-1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В основе концепции быстрого преобразования Фурье лежит идея отдельного расчета четных и&lt;br /&gt;
нечетных коэффициентов с использованием векторов меньшего размера. Разобъем дискретное&lt;br /&gt;
преобразование Фурье следующим образом&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
F_k = \sum_{n = 0}^{N - 1}\, f_n\, W_{N}^{-nk}&lt;br /&gt;
        = \sum_{n = 0}^{N/2 - 1}\, g_n\, W_{N/2}^{-nk}&lt;br /&gt;
        + \sum_{n = 0}^{N/2 - 1}\, h_n\, W_{N/2}^{-nk}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следовательно, четные и нечетные коэффициенты Фурье могут быть записаны в&lt;br /&gt;
следующем виде:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
F_{2k} = \sum_{n = 0}^{N/2 - 1}\, \underbrace{&lt;br /&gt;
        \left(f_n + f_{n + N/2}\right)&lt;br /&gt;
}_{g_n} \, W_{N/2}^{-nk}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
F_{2k+1} = \sum_{n = 0}^{N/2 - 1}\, \underbrace{&lt;br /&gt;
        \left((f_n + f_{n + N/2}) W^{-n}\right)&lt;br /&gt;
}_{h_n} \, W_{N/2}^{-nk}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, вычисление дискретного преобразования Фурье от &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
коэффициентов сводится к вычислению 2 дискретных преобразований Фурье от&lt;br /&gt;
&amp;lt;math&amp;gt;N/2&amp;lt;/math&amp;gt; коэффициентов. Применяя данную идею рекурсивно, получаем&lt;br /&gt;
алгоритм быстрого преобразования Фурье.&lt;br /&gt;
&lt;br /&gt;
===Вычислительное ядро алгоритма===&lt;br /&gt;
&lt;br /&gt;
[[File:Stepmoon_buttefly.png|left|200px]]&lt;br /&gt;
В случае размера входных данных равному &amp;lt;math&amp;gt;N = 2^M&amp;lt;/math&amp;gt; вычислительным&lt;br /&gt;
ядром алгоритма являются так называемые &amp;quot;бабочки&amp;quot;. В элементарном случае&lt;br /&gt;
&amp;quot;бабочка&amp;quot; является двухточечным преобразованием.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
g_n = f_n + f_{n + N/2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
h_n = \left(f_n - f_{n + N/2}\right)\, W^{-n}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Макроструктура алгоритма===&lt;br /&gt;
Описываемый алгоритм работает по принципу&lt;br /&gt;
[https://en.wikipedia.org/wiki/Divide_and_conquer_algorithms &amp;quot;разделяй и властвуй&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
Рассмотрим общую структуру для входных данных размера &amp;lt;math&amp;gt;N = N1 \cdot N2&amp;lt;/math&amp;gt;&lt;br /&gt;
# Исходное множество точек разбивается на &amp;lt;math&amp;gt;N2&amp;lt;/math&amp;gt; равных подмножеств размером &amp;lt;math&amp;gt;N1&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Над каждым из полученных множеств выполняется быстрое преобразование Фурье. Для его вычисления над каждым из подмножеств рассматриваемого множества снова выполняется деление на равные части. Такое разбиение продолжается до тех пор, пока количество элементов, над которыми необходимо выполнить быстрое преобразование Фурье, не будет равно одному.&lt;br /&gt;
# После этого выполняется объединение всех промежуточных результатов. Полученные на предыдущем шаге множества объединяются за линейное время.&lt;br /&gt;
&lt;br /&gt;
Ниже представлена схема алгоритма Cooley-Tukey для &amp;lt;math&amp;gt;N = 2^M&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Stepmoon_macro.png|center|800px]]&lt;br /&gt;
&lt;br /&gt;
===Схема реализации последовательного алгоритма===&lt;br /&gt;
Ниже представлен прототип реализации для &amp;lt;math&amp;gt;N = 2^M&amp;lt;/math&amp;gt;.&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
use Math::Complex;&lt;br /&gt;
&lt;br /&gt;
sub fft {&lt;br /&gt;
    my $input = shift;&lt;br /&gt;
    return $input if @{ $input } == 1;&lt;br /&gt;
    my @w = map {&lt;br /&gt;
        my $theta = 2 * pi * $_ / @{ $input };&lt;br /&gt;
        Math::Complex-&amp;gt;make(cos($theta), sin($theta));&lt;br /&gt;
    } (0 .. $#input);&lt;br /&gt;
    my $odd  = fft([ map { $input-&amp;gt;[2 * $_ + 1] }, (0 .. @{ $input } / 2 - 1) ]);&lt;br /&gt;
    my $even = fft([ map { $input-&amp;gt;[2 * $_] }, (0 .. @{ $input } / 2 - 1) ]);&lt;br /&gt;
    return [ map {&lt;br /&gt;
        $even-&amp;gt;[$_ % @{ $even }] + $w[$_] * $odd-&amp;gt;[$_ % @{ $odd }];&lt;br /&gt;
    } (0 .. $#input) ];&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Последовательная сложность алгоритма===&lt;br /&gt;
Если считать только главные члены выражений для последовательной сложности алгоритма, то простой алгоритм Cooley-Tukey может быть выполнен за &amp;lt;math&amp;gt;N\, log\, N&amp;lt;/math&amp;gt; операций. Таким образом, простой алгоритм Cooley-Tukey относится к линейно-логарифмическому классу по последовательной сложности.&lt;br /&gt;
&lt;br /&gt;
===Информационный граф===&lt;br /&gt;
Ниже представлен информационный граф для простого алгоритма Cooley-Tukey для &amp;lt;math&amp;gt;N = 8&amp;lt;/math&amp;gt;,   &amp;lt;math&amp;gt;\;h&amp;lt;/math&amp;gt; - операция сложения двух комплексных чисел, &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; - операция вычитания двух комплексных чисел и умножения результата вычитания на комплексное число (поворотный множитель). В последнем столбце операций умножения не производится.&lt;br /&gt;
&lt;br /&gt;
[[File:Stepmoon_Info_graph.png|center|500px]]&lt;br /&gt;
&lt;br /&gt;
===Ресурс параллелизма алгоритма===&lt;br /&gt;
Простой алгоритм Cooley-Tukey имеет критический путь, состоящий из&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;&lt;br /&gt;
операций комплексного умножения (основание логарифма зависит от выбранного на&lt;br /&gt;
каждом шаге алгоритма разбиения). Следовательно, простой алгоритм быстрого&lt;br /&gt;
преобразования Фурье в общем случае может быть отнесен к логарифмическому классу&lt;br /&gt;
по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
===Входные и выходные данные алгоритма===&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; измеренные значения сигнала в дискретных временных точках с номерами &amp;lt;math&amp;gt;0 \dots N&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;f \in \mathbb{R}^N&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;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; вектор компонент спектра исходного сигнала: &amp;lt;math&amp;gt;F \in \mathbb{C}^N&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;
Обработку подмножеств, получаемых после разбиения, можно выполнять независимо,&lt;br /&gt;
этот факт можно использовать при распараллеливании алгоритма.&lt;br /&gt;
В случае рекурсивной реализации глубина рекурсии составит &amp;lt;math&amp;gt;log\, N&amp;lt;/math&amp;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;
===Особенности реализации последовательного алгоритма===&lt;br /&gt;
&lt;br /&gt;
===Локальность данных и вычислений===&lt;br /&gt;
&lt;br /&gt;
===Возможные способы и особенности параллельной реализации алгоритма===&lt;br /&gt;
&lt;br /&gt;
===Масштабируемость алгоритма и его реализации===&lt;br /&gt;
Набор и границы значений изменяемых параметров запуска реализации алгоритма:&lt;br /&gt;
* число процессоров [ 5 : 45 ] с шагом 5&lt;br /&gt;
* размер вектора [ 256 : 16384 ] с шагом равным степени двойки&lt;br /&gt;
&lt;br /&gt;
Условия экспериментов:&lt;br /&gt;
* Для тестирования была использована библиотека FFTW версии 3.3.5. Библиотека была сконфигурирована и собрана локально&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
./configure --enable-mpi --prefix=/mnt/data/users/dm4/vol12/munkinigor_1854/fftw_mpi_build_instalation&lt;br /&gt;
make install&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Исходный код был скомпилирован с помощью компилятора mpicc из модуля openmpi/1.8.4-gcc&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
module add openmpi/1.8.4-gcc&lt;br /&gt;
mpicc test.c -Ifftw_mpi_build_instalation/include/ -Lfftw_mpi_build_instalation/lib/ -lm -lfftw3_mpi -lfftw3 -o ../_scratch/bench&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график производительности выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
[[File:Stepmoon_performance.png|center|800px]]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/FFTW/fftw3 Текст исследуемой программы]&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://fftw.org/ FFTW], изначально разработанная сотрудниками&lt;br /&gt;
[https://en.wikipedia.org/wiki/Massachusetts_Institute_of_Technology MIT]&lt;br /&gt;
Matteo Frigo и Steven G. Johnson. На сегодняшний день исходный код библиотеки&lt;br /&gt;
распространяется под свободной лицензией&lt;br /&gt;
[https://en.wikipedia.org/wiki/GNU_General_Public_License#Version_2 GNU General Public Licence v2]&lt;br /&gt;
с помощью сервиса [https://github.com/FFTW/fftw3 Github], тем самым давая&lt;br /&gt;
возможность сообществу принимать участие в разработке. Несмотря на то, что сама&lt;br /&gt;
библиотека [http://fftw.org/faq/section2.html#whynotfortran написана на языке C]&lt;br /&gt;
существуют интерфейсы для работы с ней используя такие языки программирования&lt;br /&gt;
как Fortran, C++ и другиe.&lt;br /&gt;
&lt;br /&gt;
Для разбиения преобразований, размеры которых являются составным числом, на&lt;br /&gt;
меньшие используется одна из многочисленных реализаций алгоритма&lt;br /&gt;
[https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm Cooley-Tukey]&lt;br /&gt;
(в зависимости от разложения на простые множители и паттернов доступа к памяти).&lt;br /&gt;
В свою очередь для преобразований, размеры которых являются простыми числом&lt;br /&gt;
используется либо алгоритм&lt;br /&gt;
[https://en.wikipedia.org/wiki/Rader%27s_FFT_algorithm Raider], либо алгоритм&lt;br /&gt;
[https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm Bluestein].&lt;br /&gt;
Библиотека признана самой быстрой реализацией быстрого преобразования Фурье,&lt;br /&gt;
гарантируя на любых объемах входных данных сложность&lt;br /&gt;
&amp;lt;math&amp;gt;O \left(n\; log\, n \right)&amp;lt;/math&amp;gt;.  Существует реализация FFTW и с&lt;br /&gt;
поддержкой [https://en.wikipedia.org/wiki/Message_Passing_Interface MPI],&lt;br /&gt;
позволяющая работать с данными не помещающимися в память. Однако перегруппировка&lt;br /&gt;
данных приводит к оверхеду, которого трудно избежать при in-place вычислении&lt;br /&gt;
преобразований различных размеров.&lt;br /&gt;
&lt;br /&gt;
==Литература==&lt;br /&gt;
[1] L. Rabiner, B. Gold, &#039;&#039;&amp;quot;Theory and application of digital signal processing&amp;quot;&#039;&#039; // Prentice Hall, New Jersey, 1975.&lt;br /&gt;
&lt;br /&gt;
[2] Бахвалов Н.С., Жидков Н.П., Кобельков Г.М. &#039;&#039;&amp;quot;Численные методы&amp;quot;&#039;&#039; // БИНОМ. Лаборатория знаний, Москва, 2008.&lt;br /&gt;
&lt;br /&gt;
[3] M. Frigo, S. G. Johnson &#039;&#039;&amp;quot;FFTW Documentation&amp;quot;&#039;&#039; // [http://fftw.org/fftw3_doc/ fftw.org]&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Timokhinivan/%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&amp;diff=19468</id>
		<title>Участник:Timokhinivan/Быстрое преобразование Фурье</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:Timokhinivan/%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&amp;diff=19468"/>
		<updated>2016-11-29T14:16:43Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Kronberg}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[U:Timokhinivan|Тимохин И.В.]], ВМК МГУ.&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
В общем случае многомерное преобразование Фурье соответствует умножению на матрицу преобразования по каждой из координат, что для массива данных &amp;lt;math&amp;gt;N_1\times N_2 \times \cdots \times N_m&amp;lt;/math&amp;gt; означает выполнение &amp;lt;math&amp;gt;\sum_{i=1}^m \prod_{k\neq i} N_k&amp;lt;/math&amp;gt; одномерных преобразований. Ключевым наблюдением, приводящим к быстрому алгоримту для вычисления преобразования, является возможность свести (с некоторыми оговорками) одномерное преобразование к двумерному, которое для &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt; требует &amp;lt;math&amp;gt;m + n&amp;lt;/math&amp;gt; рекурсивных вызовов, что, при применении такого метода на всех уровнях рекурсии, даёт общую сложность алгоритма &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Преобразование Фурье задаётся следующей формулой:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Y_k = \sum_{l = 0}^{N-1} X_l \epsilon^{kl}_N,\qquad k = \overline{0,N-1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\epsilon_N = e^{-\frac{2i\pi}{N}}&amp;lt;/math&amp;gt; — первообразный корень&lt;br /&gt;
из 1 степени &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; (т.е. &amp;lt;math&amp;gt;\epsilon_N&amp;lt;/math&amp;gt; является корнем из 1 степени &amp;lt;math&amp;gt;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;
матрицей &amp;lt;math&amp;gt;F = \left\{ \epsilon^{kl}_N \right\}_{k,l = 0}^{N-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Многомерное преобразование Фурье ====&lt;br /&gt;
Естественным обобщением описанного одномерного преобразования Фурье является многомерный вариант:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Y(l_1,\ldots,l_d) = \sum_{k_1 = 0}^{N_1-1} \cdots \sum_{k_d = 0}^{N_d-1} X(k_1,\ldots,k_d) \prod_{j=1}^{d} \epsilon_{N_j}^{l_j k_j}&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;N = \prod_{j=1}^d N_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Сведение к (почти) двумерному преобразованию (алгоритм Кули-Тьюки) ====&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; — составное, т.е. &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt;, то возможно&lt;br /&gt;
существенно сократить вычислительные расходы за счёт сведения к&lt;br /&gt;
двумерному преобразованию Фурье.  А именно, положим &amp;lt;math&amp;gt;X(l_1, l_2)&lt;br /&gt;
= X_{l_1 n + l_2}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; Y(k_1, k_2) = Y_{k_1 m + k_2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;l_1, k_2 = \overline{0, m-1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l_2, k_1 =&lt;br /&gt;
\overline{0, n-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В этом случае&amp;lt;ref name=&amp;quot;bachv&amp;quot;&amp;gt;Н.С. Бахвалов, Н.П. Жидков, Г.М. Кобельков, &#039;&#039;Численные методы&#039;&#039;, 2003 г., ISBN: 5-94774-060-5&amp;lt;/ref&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{align}&lt;br /&gt;
    Y(k_1, k_2) &amp;amp;=&amp;amp; \sum_{l_2=0}^{n-1} (\epsilon^{k_2 l_2}_N \hat{X}(k_2, l_2)) \epsilon^{k_1 l_2}_n \\&lt;br /&gt;
    \hat{X}(k_2, l_2) &amp;amp;=&amp;amp; \sum_{l_1=0}^{m-1} X(l_1, l_2) \epsilon^{k_2 l_1}_m&lt;br /&gt;
  \end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это даёт следующий алгоритм вычисления преобразования:&lt;br /&gt;
&lt;br /&gt;
# Записываем исходный вектор в матрицу &amp;lt;math&amp;gt;m\times n&amp;lt;/math&amp;gt; по строкам.&lt;br /&gt;
# Применяем к каждому столбцу преобразование Фурье.&lt;br /&gt;
# Умножаем элемент в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;\epsilon^{ij}_N&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Применяем к каждой строке преобразование Фурье.&lt;br /&gt;
# Результат записан в получившейся матрице по столбцам.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм уже даёт существенный выигрыш по сравнению с обычным&lt;br /&gt;
умножением матрицы на вектор: &amp;lt;math&amp;gt; O(m^2 n + n^2 m) &amp;lt;/math&amp;gt; против&lt;br /&gt;
&amp;lt;math&amp;gt;O(m^2 n^2)&amp;lt;/math&amp;gt;.  Однако наилучших результатов можно добиться,&lt;br /&gt;
если применять этот алгоритм рекурсивно на этапах 2 и 4.&lt;br /&gt;
&lt;br /&gt;
Так, если &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;, то целесообразно&lt;br /&gt;
на каждом уровне рекурсии «отщеплять» одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; (уровней рекурсии при этом будет &amp;lt;math&amp;gt;K \sim \log N&amp;lt;/math&amp;gt;).  В этом&lt;br /&gt;
случае задача сводится к вычислению &amp;lt;math&amp;gt; \prod_{i\neq j} p_i &amp;lt;/math&amp;gt;&lt;br /&gt;
преобразований Фурье порядка &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt; для всех&lt;br /&gt;
&amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.  При небольших &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; (например, 2), это&lt;br /&gt;
можно проделывать «в лоб».&lt;br /&gt;
&lt;br /&gt;
==== Преобразование Фурье с большим простым порядком (алгоритм Радера) ====&lt;br /&gt;
&lt;br /&gt;
В случае, если порядок преобразования простой, но ещё слишком большой, чтобы вычислять его непосредственно, задача усложняется.  Алгоритм в этом случае такой:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_0 = \sum_{l=0}^{N-1} X_l&amp;lt;/math&amp;gt; вычисляется непосредственно.&lt;br /&gt;
# Любым способом вычисляется &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; — первообразный корень мультипликативной группы поля &amp;lt;math&amp;gt;\mathbb{Z}_{N}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_{g^{N-p} \bmod N} = X_0 + \sum_{q=0}^{N-2} X_{g^q \bmod N} \epsilon_N^{g^{q-p}\bmod N} &amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Последнее выражение представляет собой свёртку и вычисляется с помощью всё того же преобразования Фурье.  В принципе, это можно проделывать непосредственно, поскольку порядок свёртки &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;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;
&amp;lt;math&amp;gt;O(mn (\log m + \log n))&amp;lt;/math&amp;gt; операций против&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; для умножения на поправочные коэффициенты (шаг 3).&lt;br /&gt;
&lt;br /&gt;
То же верно и для алгоритма в целом в случае &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;;&lt;br /&gt;
а именно, суммарно наибольшие вычислительные затраты связаны с&lt;br /&gt;
вычислением преобразований Фурье порядка &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Фактически, макроструктура алгоритма уже описана в разделе&lt;br /&gt;
математического описания.  На каждом уровне рекурсии, алгоритм состоит&lt;br /&gt;
из&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; рекурсивных вызовов алгоритма.&lt;br /&gt;
# Умножение всех элементов рабочего вектора на поправочные коэффициенты.&lt;br /&gt;
# Ещё &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рекурсивных вызовов.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Псевдокод (для метода Кули-Тьюки):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fft(X, N)&lt;br /&gt;
begin&lt;br /&gt;
  (m, n) = factor(N)&lt;br /&gt;
&lt;br /&gt;
  if (n == 1) // Предполагается, что n &amp;lt;= m&lt;br /&gt;
    direct_fft(X, N) // Через умножение на матрицу&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  transpose(X, m, n) //Транспонирует X как матрицу m x n, хранящуюся по строкам&lt;br /&gt;
   &lt;br /&gt;
  // 2&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    fft(X(i * m : (i + 1) * m - 1), m)&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    do j = 0, m - 1&lt;br /&gt;
      X(i * m + j) *= eps(i * j, N)&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  transpose(X, n, m)&lt;br /&gt;
&lt;br /&gt;
  // 5&lt;br /&gt;
  do j = 0, m - 1&lt;br /&gt;
    fft(X(j * n : (j + 1) * n - 1), n)&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  transpose(X, m, n)&lt;br /&gt;
end&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;code&amp;gt;eps&amp;lt;/code&amp;gt; может как вычислять соответствующие коэффициенты, так и брать их из заранее заготовленной таблицы.  Последний способ требует увеличения затрат по памяти, но позволяет существенно сэкономить на вычислениях, а потому является предпочтительным.&lt;br /&gt;
&lt;br /&gt;
Кроме того, шаги 3 и 4 можно переставлять произвольным образом (с очевидными изменениями в шаге 3).&lt;br /&gt;
&lt;br /&gt;
Особо следует отметить, что если порядок элементов в выходном массиве не важен, шаг 6 можно пропустить.  &lt;br /&gt;
&lt;br /&gt;
Так, например, если преобразование Фурье используется для вычисления свёртки векторов, то конкретный порядок элементов в их образах Фурье совершенно не важен, лишь бы он был одинаков для обоих векторов.  В этом случае в прямом преобразовании можно опустить шаг 6, а в обратном — 1.&lt;br /&gt;
&lt;br /&gt;
Если необходимо учитывать случай большого простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; (что может и не требоваться, в зависимости от задачи), то в первом условном операторе следует дополнительно проверить величину &amp;lt;math&amp;gt;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;
&lt;br /&gt;
Непосредственно из описания алгоритма получаем, что если сложность&lt;br /&gt;
вычисления преобразования Фурье обозначить &amp;lt;math&amp;gt;f_N&amp;lt;/math&amp;gt;, то&lt;br /&gt;
&amp;lt;math&amp;gt;f_{mn} = n f_{m} + m f_{n} + mn&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N = \prod_{i=1}^K p_i&amp;lt;/math&amp;gt;, и на каждом шаге&lt;br /&gt;
«отщеплять» от него одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;, а преобразование Фурье для&lt;br /&gt;
&amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; вычислять «в лоб», то общее количество операций&lt;br /&gt;
составит &amp;lt;math&amp;gt;O\left(N\sum_{i=1}^K p_i\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В частности, если &amp;lt;math&amp;gt;p_i \leq P&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;K \leq \log_P N&amp;lt;/math&amp;gt;&lt;br /&gt;
и для сложности получаем &amp;lt;math&amp;gt;O(NP\log_P N)&amp;lt;/math&amp;gt;.  Полагая&lt;br /&gt;
&amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; фиксированным, получаем заявленную сложность &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:FourierRecursiveGraph.svg|center|thumb|600px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 15&amp;lt;/math&amp;gt;. Исходные данные обозначены фиолетовым, результат — красным.  Преобразования Фурье для &amp;lt;math&amp;gt;m = 5&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n = 3&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты представлено оранжевыми узлами.]]&lt;br /&gt;
&lt;br /&gt;
Каждый уровень рекурсивного вызова состоит из трёх этапов: рекурсивный вызов по столбцам, умножение на поправочные коэффициенты и рекурсивного вызова по строкам. Таким образом для высоты ЯПФ имеем&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 h(mn) = 1 + h(m) + h(n)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для «элементарных» БПФ высота ЯПФ будет та же, что и для [[Умножение плотной матрицы на вектор|умножения матрицы на вектор]].&lt;br /&gt;
&lt;br /&gt;
Таким образом, с учётом разложения &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;h(N) = O\left(\sum_{i=1}^K p_i\right) = O(P \log_P N) = O(\log N)&amp;lt;/math&amp;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;
&lt;br /&gt;
При этом, в отличие от традиционной реализации, в которой на каждом этапе один из множителей берётся&lt;br /&gt;
малым, при параллельной реализации целесообразно взять и&lt;br /&gt;
&amp;lt;math&amp;gt;m&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;
алгоритм всего с одной внутренней пересылкой.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
В общем случае на входе и на выходе имеются комплексные векторы&lt;br /&gt;
порядка &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.  Однако в частных случаях, возможны более компактные представления для параметров и результатов.&lt;br /&gt;
&lt;br /&gt;
Для описания этих случаев нам понадобится матрица&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Q = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;   \\ &lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — вещественный, то &amp;lt;math&amp;gt;Y = Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — чисто мнимый, то &amp;lt;math&amp;gt;Y = -Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — вещественный.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = -Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — чисто мнимый.&lt;br /&gt;
&lt;br /&gt;
В каждом случае, &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; требуют для своего хранения в два раза меньше памяти, чем в общем случае.&lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии дополнительной информации о входном векторе, возможно сократить объём памяти используемый для хранения входных и выходных данных.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Обозначая матрицу преобразования &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;, имеем&amp;lt;ref name=&amp;quot;voevodtyrt&amp;quot;&amp;gt;В.В. Воеводин, Е.Е. Тыртышников, &#039;&#039;Вычислительные процессы с теплицевыми матрицами&#039;&#039;, Москва, «Наука», 1987 г.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;F = F^T&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^{-1} = \tfrac{1}{N} F^*&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^* = QF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Свойство 2 представляет особый интерес, поскольку позволяет вычислять обратное преобразование Фурье по тем же алгоритмам, что и прямое, поскольку &amp;lt;math&amp;gt;F^*&amp;lt;/math&amp;gt; представляет собой матрицу того же вида, что и &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;, но с &amp;lt;math&amp;gt;\bar{\epsilon}&amp;lt;/math&amp;gt; вместо &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt;.  В сущности, свойство 3 позволяет свести задачу к исходной непосредственно, но ценой дополнительных пересылок.&lt;br /&gt;
&lt;br /&gt;
Кроме того, из второго свойства следует&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 \|F x\|_2 = \sqrt{N} \|x\|_2&lt;br /&gt;
&amp;lt;/math&amp;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;
==== Поворотные коэффициенты ====&lt;br /&gt;
&lt;br /&gt;
Как уже упоминалось, поворотные коэффициенты имеет смысл вычислить заранее и хранить в специальном массиве.  Отдельное внимание при этом стоит обратить на передачу этого массива в рекурсивные вызовы.&lt;br /&gt;
&lt;br /&gt;
В самом деле, поскольку &amp;lt;math&amp;gt;\epsilon^{l}_{n} = \epsilon^{lm}_{mn} = \epsilon^{lm}_{N}&amp;lt;/math&amp;gt;, все необходимые рекурсивному вызову значения поворотных коэффициентов уже имеются в используемом в текущем вызове массиве, а потому его достаточно подготовить один раз на весь алгоритм.&lt;br /&gt;
&lt;br /&gt;
==== Нерекурсивный случай ====&lt;br /&gt;
&lt;br /&gt;
На нижнем уровне рекурсии преобразование Фурье вычисляется путём умножения на матрицу.  Поскольку этот алгоритм, вообще говоря, более пригоден для векторизации, чем рекурсивная реализация, имеет смысл не разбивать &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; на &#039;&#039;совсем&#039;&#039; мелкие множители (&amp;lt;math&amp;gt;p_i \equiv 2&amp;lt;/math&amp;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;
==== Предварительные вычисления ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Многие&#039;&#039; используемые в алгоритме данные зависят только от порядка преобразования — массив поворотных коэффициентов, факторизация &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; в алгоритме Радера, конкретная задаваемая им перестановка и оптимальный порядок свёртки там же.  Вычисление некоторых из них сопоставимо по сложности со всем остальным алгоритмом.  Имея это ввиду, если планируется вычисление нескольких преобразований Фурье одного порядка, имеет смысл подготовить все эти данные заранее.&lt;br /&gt;
&lt;br /&gt;
==== Численная устойчивость ====&lt;br /&gt;
&lt;br /&gt;
Поскольку все преобразования в алгоритме являются, с точностью до константы &amp;lt;math&amp;gt;\leq \sqrt{N}&amp;lt;/math&amp;gt;, унитарными, катастрофического роста ошибок округления не предвидится, а потому все вычисления возможно проводить с целевой точностью.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
[[Файл:IT_FFTMemaccess.png|center|thumb|800px|Рисунок 2. Профиль обращений в память простого алгоритма Кули-Тьюки с &amp;lt;math&amp;gt;p = (2, 2, 3, 5, 2)&amp;lt;/math&amp;gt;. Сверху вниз: вспомогательная память для прямого умножения матрицы на вектор, память для хранения поворотных коэффициентов, рабочий вектор. Реализация не использует транспонирования векторов в явном виде.]]&lt;br /&gt;
&lt;br /&gt;
Как видно, алгоритм в целом обладает невысокой локальностью, что связано с его структурой (множество рекурсивных шагов, каждый из которых охватывает существенную часть рабочего вектора) и организацией доступа к поворотным коэффициентам. Некоторого улучшения локальности можно добиться, если транспонировать рабочую «матрицу» в памяти, но это либо требует дополнительной памяти, либо, если транспонировать «на месте», существенно усложняет алгоритм.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В случае составного &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; с &#039;&#039;достаточно&#039;&#039; большими делителями, наиболее очевидным и простым вариантом является распределение по процессорам рекурсивных вызовов алгоритма Кули-Тьюки. В этом случае на весь алгоритм приходится только три пересылки, но зато они проходят по схеме «все-со-всеми» (транспонирование матрицы) и не могут выполняться параллельно с вычислениями.&lt;br /&gt;
&lt;br /&gt;
Отдельно заметим, что, как уже упоминалось, первой либо последней группы пересылок можно избежать, если порядок элементов результата не важен.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; простое либо один из множителей в &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt; всегда мал, ситуация усложняется. В этом случае придётся применять алгоритм Радера на межпроцессорном уровне, что связано с более сложной схемой пересылок и большим их количеством (т.к. приходится осуществлять преобразование Фурье дважды: прямое и обратное). &lt;br /&gt;
&lt;br /&gt;
В связи с этим, некоторые существующие реализации (например, Intel® MKL) просто не поддерживают этот случай. Впрочем, некоторые реализации не поддерживают большие простые делители &#039;&#039;вовсе&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Хотя, как уже было указано, алгоритм поддаётся реализации с относительно небольшим числом пересылок, следует отметить, что вычислительна нагрузка на отдельные вычислительные ядра невелика и сопоставима с объёмом пересылок.  При этом сами пересылки достаточно объёмны (весь рабочий вектор), нелокальны (все-со-всеми), и не могут проводиться одновременно с вычислениями.  По этим причинам, ожидать от алгоритма хорошей масштабируемости не приходится, несмотря на «хорошие» свойства информационного графа.  Это и подтверждается экспериментом:&lt;br /&gt;
&lt;br /&gt;
[[Файл:TIFFTEfficiency.png|center|thumb|800px|Рисунок 3. Эффективность распараллеливания быстрого преобразования Фурье, выраженная как отношение времени работы на одном процессоре к произведению времени работы на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; процессорах на &amp;lt;math&amp;gt;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;
[[Файл:TIFFTSpeedup.png|center|thumb|800px|Рисунок 4.  Ускорение, получаемое при распараллеливании преобразования Фурье.  Обозначения те же, что и на рис. 3]]&lt;br /&gt;
&lt;br /&gt;
В общем, можно заметить, что, если возникла необходимость вычислять преобразование Фурье параллельно, добавление вычислительных узлов является целесообразным, однако такой ситуации следует по возможности избегать, поскольку ресурсы параллельной системы при этом расходуются крайне неэффективно.&lt;br /&gt;
&lt;br /&gt;
При измерениях использовалась реализация алгоритма, имеющаяся в библиотеке Intel® MKL, расчёты проводились на суперкомпьютере «Ломоносов». Код доступен [https://bitbucket.org/timokhin_ivan/fft/src здесь].&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;
&lt;br /&gt;
Алгоритм быстрого преобразования Фурье относится к базовым алгоритмам вычислительной математики, а потому его реализации существуют во многих библиотеках.  Перечислим некоторые из них:&lt;br /&gt;
&lt;br /&gt;
* [//software.intel.com/en-us/intel-mkl Intel® Math Kernel Library] — содержит в том числе и версию для распределённых систем.  Имеет интерфейсы для C, C++ и Fortran.&lt;br /&gt;
* [http://www.fftw.org FFTW] — содержит распределённую реализацию начиная с версии 3.3.  Имеет интерфейсы для C и Fortran.&lt;br /&gt;
* [//www.gnu.org/software/gsl GNU Scientific Library] — имеет интерфейс для C.&lt;br /&gt;
* [//developer.nvidia.com/cufft cuFFT] — реализация алгоритма для GPU от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft clFFT] — реализация алгоритма на OpenCL для CPU и GPU.  Поддерживает только &amp;lt;math&amp;gt;N = 2^p 3^q 5^r 7^s&amp;lt;/math&amp;gt;&lt;br /&gt;
* [http://www.ibm.com/systems/power/software/essl IBM® Engineering and Scientific Subroutine Library] — для процессоров IBM® POWER™, имеет интерфейсы для C, C++ и Fortran, существует версия для многопроцессорных систем.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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=19467</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=19467"/>
		<updated>2016-11-29T14:09:45Z</updated>

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

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к [[Участник:Ruslanlazovskiy/CHAMELEON]] ==&lt;br /&gt;
&lt;br /&gt;
1) все ссылки в статье не работают&lt;br /&gt;
&lt;br /&gt;
2) список литературы оформлен неправильно - см. как сделано в [[Разложение_Холецкого_(метод_квадратного_корня)]]&lt;br /&gt;
&lt;br /&gt;
3) для оформления (псевдо) кода следует использовать тег &amp;lt;nowiki&amp;gt;&amp;lt;source lang=&amp;quot;...&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Мои правки ==&lt;br /&gt;
&lt;br /&gt;
2) и 3) теперь сделаны как надо. В 1) не удалось поправить самую первую ссылку, ресурс с последовательной реализацией действительно умер.&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье про Хамелеон (ещё порция) ==&lt;br /&gt;
&lt;br /&gt;
* Ссылки на авторов и список литературы - присоединяюсь к замечаниям. Список литературы действительно всего из одного наименования? [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* В разделе 1 пояснить, нужно ли заранее знать число кластеров. Это неясно и из описания входных и выходных данных, должно же оно быть хоть где-нибудь [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.2. Когда вводим граф, желательно пояснить аргументы V и E в скобках. Аналогично с параметром m, который встречается впервые [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.6. Стоит сделать логарифм красивей [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Правки - порция 2. ==&lt;br /&gt;
&lt;br /&gt;
* В список литературы добавлена статья с исследованием параллелизма. Остальные найденные статьи по Chameleon представляют собой кальку с оригинальной статьи ( первый пункт в списке ) в разделе описания алгоритма + описание задачи, для которой применялся алгоритм, для примера добавил одну из таких статей.&lt;br /&gt;
* Сделано&lt;br /&gt;
* Сделано&lt;br /&gt;
* Поправил, добавил описание &amp;quot;предельного&amp;quot; случая.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Изменил статью в соответствии с замечаниями к 2.4&lt;br /&gt;
&lt;br /&gt;
== Замечания к пункту 2.4 == &lt;br /&gt;
&lt;br /&gt;
* Отсутствуют подписи к меткам на оси числа процессов&lt;br /&gt;
* Желательно сделать крупнее размер шрифта подписей&lt;br /&gt;
&lt;br /&gt;
== Ещё замечание ==&lt;br /&gt;
&lt;br /&gt;
* Все, кажется, хорошо, но плохо оформлены скобки в тексте, их надо писать без пробела (так), а не с пробелом по краям ( так не надо ). Я начал исправлять, но их слишком много, поправьте пожалуйста. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 11:43, 28 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Ruslanlazovskiy&amp;diff=19340</id>
		<title>Обсуждение участника:Ruslanlazovskiy</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:Ruslanlazovskiy&amp;diff=19340"/>
		<updated>2016-11-28T08:43:06Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к [[Участник:Ruslanlazovskiy/CHAMELEON]] ==&lt;br /&gt;
&lt;br /&gt;
1) все ссылки в статье не работают&lt;br /&gt;
&lt;br /&gt;
2) список литературы оформлен неправильно - см. как сделано в [[Разложение_Холецкого_(метод_квадратного_корня)]]&lt;br /&gt;
&lt;br /&gt;
3) для оформления (псевдо) кода следует использовать тег &amp;lt;nowiki&amp;gt;&amp;lt;source lang=&amp;quot;...&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Мои правки ==&lt;br /&gt;
&lt;br /&gt;
2) и 3) теперь сделаны как надо. В 1) не удалось поправить самую первую ссылку, ресурс с последовательной реализацией действительно умер.&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье про Хамелеон (ещё порция) ==&lt;br /&gt;
&lt;br /&gt;
* Ссылки на авторов и список литературы - присоединяюсь к замечаниям. Список литературы действительно всего из одного наименования? [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* В разделе 1 пояснить, нужно ли заранее знать число кластеров. Это неясно и из описания входных и выходных данных, должно же оно быть хоть где-нибудь [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.2. Когда вводим граф, желательно пояснить аргументы V и E в скобках. Аналогично с параметром m, который встречается впервые [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.6. Стоит сделать логарифм красивей [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Правки - порция 2. ==&lt;br /&gt;
&lt;br /&gt;
* В список литературы добавлена статья с исследованием параллелизма. Остальные найденные статьи по Chameleon представляют собой кальку с оригинальной статьи ( первый пункт в списке ) в разделе описания алгоритма + описание задачи, для которой применялся алгоритм, для примера добавил одну из таких статей.&lt;br /&gt;
* Сделано&lt;br /&gt;
* Сделано&lt;br /&gt;
* Поправил, добавил описание &amp;quot;предельного&amp;quot; случая.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Изменил статью в соответствии с замечаниями к 2.4&lt;br /&gt;
&lt;br /&gt;
== Замечания к пункту 2.4 == &lt;br /&gt;
&lt;br /&gt;
* Отсутствуют подписи к меткам на оси числа процессов&lt;br /&gt;
* Желательно сделать крупнее размер шрифта подписей&lt;br /&gt;
&lt;br /&gt;
== Ещё замечание&lt;br /&gt;
&lt;br /&gt;
* Все, кажется, хорошо, но плохо оформлены скобки в тексте, их надо писать без пробела (так), а не с пробелом по краям ( так не надо ). Я начал исправлять, но их слишком много, поправьте пожалуйста. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 11:43, 28 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Ruslanlazovskiy/CHAMELEON&amp;diff=19337</id>
		<title>Участник:Ruslanlazovskiy/CHAMELEON</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:Ruslanlazovskiy/CHAMELEON&amp;diff=19337"/>
		<updated>2016-11-28T08:37:35Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Algoman}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = CHAMELEON&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n*m + n*log{n} + m*log{m})&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; - этап 1. &amp;lt;math&amp;gt;O(q)&amp;lt;/math&amp;gt; - отдельная итерация этапа 3.&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; - этап 1. &amp;lt;math&amp;gt;O(q)&amp;lt;/math&amp;gt; - отдельная итерация этапа 3.&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2 / 2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Авторы статьи: &lt;br /&gt;
[https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ruslanlazovskiy|&amp;lt;b&amp;gt;Лазовский Р. А.&amp;lt;/b&amp;gt;] ( разделы 1.1 - 1.4 , 1.7 , 1.8 ),&lt;br /&gt;
[https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%9C%D1%83%D1%81%D1%82%D0%B0%D1%84%D0%B0%D0%B5%D0%B2_%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D0%BD_%D0%AD%D0%BB%D1%8C%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%87|&amp;lt;b&amp;gt;Мустафаев Э. Э.&amp;lt;/b&amp;gt;] ( разделы 1.5 , 1.6 , 1.9 , 1.10 , 2.2 )&lt;br /&gt;
&lt;br /&gt;
==Свойства и структура алгоритма==&lt;br /&gt;
===Общее описание алгоритма===&lt;br /&gt;
CHAMELEON – алгоритм динамической иерархической кластеризации графа, то есть процесса разбиения графа на несколько подграфов-кластеров таким образом, что данные внутри одного кластера имеют максимально схожи, а данные в разных кластерах — максимально различны. В классических алгоритмах кластеризации схожесть объектов определяется с помощью некоторой метрики (простейший пример — евклидова метрика). Не следует путать задачу кластеризации с задачей классификации: в последней характеристики объекта, необходимые для попадания в тот или иной класс, известны заранее, в рассматриваемой же задаче они становятся известны в процессе решения. Первоначально алгоритм был описан в статье&amp;lt;ref&amp;gt;CHAMELEON: A Hierarchical Clustering Algorithm Using Dynamic Modeling. George Karypis, Eui-Hong Han, Vipin Kumar. IEEE Computer 32(8): 68-75, 1999&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм CHAMELEON работает в 3 этапа. На первом этапе исходное множество объектов организуется в граф по принципу k ближайших соседей. На втором этапе этого граф разбивается на достаточно малые подграфы-кластеры. На третьем этапе происходит агломеративная иерархическая кластеризация полученных подграфов в соответствии с одной из выбранных метрик (возможные метрики будут подробно описаны далее). Число получаемых в итоге кластеров в явном виде не задаётся.&lt;br /&gt;
&lt;br /&gt;
===Математическое описание алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Исходные данные:&amp;lt;/b&amp;gt; Первоначально исходными данными алгоритма являются &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов длины &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, каждый из которых соответствует одному объекту. На основании значений элементов этих векторов строится матрица смежности размера &amp;lt;math&amp;gt;n * n&amp;lt;/math&amp;gt; ( возможно сокращение требуемого объема памяти до &amp;lt;math&amp;gt;(n*(n - 1))/ 2&amp;lt;/math&amp;gt; в соответствии с выбранной метрикой. К исходным данным также относится набор параметров. Это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество ближайших соседей на первом этапе алгоритма, &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - отношение количества вершин в наибольшем подграфе по отношению к общему числу объектов на втором этапе алгоритма, а также параметры &amp;lt;math&amp;gt;T_{RI}, T_{RI}&amp;lt;/math&amp;gt; - пороговые значения, после которых прекращается агломеративная кластеризация на третьем этапе. Следует особо отметить, что число получаемых в итоге кластеров заранее не известно, хотя на него и можно повлиять изменением значений перечисленных параметров.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Первый этап алгоритма:&amp;lt;/b&amp;gt; На первом этапе на основании матрицы смежности строится граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа ( т.е. объекты ), а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество ребер графа ( т.е. построенные связи между ближайшими соседями ). Указанный граф не обязательно является связным.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Второй этап алгоритма:&amp;lt;/b&amp;gt; Граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; итеративно разбивается на множество подграфов &amp;lt;math&amp;gt;G_{i} = (V_{i}, E_{i}), i = 1 .. m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \cup V_{i} = V, i = 1 .. m&amp;lt;/math&amp;gt;. Параметр &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; явно не задаётся, выполнение этого этапа алгоритма прекращается по выполнении описанных далее условий. На каждой итерации выбирается &amp;lt;math&amp;gt;G_{b}&amp;lt;/math&amp;gt; с числом вершин, наибольшим среди всех подграфов, имеющихся на данной итерации. Этот граф разбивается на 2 подграфа &amp;lt;math&amp;gt;G_{bi}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;G_{bj}&amp;lt;/math&amp;gt; таких, что 1) &amp;lt;math&amp;gt;min(V_{bi},V_{bj}) &amp;gt;= 0.25 * V_{b}&amp;lt;/math&amp;gt; ( имеется ввиду количество вершин ) и 2) &amp;lt;math&amp;gt;\sum{E_{kl}}, E_{kl} = (v_{k}, v_{l}), v_{k} \in V_{bi} , v_{l} \in V_{bj} &amp;lt;/math&amp;gt; минимальна среди всех разбиений, удовлетворяющих 1). Итеративный процесс прекращается, когда &amp;lt;math&amp;gt;\sum{v_{b}} &amp;lt;= q * \sum{v} , v_{b} \in V_{b} , e \in E&amp;lt;/math&amp;gt; ( имеется ввиду количество вершин ). Здесь &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - задаваемый параметр, на практике он варьируется от &amp;lt;math&amp;gt;0.01&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.05&amp;lt;/math&amp;gt;. Полученные подграфы называются малыми кластерами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Третий этап алгоритма:&amp;lt;/b&amp;gt; Множество кластеров &amp;lt;math&amp;gt;G_{i} = (V_{i}, E_{i}), i = 1 .. m&amp;lt;/math&amp;gt; итеративно преобразуется в множество кластеров &amp;lt;math&amp;gt;G_{j} = (V_{j}, E_{j}), j = 1 .. l&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l &amp;lt;= m&amp;lt;/math&amp;gt;. Для этого вводятся следующие понятия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; - абсолютная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Это суть сумма весов ребер, соединяющих вершины, принадлежащие &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; c вершинами из &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt;. &amp;lt;math&amp;gt;EC_{(C_{i},C_{i})}&amp;lt;/math&amp;gt; вычисляется как сумма ребер, входящих в разделитель, разбивающий &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; на два равных подграфа.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt; - абсолютное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Это суть среднее сходство между соединенными вершинами, принадлежащими &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt; соответственно. Соединения обусловлены разбиением общего графа, полученного на первом этапе алгоритма.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} = \frac{2*|EC_{(C_{i},C_{j})}|}{|EC_{C_{i}}|+|EC_{C_{j}}|}&amp;lt;/math&amp;gt; - относительная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}= \frac{S_{EC_{(C_{i},C_{j})}}}{\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{i})}}+\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{j})}}}&amp;lt;/math&amp;gt; - относительное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На основании этих показателей осуществляется агломеративная иерерхическая кластеризация. Существует две стратегии анализа показателей сходства. В первом случае для каждой пары кластеров &amp;lt;math&amp;gt;C{i}, C{j}&amp;lt;/math&amp;gt; проверяется истинность выражений &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} &amp;gt;= T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;RC_{(C_{i},C_{j})} &amp;gt;= T_{RC}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{RI}, T_{RI}&amp;lt;/math&amp;gt; - заранее заданные пороговые значения. Если для некоторого &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; таких &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt; несколько, то конкретная пара определяется по максимуму значения &amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;. Во втором случае выбираются пары кластеров, максимизирующие функцию вида &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}*RC_{(C_{i},C_{j})}^\alpha&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; - заданный пользователем параметр. В обоих случаях процесс останавливается либо когда на очередной итерации не находится подходящих пар кластеров, либо когда остаётся только один кластер.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Вычисляемые данные:&amp;lt;/b&amp;gt; В процессе агломеративной кластеризации для всех объектов, т.е. всех вершин всех кластеров указывается индекс &amp;lt;math&amp;gt;j&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;
В алгоритме 3 основных вычислительных ядра, по одному на каждый этап алгоритма.&lt;br /&gt;
&lt;br /&gt;
На первом этапе алгоритма основная вычислительная нагрузка приходится на анализ матрицы смежности, необходимого для построения первоначального графа &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt;, так как для каждого объекта необходимо перебрать всех его соседей и выделить &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших по заданной метрике.&lt;br /&gt;
&lt;br /&gt;
На втором этапе наибольшее время занимает поиск подходящего разбиения очередного подграфа. В элементарном варианте этой процедуры необходимо перебрать все комбинации вершин в этом подграфе, удовлетворяющие условию 1) из математического описания алгоритма, и для каждой из них подсчитать значение, используемое в 2). &lt;br /&gt;
&lt;br /&gt;
На третьем этапе алгоритма вычислительное ядро полностью совпадает с самим этапом, то есть итерационным процессов агломеративной иерархической кластеризации, включающем в себя подсчёт выбранной метрики для каждой пары кластеров, имеющихся на очередной итерации.&lt;br /&gt;
&lt;br /&gt;
===Макроструктура алгоритма===&lt;br /&gt;
Как уже понятно из предыдущих разделов описания, алгоритм CHAMELEON выполняется в 3 этапа, последовательных относительно друг друга. Каждый этап, строго говоря, соответствует отдельному алгоритму. В рамках этого описания макрооперациями будут считаться процедуры разбиения наибольшего подграфа на каждой итерации второго этапа алгоритма, а также процедура вычисления метрики для принятия решения об объединении кластеров на каждой итерации третьего этапа.&lt;br /&gt;
&lt;br /&gt;
===Схема реализации последовательного алгоритма===&lt;br /&gt;
&lt;br /&gt;
Схема представлена в виде C++-подобного псевдокода, макрооперации в алгоритме выделены в соответствии с предыдущим разделом описания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // First stage.&lt;br /&gt;
 int k // Number of neighbors.&lt;br /&gt;
 float v[n][p] // Initial vectors.&lt;br /&gt;
 float M[n][n] // Adjacency matrix.&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; ++i) {&lt;br /&gt;
  for (int l = 0; l &amp;lt; n; ++l) {&lt;br /&gt;
   M[i][l] = Compute_metric(v[i],v[l]);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
 Graph max_graph // Structure/class that represents a graph.&lt;br /&gt;
 Neighbor_list list // Here neighbors are initially stored.&lt;br /&gt;
 max_graph.add_vertices // Vertices ~ initial vectors.&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; ++i) {&lt;br /&gt;
  for (int l = i + 1; i &amp;lt; n; ++i) { // &amp;quot;Smart&amp;quot; version - we do not repeat same vertex combinations.&lt;br /&gt;
   if (neighbor_number &amp;lt; k) {&lt;br /&gt;
     add_neighbor(v[i],v[l]) // Just add.&lt;br /&gt;
   }else{&lt;br /&gt;
     substitute_neighbor(v[i],v[l]) // Check whether a new element is closer than some of the oldies.&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  max_graph.add_neighbors // Looks at neighbor lists of current vector and adds them&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // Second stage. &lt;br /&gt;
 float percent // Determines when to stop splitting graphs. &lt;br /&gt;
 Graph graphs[n] // GRAPHS WILL MOST LIKELY TAKE LESS SPACE, IT&#039;S JUST THE UPPER LIMIT. &lt;br /&gt;
 max_graph = find_max_graph() // Biggest graph by number of vertices available. &lt;br /&gt;
 while (graph_size(max_graph) &amp;gt; n * percent) {&lt;br /&gt;
   Split_graph(max_graph); // Split the graph - a macro operation that represents the second phase.&lt;br /&gt;
 } &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // Third stage. &lt;br /&gt;
 Graph graphs[m] // That&#039;s how many graphs we got. m &amp;lt;= n. &lt;br /&gt;
 bool pairs_exist // Check if the&#039;re pairs of clusters to unite.&lt;br /&gt;
 while (num_graphs &amp;gt; 1 &amp;amp;&amp;amp; pairs_exist){&lt;br /&gt;
  pairs_exist = false // So we don&#039;t repeat if it fails.&lt;br /&gt;
  for (int i = 0; i &amp;lt; m; ++i) {&lt;br /&gt;
   for (int l = i + 1; l &amp;lt; m; ++l) { // Also a &amp;quot;Smart&amp;quot; version.&lt;br /&gt;
     check_pair(pairs_exist, graphs[i], graphs[l]); // Check whether graphs are similar enough by some metrics.&lt;br /&gt;
     if (pairs_exist) { Unite_graphs(graphs[i], graphs[l]) }; // Pairs_exist is set to true if something&#039;s found.&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Последовательная сложность алгоритма===&lt;br /&gt;
&lt;br /&gt;
Общая временная сложность последовательного варианта алгоритма CHAMELEON определяется как &amp;lt;math&amp;gt;O(nm + nlog{n} + mlog{m})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число исходных объектов, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число малых кластеров, полученных после первого этапа работы. В случае &amp;quot;предельного&amp;quot; разбиения &amp;lt;math&amp;gt;m = n&amp;lt;/math&amp;gt; эта оценка превращается в &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;. В любом случае эта оценка не учитывает необходимости построения матрицы смежности по исходному набору векторов (авторы оригинальной статьи в своих исследованиях подразумевают, что матрица смежности уже построена), что означает дополнительные &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; операций. Тогда оценка будет составлять &amp;lt;math&amp;gt;O(n^2 + nm + nlog{n} + mlog{m})&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;
Следует также обратить внимание, что это оценка по числу не сложений и умножений, а достаточно крупных операций. Например, вычисление метрики между двумя векторами рассматривается как одна операция, а она включает в себя &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;2n&amp;lt;/math&amp;gt; сложений ( для вычисления квадратичной нормы разности векторов).&lt;br /&gt;
&lt;br /&gt;
===Информационный граф===&lt;br /&gt;
&lt;br /&gt;
Информационный граф дан в соответствии с макроструктурой алгоритма. Следует отметить, что имеются успешные попытки распараллелить 2 и 3 этапы алгоритма CHAMELEON ( см. раздел &amp;quot;Существующие реализации&amp;quot; ). Однако они включают в себя модификацию исходного алгоритма. Также возможно распараллеливание выборки пар кластеров и подсчёта их метрик внутри каждой из вершин stage_3_iter. Структура информационного графа внутри этих итераций аналогична структуре информационного графа первого этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
[[File:CHAMELEON ruslanlazovskiy.png]]&lt;br /&gt;
&lt;br /&gt;
===Ресурс параллелизма алгоритма===&lt;br /&gt;
&lt;br /&gt;
Параллельную сложность алгоритма CHAMELEON следует оценивать отдельно для каждого этапа.&lt;br /&gt;
На первом этапе алгоритм поиска &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей может быть выполнен за &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; вычислений значения метрики между двумя векторами.&lt;br /&gt;
На втором этапе итерации алгоритма выполняются строго последовательно, а распараллеливание алгоритма внутри итерации не относится к теме статьи.&lt;br /&gt;
На третьем этапе итерации также выполняются последовательно, однако возможно распараллеливание обработки пар кластеров внутри итерации. Каждая итерация может быть выполнена за &amp;lt;math&amp;gt;q - 1&amp;lt;/math&amp;gt; вычислений значения метрики между двумя кластерами, где q - количество кластеров на этой итерации.&lt;br /&gt;
&lt;br /&gt;
===Входные и выходные данные алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Входные данные:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt; - набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов длины &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число, определяющиее количество ближайших соседей, по которым строится первоначальный граф. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Выходные данные:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; - вектор длины &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, значение &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го элемента указывает на принадлежность соответствующего вектора одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
===Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Соотношение последовательной и параллельной сложности:&amp;lt;/b&amp;gt; Различные этапы алгоритма строго последовательны относительно друг друга. На первом этапе отношение последовательной сложности алгоритма к параллельной есть &amp;lt;math&amp;gt;n/2&amp;lt;/math&amp;gt;. На втором и третьем этапах различные итерации последовательны относительно друг друга. Однако внутри итераций допускается некоторый параллелизм. Например, для итераций третьего этапа в наилучшем случае отношение сложности последовательной реализации к параллельной есть &amp;lt;math&amp;gt;q/2&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - число кластеров на конкретной итерации.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Вычислительная мощность:&amp;lt;/b&amp;gt; графовые алгоритмы, вообще говоря, обладают низкой вычислительной мощностью. В случае CHAMELEON вычислительная мощность первого этапа равна &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;. Для второго и третьего этапов оценка вычислительной мощности по сложениям и умножениям затруднительна, однако в терминах макроопераций можно дать грубую оценку. Вычислительная мощность каждой итерации второго этапа - &amp;lt;math&amp;gt;1 / \sum C_n^k&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число вершин в выбранном подграфе, а &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; варьируется от &amp;lt;math&amp;gt;0.25 * n&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.5 * n&amp;lt;/math&amp;gt;. Вычислительная мощность каждой итерации третьего этапа в этих терминах - &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Детерминированность:&amp;lt;/b&amp;gt; Важной особенностью описываемого алгоритма является его недетерминированность. Условия завершения как 2-го, так и 3-го этапа позволяют обозначить CHAMELEON как итерационный алгоритм с выходом по точности. При этом детерминированность 2-го этапа алгоритма можно обеспечить указанием вырожденных условий кластеризации ( такая кластеризация, очевидно, не будет иметь практического смысла ), в то время как 2-ый этап является недетерминированным всегда.&lt;br /&gt;
&lt;br /&gt;
==Программная реализация алгоритма==&lt;br /&gt;
===Особенности реализации последовательного алгоритма===&lt;br /&gt;
===Локальность данных и вычислений===&lt;br /&gt;
===Возможные способы и особенности параллельной реализации алгоритма===&lt;br /&gt;
===Масштабируемость алгоритма и его реализации===&lt;br /&gt;
&lt;br /&gt;
Как можно увидеть из раздела &amp;quot;существующие реализации&amp;quot;, параллельной реализации алгоритма CHAMELEON на система с распределенной памятью найдено не было. Однако в &amp;lt;ref name = &amp;quot;exp&amp;quot;&amp;gt;Parallel Algorithm for the Chameleon Clustering Algorithm using Dynamic Modeling.  Rajnish Dashora, Harsh Bajaj, Akshat Dube, Geetha Mary .A. VIT University,Vellore. International Journal of Computer Applications (0975 – 8887) Volume 79 – No8, October 2013 &amp;lt;/ref&amp;gt;. приводится экспериментальный вариант параллельной реализации посредством OpenMP для систем с общей памятью, включающий в себя значительную модификацию исходного алгоритма, но не приводится рабочая реализация.&lt;br /&gt;
&lt;br /&gt;
В силу вышескасновного основное внимание будет уделено исследованию масштабируемости алгоритма по размеру задачи, в пределах одного узла. Далее приводятся графики производительности в Гфлопс для различных размеров задачи. Эффективность работы алгоритма убывает на всей рассматриваемой области. Темпы убывания эффективности снижаются с ростом размера задачи.&lt;br /&gt;
&lt;br /&gt;
Для исследования такой масштабируемости вширь использовалась референсная последовательная реализация алгоритма ( м. раздел &amp;quot;Существующие реализации&amp;quot; ). Соответствующая программа запускалась на системе BG/P на одном вычислительном узле. Характеристики вычислительного узла, а также микропроцессорного ядра - элемента такого узла, приводятся в &amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt; Таким образом, удостоверенными экспериментальными данными являются только значения, полученные для случая 1 процесса. Значения для случаев 2 и 4 процессов являются ОЦЕНОЧНЫМИ и выводятся из результатов работы модифицированной версии алгоритма, исследуемой в &amp;lt;ref name = &amp;quot;exp&amp;quot;&amp;gt;Parallel Algorithm for the Chameleon Clustering Algorithm using Dynamic Modeling.  Rajnish Dashora, Harsh Bajaj, Akshat Dube, Geetha Mary .A. VIT University,Vellore. International Journal of Computer Applications (0975 – 8887) Volume 79 – No8, October 2013 &amp;lt;/ref&amp;gt;. К сожалению, указанной реализации авторы не предоставляют. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:CHAMELEON performance.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===Динамические характеристики и эффективность реализации алгоритма===&lt;br /&gt;
===Выводы для классов архитектур===&lt;br /&gt;
===Существующие реализации алгоритма===&lt;br /&gt;
С копией референсной реализации иерархической кластеризации, взятой с данного ресурса [http://us.codeforge.com/article/192925 Sequental], в случае неработоспособности последнего, можно ознакомиться здесь [http://pastebin.com/PhC4X0Cy Copy]&amp;lt;br/&amp;gt;&lt;br /&gt;
Последовательная реализация алгоритма CHAMELEON использовалась в статье &amp;lt;ref&amp;gt;Clustering Of Web Usage Data Using&lt;br /&gt;
Chameleon Algorithm, T.Vijaya Kumar, Dr. H.S.Guruprasad, International Journal of Innovative Research in Computer&lt;br /&gt;
and Communication Engineering, Vol. 2, Issue 6, June 2014&amp;lt;/ref&amp;gt;&lt;br /&gt;
Теоретически возможна реализация алгоритма CHAMELEON с использованием графовых библиотек, например [http://glaros.dtc.umn.edu/gkhome/metis/metis/overview METIS ], [http://glaros.dtc.umn.edu/gkhome/metis/hmetis/overview hMETIS] и [https://cran.r-project.org/web/packages/RANN/index.html RANN].&amp;lt;br/&amp;gt;&lt;br /&gt;
Параллельных реализаций алгоритма CHAMELEON в графовых библиотеках мною найдено не было. Однако существует исследование, связанное с реализацией CHAMELEON посредством технологии OpenMP, с которым можно ознакомиться здесь [http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf параллельная реализация].&lt;br /&gt;
&lt;br /&gt;
==Литература==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Краткий обзор алгоритма [http://studopedia.ru/7_41934_algoritm-dinamicheskoy-ierarhicheskoy-klasterizatsii-CHAMELEON.html|]&amp;lt;br/&amp;gt;&lt;br /&gt;
Первоначальная статья от авторов алгоритма [http://www-users.cs.umn.edu/~han/dmclass/chameleon.pdf|]&amp;lt;br/&amp;gt;&lt;br /&gt;
Исследования возможной параллелизации алгоритма[http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf|]&amp;lt;br/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Ruslanlazovskiy/CHAMELEON&amp;diff=19336</id>
		<title>Участник:Ruslanlazovskiy/CHAMELEON</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:Ruslanlazovskiy/CHAMELEON&amp;diff=19336"/>
		<updated>2016-11-28T08:36:34Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Algoman}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = CHAMELEON&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n*m + n*log{n} + m*log{m})&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; - этап 1. &amp;lt;math&amp;gt;O(q)&amp;lt;/math&amp;gt; - отдельная итерация этапа 3.&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; - этап 1. &amp;lt;math&amp;gt;O(q)&amp;lt;/math&amp;gt; - отдельная итерация этапа 3.&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2 / 2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Авторы статьи: &lt;br /&gt;
[https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ruslanlazovskiy|&amp;lt;b&amp;gt;Лазовский Р. А.&amp;lt;/b&amp;gt;] ( разделы 1.1 - 1.4 , 1.7 , 1.8 ),&lt;br /&gt;
[https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%9C%D1%83%D1%81%D1%82%D0%B0%D1%84%D0%B0%D0%B5%D0%B2_%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D0%BD_%D0%AD%D0%BB%D1%8C%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%87|&amp;lt;b&amp;gt;Мустафаев Э. Э.&amp;lt;/b&amp;gt;] ( разделы 1.5 , 1.6 , 1.9 , 1.10 , 2.2 )&lt;br /&gt;
&lt;br /&gt;
==Свойства и структура алгоритма==&lt;br /&gt;
===Общее описание алгоритма===&lt;br /&gt;
CHAMELEON – алгоритм динамической иерархической кластеризации графа, то есть процесса разбиения графа на несколько подграфов-кластеров таким образом, что данные внутри одного кластера имеют максимально схожи, а данные в разных кластерах — максимально различны. В классических алгоритмах кластеризации схожесть объектов определяется с помощью некоторой метрики (простейший пример - евклидова метрика). Не следует путать задачу кластеризации с задачей классификации - в последней характеристики объекта, необходимые для попадания в тот или иной класс, известны заранее, в рассматриваемой же задаче они становятся известны в процессе решения. Первоначально алгоритм был описан в статье&amp;lt;ref&amp;gt;CHAMELEON: A Hierarchical Clustering Algorithm Using Dynamic Modeling. George Karypis, Eui-Hong Han, Vipin Kumar. IEEE Computer 32(8): 68-75, 1999&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм CHAMELEON работает в 3 этапа. На первом этапе исходное множество объектов организуется в граф по принципу k ближайших соседей. На втором этапе этого граф разбивается на достаточно малые подграфы-кластеры. На третьем этапе происходит агломеративная иерархическая кластеризация полученных подграфов в соответствии с одной из выбранных метрик ( возможные метрики будут подробно описаны далее ). Число получаемых в итоге кластеров в явном виде не задаётся.&lt;br /&gt;
&lt;br /&gt;
===Математическое описание алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Исходные данные:&amp;lt;/b&amp;gt; Первоначально исходными данными алгоритма являются &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов длины &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, каждый из которых соответствует одному объекту. На основании значений элементов этих векторов строится матрица смежности размера &amp;lt;math&amp;gt;n * n&amp;lt;/math&amp;gt; ( возможно сокращение требуемого объема памяти до &amp;lt;math&amp;gt;(n*(n - 1))/ 2&amp;lt;/math&amp;gt; в соответствии с выбранной метрикой. К исходным данным также относится набор параметров. Это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество ближайших соседей на первом этапе алгоритма, &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - отношение количества вершин в наибольшем подграфе по отношению к общему числу объектов на втором этапе алгоритма, а также параметры &amp;lt;math&amp;gt;T_{RI}, T_{RI}&amp;lt;/math&amp;gt; - пороговые значения, после которых прекращается агломеративная кластеризация на третьем этапе. Следует особо отметить, что число получаемых в итоге кластеров заранее не известно, хотя на него и можно повлиять изменением значений перечисленных параметров.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Первый этап алгоритма:&amp;lt;/b&amp;gt; На первом этапе на основании матрицы смежности строится граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа ( т.е. объекты ), а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество ребер графа ( т.е. построенные связи между ближайшими соседями ). Указанный граф не обязательно является связным.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Второй этап алгоритма:&amp;lt;/b&amp;gt; Граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; итеративно разбивается на множество подграфов &amp;lt;math&amp;gt;G_{i} = (V_{i}, E_{i}), i = 1 .. m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \cup V_{i} = V, i = 1 .. m&amp;lt;/math&amp;gt;. Параметр &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; явно не задаётся, выполнение этого этапа алгоритма прекращается по выполнении описанных далее условий. На каждой итерации выбирается &amp;lt;math&amp;gt;G_{b}&amp;lt;/math&amp;gt; с числом вершин, наибольшим среди всех подграфов, имеющихся на данной итерации. Этот граф разбивается на 2 подграфа &amp;lt;math&amp;gt;G_{bi}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;G_{bj}&amp;lt;/math&amp;gt; таких, что 1) &amp;lt;math&amp;gt;min(V_{bi},V_{bj}) &amp;gt;= 0.25 * V_{b}&amp;lt;/math&amp;gt; ( имеется ввиду количество вершин ) и 2) &amp;lt;math&amp;gt;\sum{E_{kl}}, E_{kl} = (v_{k}, v_{l}), v_{k} \in V_{bi} , v_{l} \in V_{bj} &amp;lt;/math&amp;gt; минимальна среди всех разбиений, удовлетворяющих 1). Итеративный процесс прекращается, когда &amp;lt;math&amp;gt;\sum{v_{b}} &amp;lt;= q * \sum{v} , v_{b} \in V_{b} , e \in E&amp;lt;/math&amp;gt; ( имеется ввиду количество вершин ). Здесь &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - задаваемый параметр, на практике он варьируется от &amp;lt;math&amp;gt;0.01&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.05&amp;lt;/math&amp;gt;. Полученные подграфы называются малыми кластерами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Третий этап алгоритма:&amp;lt;/b&amp;gt; Множество кластеров &amp;lt;math&amp;gt;G_{i} = (V_{i}, E_{i}), i = 1 .. m&amp;lt;/math&amp;gt; итеративно преобразуется в множество кластеров &amp;lt;math&amp;gt;G_{j} = (V_{j}, E_{j}), j = 1 .. l&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l &amp;lt;= m&amp;lt;/math&amp;gt;. Для этого вводятся следующие понятия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt; - абсолютная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Это суть сумма весов ребер, соединяющих вершины, принадлежащие &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; c вершинами из &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt;. &amp;lt;math&amp;gt;EC_{(C_{i},C_{i})}&amp;lt;/math&amp;gt; вычисляется как сумма ребер, входящих в разделитель, разбивающий &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; на два равных подграфа.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;S_{EC_{(C_{i},C_{j})}}&amp;lt;/math&amp;gt; - абсолютное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;. Это суть среднее сходство между соединенными вершинами, принадлежащими &amp;lt;math&amp;gt;C{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C{j}&amp;lt;/math&amp;gt; соответственно. Соединения обусловлены разбиением общего графа, полученного на первом этапе алгоритма.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} = \frac{2*|EC_{(C_{i},C_{j})}|}{|EC_{C_{i}}|+|EC_{C_{j}}|}&amp;lt;/math&amp;gt; - относительная взаимная связность пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt; RC_{(C_{i},C_{j})}= \frac{S_{EC_{(C_{i},C_{j})}}}{\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{i})}}+\frac{|C_{i}|}{|C_{i}+C_{j}|}*S_{EC_{(C_{j})}}}&amp;lt;/math&amp;gt; - относительное взаимное сходство пары кластеров &amp;lt;math&amp;gt;C_{i}, C{j}&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На основании этих показателей осуществляется агломеративная иерерхическая кластеризация. Существует две стратегии анализа показателей сходства. В первом случае для каждой пары кластеров &amp;lt;math&amp;gt;C{i}, C{j}&amp;lt;/math&amp;gt; проверяется истинность выражений &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})} &amp;gt;= T_{RI}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;RC_{(C_{i},C_{j})} &amp;gt;= T_{RC}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{RI}, T_{RI}&amp;lt;/math&amp;gt; - заранее заданные пороговые значения. Если для некоторого &amp;lt;math&amp;gt;C_{i}&amp;lt;/math&amp;gt; таких &amp;lt;math&amp;gt;C_{j}&amp;lt;/math&amp;gt; несколько, то конкретная пара определяется по максимуму значения &amp;lt;math&amp;gt;EC_{(C_{i},C_{j})}&amp;lt;/math&amp;gt;. Во втором случае выбираются пары кластеров, максимизирующие функцию вида &amp;lt;math&amp;gt;RI_{(C_{i},C_{j})}*RC_{(C_{i},C_{j})}^\alpha&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; - заданный пользователем параметр. В обоих случаях процесс останавливается либо когда на очередной итерации не находится подходящих пар кластеров, либо когда остаётся только один кластер.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Вычисляемые данные:&amp;lt;/b&amp;gt; В процессе агломеративной кластеризации для всех объектов, т.е. всех вершин всех кластеров указывается индекс &amp;lt;math&amp;gt;j&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;
В алгоритме 3 основных вычислительных ядра, по одному на каждый этап алгоритма.&lt;br /&gt;
&lt;br /&gt;
На первом этапе алгоритма основная вычислительная нагрузка приходится на анализ матрицы смежности, необходимого для построения первоначального графа &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt;, так как для каждого объекта необходимо перебрать всех его соседей и выделить &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших по заданной метрике.&lt;br /&gt;
&lt;br /&gt;
На втором этапе наибольшее время занимает поиск подходящего разбиения очередного подграфа. В элементарном варианте этой процедуры необходимо перебрать все комбинации вершин в этом подграфе, удовлетворяющие условию 1) из математического описания алгоритма, и для каждой из них подсчитать значение, используемое в 2). &lt;br /&gt;
&lt;br /&gt;
На третьем этапе алгоритма вычислительное ядро полностью совпадает с самим этапом, то есть итерационным процессов агломеративной иерархической кластеризации, включающем в себя подсчёт выбранной метрики для каждой пары кластеров, имеющихся на очередной итерации.&lt;br /&gt;
&lt;br /&gt;
===Макроструктура алгоритма===&lt;br /&gt;
Как уже понятно из предыдущих разделов описания, алгоритм CHAMELEON выполняется в 3 этапа, последовательных относительно друг друга. Каждый этап, строго говоря, соответствует отдельному алгоритму. В рамках этого описания макрооперациями будут считаться процедуры разбиения наибольшего подграфа на каждой итерации второго этапа алгоритма, а также процедура вычисления метрики для принятия решения об объединении кластеров на каждой итерации третьего этапа.&lt;br /&gt;
&lt;br /&gt;
===Схема реализации последовательного алгоритма===&lt;br /&gt;
&lt;br /&gt;
Схема представлена в виде C++-подобного псевдокода, макрооперации в алгоритме выделены в соответствии с предыдущим разделом описания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // First stage.&lt;br /&gt;
 int k // Number of neighbors.&lt;br /&gt;
 float v[n][p] // Initial vectors.&lt;br /&gt;
 float M[n][n] // Adjacency matrix.&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; ++i) {&lt;br /&gt;
  for (int l = 0; l &amp;lt; n; ++l) {&lt;br /&gt;
   M[i][l] = Compute_metric(v[i],v[l]);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
 Graph max_graph // Structure/class that represents a graph.&lt;br /&gt;
 Neighbor_list list // Here neighbors are initially stored.&lt;br /&gt;
 max_graph.add_vertices // Vertices ~ initial vectors.&lt;br /&gt;
 for (int i = 0; i &amp;lt; n; ++i) {&lt;br /&gt;
  for (int l = i + 1; i &amp;lt; n; ++i) { // &amp;quot;Smart&amp;quot; version - we do not repeat same vertex combinations.&lt;br /&gt;
   if (neighbor_number &amp;lt; k) {&lt;br /&gt;
     add_neighbor(v[i],v[l]) // Just add.&lt;br /&gt;
   }else{&lt;br /&gt;
     substitute_neighbor(v[i],v[l]) // Check whether a new element is closer than some of the oldies.&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  max_graph.add_neighbors // Looks at neighbor lists of current vector and adds them&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // Second stage. &lt;br /&gt;
 float percent // Determines when to stop splitting graphs. &lt;br /&gt;
 Graph graphs[n] // GRAPHS WILL MOST LIKELY TAKE LESS SPACE, IT&#039;S JUST THE UPPER LIMIT. &lt;br /&gt;
 max_graph = find_max_graph() // Biggest graph by number of vertices available. &lt;br /&gt;
 while (graph_size(max_graph) &amp;gt; n * percent) {&lt;br /&gt;
   Split_graph(max_graph); // Split the graph - a macro operation that represents the second phase.&lt;br /&gt;
 } &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
 // Third stage. &lt;br /&gt;
 Graph graphs[m] // That&#039;s how many graphs we got. m &amp;lt;= n. &lt;br /&gt;
 bool pairs_exist // Check if the&#039;re pairs of clusters to unite.&lt;br /&gt;
 while (num_graphs &amp;gt; 1 &amp;amp;&amp;amp; pairs_exist){&lt;br /&gt;
  pairs_exist = false // So we don&#039;t repeat if it fails.&lt;br /&gt;
  for (int i = 0; i &amp;lt; m; ++i) {&lt;br /&gt;
   for (int l = i + 1; l &amp;lt; m; ++l) { // Also a &amp;quot;Smart&amp;quot; version.&lt;br /&gt;
     check_pair(pairs_exist, graphs[i], graphs[l]); // Check whether graphs are similar enough by some metrics.&lt;br /&gt;
     if (pairs_exist) { Unite_graphs(graphs[i], graphs[l]) }; // Pairs_exist is set to true if something&#039;s found.&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Последовательная сложность алгоритма===&lt;br /&gt;
&lt;br /&gt;
Общая временная сложность последовательного варианта алгоритма CHAMELEON определяется как &amp;lt;math&amp;gt;O(nm + nlog{n} + mlog{m})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число исходных объектов, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число малых кластеров, полученных после первого этапа работы. В случае &amp;quot;предельного&amp;quot; разбиения &amp;lt;math&amp;gt;m = n&amp;lt;/math&amp;gt; эта оценка превращается в &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;. В любом случае эта оценка не учитывает необходимости построения матрицы смежности по исходному набору векторов (авторы оригинальной статьи в своих исследованиях подразумевают, что матрица смежности уже построена), что означает дополнительные &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; операций. Тогда оценка будет составлять &amp;lt;math&amp;gt;O(n^2 + nm + nlog{n} + mlog{m})&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;
Следует также обратить внимание, что это оценка по числу не сложений и умножений, а достаточно крупных операций. Например, вычисление метрики между двумя векторами рассматривается как одна операция, а она включает в себя &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;2n&amp;lt;/math&amp;gt; сложений ( для вычисления квадратичной нормы разности векторов).&lt;br /&gt;
&lt;br /&gt;
===Информационный граф===&lt;br /&gt;
&lt;br /&gt;
Информационный граф дан в соответствии с макроструктурой алгоритма. Следует отметить, что имеются успешные попытки распараллелить 2 и 3 этапы алгоритма CHAMELEON ( см. раздел &amp;quot;Существующие реализации&amp;quot; ). Однако они включают в себя модификацию исходного алгоритма. Также возможно распараллеливание выборки пар кластеров и подсчёта их метрик внутри каждой из вершин stage_3_iter. Структура информационного графа внутри этих итераций аналогична структуре информационного графа первого этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
[[File:CHAMELEON ruslanlazovskiy.png]]&lt;br /&gt;
&lt;br /&gt;
===Ресурс параллелизма алгоритма===&lt;br /&gt;
&lt;br /&gt;
Параллельную сложность алгоритма CHAMELEON следует оценивать отдельно для каждого этапа.&lt;br /&gt;
На первом этапе алгоритм поиска &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ближайших соседей может быть выполнен за &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; вычислений значения метрики между двумя векторами.&lt;br /&gt;
На втором этапе итерации алгоритма выполняются строго последовательно, а распараллеливание алгоритма внутри итерации не относится к теме статьи.&lt;br /&gt;
На третьем этапе итерации также выполняются последовательно, однако возможно распараллеливание обработки пар кластеров внутри итерации. Каждая итерация может быть выполнена за &amp;lt;math&amp;gt;q - 1&amp;lt;/math&amp;gt; вычислений значения метрики между двумя кластерами, где q - количество кластеров на этой итерации.&lt;br /&gt;
&lt;br /&gt;
===Входные и выходные данные алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Входные данные:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt; - набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов длины &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;. &amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число, определяющиее количество ближайших соседей, по которым строится первоначальный граф. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Выходные данные:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; - вектор длины &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, значение &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го элемента указывает на принадлежность соответствующего вектора одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
===Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Соотношение последовательной и параллельной сложности:&amp;lt;/b&amp;gt; Различные этапы алгоритма строго последовательны относительно друг друга. На первом этапе отношение последовательной сложности алгоритма к параллельной есть &amp;lt;math&amp;gt;n/2&amp;lt;/math&amp;gt;. На втором и третьем этапах различные итерации последовательны относительно друг друга. Однако внутри итераций допускается некоторый параллелизм. Например, для итераций третьего этапа в наилучшем случае отношение сложности последовательной реализации к параллельной есть &amp;lt;math&amp;gt;q/2&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; - число кластеров на конкретной итерации.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Вычислительная мощность:&amp;lt;/b&amp;gt; графовые алгоритмы, вообще говоря, обладают низкой вычислительной мощностью. В случае CHAMELEON вычислительная мощность первого этапа равна &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;. Для второго и третьего этапов оценка вычислительной мощности по сложениям и умножениям затруднительна, однако в терминах макроопераций можно дать грубую оценку. Вычислительная мощность каждой итерации второго этапа - &amp;lt;math&amp;gt;1 / \sum C_n^k&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число вершин в выбранном подграфе, а &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; варьируется от &amp;lt;math&amp;gt;0.25 * n&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.5 * n&amp;lt;/math&amp;gt;. Вычислительная мощность каждой итерации третьего этапа в этих терминах - &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Детерминированность:&amp;lt;/b&amp;gt; Важной особенностью описываемого алгоритма является его недетерминированность. Условия завершения как 2-го, так и 3-го этапа позволяют обозначить CHAMELEON как итерационный алгоритм с выходом по точности. При этом детерминированность 2-го этапа алгоритма можно обеспечить указанием вырожденных условий кластеризации ( такая кластеризация, очевидно, не будет иметь практического смысла ), в то время как 2-ый этап является недетерминированным всегда.&lt;br /&gt;
&lt;br /&gt;
==Программная реализация алгоритма==&lt;br /&gt;
===Особенности реализации последовательного алгоритма===&lt;br /&gt;
===Локальность данных и вычислений===&lt;br /&gt;
===Возможные способы и особенности параллельной реализации алгоритма===&lt;br /&gt;
===Масштабируемость алгоритма и его реализации===&lt;br /&gt;
&lt;br /&gt;
Как можно увидеть из раздела &amp;quot;существующие реализации&amp;quot;, параллельной реализации алгоритма CHAMELEON на система с распределенной памятью найдено не было. Однако в &amp;lt;ref name = &amp;quot;exp&amp;quot;&amp;gt;Parallel Algorithm for the Chameleon Clustering Algorithm using Dynamic Modeling.  Rajnish Dashora, Harsh Bajaj, Akshat Dube, Geetha Mary .A. VIT University,Vellore. International Journal of Computer Applications (0975 – 8887) Volume 79 – No8, October 2013 &amp;lt;/ref&amp;gt;. приводится экспериментальный вариант параллельной реализации посредством OpenMP для систем с общей памятью, включающий в себя значительную модификацию исходного алгоритма, но не приводится рабочая реализация.&lt;br /&gt;
&lt;br /&gt;
В силу вышескасновного основное внимание будет уделено исследованию масштабируемости алгоритма по размеру задачи, в пределах одного узла. Далее приводятся графики производительности в Гфлопс для различных размеров задачи. Эффективность работы алгоритма убывает на всей рассматриваемой области. Темпы убывания эффективности снижаются с ростом размера задачи.&lt;br /&gt;
&lt;br /&gt;
Для исследования такой масштабируемости вширь использовалась референсная последовательная реализация алгоритма ( м. раздел &amp;quot;Существующие реализации&amp;quot; ). Соответствующая программа запускалась на системе BG/P на одном вычислительном узле. Характеристики вычислительного узла, а также микропроцессорного ядра - элемента такого узла, приводятся в &amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt; Таким образом, удостоверенными экспериментальными данными являются только значения, полученные для случая 1 процесса. Значения для случаев 2 и 4 процессов являются ОЦЕНОЧНЫМИ и выводятся из результатов работы модифицированной версии алгоритма, исследуемой в &amp;lt;ref name = &amp;quot;exp&amp;quot;&amp;gt;Parallel Algorithm for the Chameleon Clustering Algorithm using Dynamic Modeling.  Rajnish Dashora, Harsh Bajaj, Akshat Dube, Geetha Mary .A. VIT University,Vellore. International Journal of Computer Applications (0975 – 8887) Volume 79 – No8, October 2013 &amp;lt;/ref&amp;gt;. К сожалению, указанной реализации авторы не предоставляют. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:CHAMELEON performance.png|800px]]&lt;br /&gt;
&lt;br /&gt;
===Динамические характеристики и эффективность реализации алгоритма===&lt;br /&gt;
===Выводы для классов архитектур===&lt;br /&gt;
===Существующие реализации алгоритма===&lt;br /&gt;
С копией референсной реализации иерархической кластеризации, взятой с данного ресурса [http://us.codeforge.com/article/192925 Sequental], в случае неработоспособности последнего, можно ознакомиться здесь [http://pastebin.com/PhC4X0Cy Copy]&amp;lt;br/&amp;gt;&lt;br /&gt;
Последовательная реализация алгоритма CHAMELEON использовалась в статье &amp;lt;ref&amp;gt;Clustering Of Web Usage Data Using&lt;br /&gt;
Chameleon Algorithm, T.Vijaya Kumar, Dr. H.S.Guruprasad, International Journal of Innovative Research in Computer&lt;br /&gt;
and Communication Engineering, Vol. 2, Issue 6, June 2014&amp;lt;/ref&amp;gt;&lt;br /&gt;
Теоретически возможна реализация алгоритма CHAMELEON с использованием графовых библиотек, например [http://glaros.dtc.umn.edu/gkhome/metis/metis/overview METIS ], [http://glaros.dtc.umn.edu/gkhome/metis/hmetis/overview hMETIS] и [https://cran.r-project.org/web/packages/RANN/index.html RANN].&amp;lt;br/&amp;gt;&lt;br /&gt;
Параллельных реализаций алгоритма CHAMELEON в графовых библиотеках мною найдено не было. Однако существует исследование, связанное с реализацией CHAMELEON посредством технологии OpenMP, с которым можно ознакомиться здесь [http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf параллельная реализация].&lt;br /&gt;
&lt;br /&gt;
==Литература==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Краткий обзор алгоритма [http://studopedia.ru/7_41934_algoritm-dinamicheskoy-ierarhicheskoy-klasterizatsii-CHAMELEON.html|]&amp;lt;br/&amp;gt;&lt;br /&gt;
Первоначальная статья от авторов алгоритма [http://www-users.cs.umn.edu/~han/dmclass/chameleon.pdf|]&amp;lt;br/&amp;gt;&lt;br /&gt;
Исследования возможной параллелизации алгоритма[http://research.ijcaonline.org/volume79/number8/pxc3891600.pdf|]&amp;lt;br/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Chachba.A&amp;diff=18076</id>
		<title>Обсуждение участника:Chachba.A</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:Chachba.A&amp;diff=18076"/>
		<updated>2016-11-18T09:43:20Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Быстрое дискретное преобразование Фурье&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
* п.п. 1.8, 1.9 не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:57, 25 октября 2016 (MSK)&lt;br /&gt;
** исправлено&lt;br /&gt;
* не указан конкретный вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:57, 25 октября 2016 (MSK)&lt;br /&gt;
** исправлено&lt;br /&gt;
* в п.п. 2.4 следует добавить описание компьютерной платформы, текст исследуемой программы.--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 13:41, 16 ноября 2016 (MSK)&lt;br /&gt;
** сделано&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Быстрое дискретное преобразование Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* Перенесите пожалуйста статью в личное пространство [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK) &lt;br /&gt;
** а как это сделать? У нас нет прав на это, похоже&lt;br /&gt;
*** пункт 1 на этой странице, надо написать модератору: [[Алговики:Студенческие работы]]&lt;br /&gt;
* Раздел 1.1 - в самом первом абзаце стоит вставить обозначения двух фунций - f и f с крышкой, чтобы было ясно, что куда отображается. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK) &lt;br /&gt;
** Done&lt;br /&gt;
* В общее описание алгоритма нужно вставить слова именно об алгоритме, а не только о математической задаче. В чем быстрота, за счет чего она достигается и т.д. Тут, в отличие от математического описания, не стоит увлекаться формулами. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
* Раздел 1.2 - существует обозначение части формулы через эпсилон, однако в следующей формуле он не встречается, странно. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
* Нужно пояснение, что происходит, когда N не представимо в виде произведения: применим ли алгоритм и в каких изменениях он нуждается. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
*** Обратите внимание на то, что объяснение других участников другое: [[Участник:Ivanov.kir.m/Быстрое дискретное преобразование Фурье]]&lt;br /&gt;
* Нет данных о глубине рекурсии, написать об этом явно, скорее всего, в разделе &amp;quot;вычислительное ядро алгоритма&amp;quot;. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
&lt;br /&gt;
* Все хорошо, кроме переноса статьи (написал об этом) и дописывания разделов про программную реализацию алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 12:43, 18 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=18069</id>
		<title>Быстрое дискретное преобразование Фурье (БПФ)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=18069"/>
		<updated>2016-11-18T09:39:24Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Assignment|IgorS}}&lt;br /&gt;
Авторы: [[Участник:Chachba.A|Чачба А.Н.]], [[Участник:Weasel|Костоев Р.С.]] &lt;br /&gt;
&lt;br /&gt;
[[Участник:Chachba.A|Чачба А.Н.]] заполнил половину всех заполненных пунктов (а именно 1.1, 1.2, 1.6, 1.8, 1.9) и построил информационный граф алгоритма, [[Участник:Weasel|Костоев Р.С.]] написал программу и провел серию запусков на суперкомпьютере и заполнил вторую половину пунктов. Оба после заполнения исправляли найденные ошибки во всех пунктах.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье (БПФ)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Преобразование Фурье - взаимно однозначное отображение одной функции &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; вещественной переменной, называемой таргетным сигналом, с другой функцией &amp;lt;math&amp;gt;\hat{f}&amp;lt;/math&amp;gt; вещественной переменной, называемой образом Фурье, или спектром исходной функции, по формуле:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{f}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}f(x)e^{-ix\omega}\,dx&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Дискретное преобразование Фурье, в свою очередь, есть аналог непрерывного преобразования Фурье, но для дискретного сигнала, содержащего &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; отсчетов.&lt;br /&gt;
Широко применяется в цифровой обработке сигналов, теории вероятностей, криптографии и акустике.&lt;br /&gt;
Преобразование Фурье обратимо, причем обратное преобразование имеет практически ту же форму, что и прямое.&lt;br /&gt;
Преобразование Фурье имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, но существует быстрый вариант преобразование Фурье со сложностью &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Такая скорость достигается благодаря применению схемы «разделяй и властвуй». Общий вид преобразования позволяет разделить массив на несколько частей таким образом, что, применяя преобразование Фурье к отдельным его частям и объединяя специальным образом результаты (при помощи поворотных множителей и повторного применения преобразования), получать на выходе образ исходного вектора. Но благодаря разделению массива исходного размера на части суммарная асимптотическая сложность алгоритма уменьшается.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Пусть исходный сигнал имеет значения &amp;lt;math&amp;gt;x_n,\quad n = 0,\dots,N-1&amp;lt;/math&amp;gt;, тогда дискретное прямое преобразование Фурье (ДПФ) имеет вид:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
X_k = \sum_{n=0}^{N-1}x_ne^{-\frac{2\pi i}{N}kn},\quad k = 0, \dots, N-1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt; \varepsilon_{N} = e^{-\frac{2\pi i}{N}}&amp;lt;/math&amp;gt;, тогда ДПФ можно перезаписать в матричной форме:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\bar X = A\bar x&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где матрица &amp;lt;math&amp;gt;A = \{\varepsilon_{N}^{(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt;&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Пусть, для простоты &amp;lt;math&amp;gt; N = km&amp;lt;/math&amp;gt;, тогда рекурсивная реализация преобразования Фурье, за счет &amp;lt;math&amp;gt;{k}&amp;lt;/math&amp;gt; рекурсий на первом этапе и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; на последнем этапе, имеет суммарную сложность &amp;lt;math&amp;gt;O(Nk + Nm + km) = O(N(k + m))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В случае, например &amp;lt;math&amp;gt;N = 2^n&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем же случае, когда &amp;lt;math&amp;gt;N = \prod_{i=1}^np_i&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt; O(N(\sum_{i=1}^np_i))&amp;lt;/math&amp;gt;. Так как рекурсия применяется каждый раз при отщеплении одного множителя, то можно заключить, что глубина рекурсии алгоритма составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&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&amp;lt;/math&amp;gt; не факторизуется в произведение простых множителей (то есть &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; — простое число), то данный алгоритм не применим. На замену ему приходит алгоритм Радера, имеющий худшую асимптотику, нежели обычный БПФ, но лучшую чем &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;. Также алгоритм Радера потребует дополнительного взаимодействия процессоров, что приведет к ухудшению эффективности.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Макроструктура БПФ для случая &amp;lt;math&amp;gt;N = km&amp;lt;/math&amp;gt; описывается рекурсивно:&lt;br /&gt;
# &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; независимых преобразований векторов меньшей размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&lt;br /&gt;
#Умножение элементов на поворотные коэффициенты (&amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; умножений)&lt;br /&gt;
# &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; обратных преобразований векторов размерностей &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Рекурсивный метод для случая &amp;lt;math&amp;gt;N = 2^k&amp;lt;/math&amp;gt;, без оптимизации на C++:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;complex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
typedef complex&amp;lt;double&amp;gt; cd;&lt;br /&gt;
typedef vector&amp;lt;cd&amp;gt; vcd;&lt;br /&gt;
&lt;br /&gt;
vcd fft(const vcd &amp;amp;as) { &lt;br /&gt;
  int n = as.size();&lt;br /&gt;
  if (n == 1) return vcd(1, as[0]);&lt;br /&gt;
&lt;br /&gt;
  vcd w(n); // Calculate roots&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    double alpha = 2 * M_PI * i / n;&lt;br /&gt;
    w[i] = cd(cos(alpha), sin(alpha));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  vcd A(n / 2), B(n / 2);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n / 2; i++) {&lt;br /&gt;
    A[i] = as[i * 2];&lt;br /&gt;
    B[i] = as[i * 2 + 1];&lt;br /&gt;
  }&lt;br /&gt;
  vcd Av = fft(A);&lt;br /&gt;
  vcd Bv = fft(B);&lt;br /&gt;
  vcd res(n);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    res[i] =   Av[i % (n / 2)] +&lt;br /&gt;
        w[i] * Bv[i % (n / 2)];&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
В последовательном варианте можно бороться за улучшение константы сложности, предподсчитав значения соответствующих коэффициентов.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Алгоритм состоит из трех этапов, следовательно если &amp;lt;math&amp;gt;N = \prod_{i = 1}^np_i&amp;lt;/math&amp;gt;, то общая сложность составляет порядка &amp;lt;math&amp;gt;O(N\sum_{i=1}^np_i)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Пусть существует некоторое фиксированное число &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, такое что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
p_i \leq P \quad \forall i = 1...n,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
тогда, учитывая, что &amp;lt;math&amp;gt;n \leq \log_{P}{N}&amp;lt;/math&amp;gt;, сложность алгоритма можно записать в виде &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:FourierGraph2.png|center|thumb|640px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф преобразования Кули-Тьюки для &amp;lt;math&amp;gt;N = km = 25, k = 5, m = 5&amp;lt;/math&amp;gt;. Желтые вершины - умножение на поворотные множители.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
В силу независимости всех рекурсивных вызовов и того факта, что путь максимальной длины в графе (так называемый критический путь) имеет порядок &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;, можно утверждать, что параллельная сложность алгоритма составит &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Замечание&#039;&#039;: формально основание логарифма на каждом рекурсивном шаге зависит от множителя в факторизации &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, потому использована О-символика.&lt;br /&gt;
&lt;br /&gt;
При условии ограниченности количества доступных вычислительных узлов рекомендуется выбирать множители в разложении числа $N$ близкими к числу доступных узлов, таким образом доступные ресурсы будут использованы максимально эффективно.&lt;br /&gt;
&lt;br /&gt;
Итого алгоритм относится к логарифмическому классу алгоритмов по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
Чаще всего для обработки сигналов в качестве входных данных для БПФ подается вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вещественных элементов. Но БПФ работает и для случая элементов над комплексным полем. Таким образом, например, можно экономить на количестве применений БПФ в некоторой конкретной задаче путем приведения двух векторов вещественных чисел к одному вектору комплексных чисел с вещественной частью равной первому вектору и комплексной частью равной второму вектору:&lt;br /&gt;
&amp;lt;math&amp;gt;x, y&amp;lt;/math&amp;gt; - исходные сигналы. Обозначим &amp;lt;math&amp;gt;z = x + iy&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;F(k, x)&amp;lt;/math&amp;gt; - это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-ый коэффициент образа Фурье вектора &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. Тогда:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учитывая, что &amp;lt;math&amp;gt;F(k, \bar z) = \overline{F(N - k, z)}&amp;lt;/math&amp;gt;, получаем:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + \overline{F(N-k, z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - \overline{F(N-k,z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Такой способ позволяет приблизительно в два раза уменьшить количество вычислений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
Вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; комплексных чисел - спектр исходного сигнала.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Матрица Вандермонда преобразования &amp;lt;math&amp;gt;A = \{e^{-\frac{2\pi i}{N}(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt; такая, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
A^{-1} = \frac{1}{N}A^*&lt;br /&gt;
&amp;lt;/math&amp;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;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Все запуски программы[http://pastebin.com/raw/xajKb9JN] проводились на с/к Blue Gene/P с использованием библиотеки Intel MKL.&lt;br /&gt;
&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 2048 четырехъядерных вычислительных узлов, с пиковой производительностью 27,9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W&lt;br /&gt;
&lt;br /&gt;
Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 ядра x 3,4 GFlop/sec на ядро = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 2.&#039;&#039;&#039; Время выполнения параллельной реализации БПФ в секундах. Size - число элементов массива. Р - число использованных процессоров.]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efff.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 3.&#039;&#039;&#039; Эффективность параллельной реализации БПФ. Size - число элементов массива. Р - число использованных процессоров. Эффективность в пределах от 0 до 1.]]&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]&lt;br /&gt;
* Реализация от Intel в рамках [//software.intel.com/en-us/intel-mkl Math Kernel Library]&lt;br /&gt;
* Реализация ДПФ на графических картах от NVidia - [//developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
* Описание алгоритма на Википедии: [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 Быстрое преобразование Фурье]&lt;br /&gt;
* Материалы лекций по &amp;quot;Алгоритмам и структурам данных&amp;quot; Школы Анализа Данных Яндекса&lt;/div&gt;</summary>
		<author><name>Kronberg</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=18068</id>
		<title>Быстрое дискретное преобразование Фурье (БПФ)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=18068"/>
		<updated>2016-11-18T09:35:39Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Assignment|IgorS}}&lt;br /&gt;
Авторы: [[Участник:Chachba.A|Чачба А.Н.]], [[Участник:Weasel|Костоев Р.С.]] &lt;br /&gt;
&lt;br /&gt;
[[Участник:Chachba.A|Чачба А.Н.]] заполнил половину всех заполненных пунктов (а именно 1.1, 1.2, 1.6, 1.8, 1.9) и построил информационный граф алгоритма, [[Участник:Weasel|Костоев Р.С.]] написал программу и провел серию запусков на суперкомпьютере и заполнил вторую половину пунктов. Оба после заполнения исправляли найденные ошибки во всех пунктах.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье (БПФ)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Преобразование Фурье - взаимно однозначное отображение одной функции &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; вещественной переменной, называемой таргетным сигналом, с другой функцией &amp;lt;math&amp;gt;\hat{f}&amp;lt;/math&amp;gt; вещественной переменной, называемой образом Фурье, или спектром исходной функции, по формуле:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{f}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}f(x)e^{-ix\omega}\,dx&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Дискретное преобразование Фурье, в свою очередь, есть аналог непрерывного преобразования Фурье, но для дискретного сигнала, содержащего &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; отсчетов.&lt;br /&gt;
Широко применяется в цифровой обработке сигналов, теории вероятностей, криптографии и акустике.&lt;br /&gt;
Преобразование Фурье обратимо, причем обратное преобразование имеет практически ту же форму, что и прямое.&lt;br /&gt;
Преобразование Фурье имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, но существует быстрый вариант преобразование Фурье со сложностью &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Такая скорость достигается благодаря применению схемы «разделяй и властвуй». Общий вид преобразования позволяет разделить массив на несколько частей таким образом, что, применяя преобразование Фурье к отдельным его частям и объединяя специальным образом результаты (при помощи поворотных множителей и повторного применения преобразования), получать на выходе образ исходного вектора. Но благодаря разделению массива исходного размера на части суммарная асимптотическая сложность алгоритма уменьшается.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Пусть исходный сигнал имеет значения &amp;lt;math&amp;gt;x_n,\quad n = 0,\dots,N-1&amp;lt;/math&amp;gt;, тогда дискретное прямое преобразование Фурье (ДПФ) имеет вид:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
X_k = \sum_{n=0}^{N-1}x_ne^{-\frac{2\pi i}{N}kn},\quad k = 0, \dots, N-1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt; \varepsilon_{N} = e^{-\frac{2\pi i}{N}}&amp;lt;/math&amp;gt;, тогда ДПФ можно перезаписать в матричной форме:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\bar X = A\bar x&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где матрица &amp;lt;math&amp;gt;A = \{\varepsilon_{N}^{(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt;&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Пусть, для простоты &amp;lt;math&amp;gt; N = km&amp;lt;/math&amp;gt;, тогда рекурсивная реализация преобразования Фурье, за счет &amp;lt;math&amp;gt;{k}&amp;lt;/math&amp;gt; рекурсий на первом этапе и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; на последнем этапе, имеет суммарную сложность &amp;lt;math&amp;gt;O(Nk + Nm + km) = O(N(k + m))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В случае, например &amp;lt;math&amp;gt;N = 2^n&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем же случае, когда &amp;lt;math&amp;gt;N = \prod_{i=1}^np_i&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt; O(N(\sum_{i=1}^np_i))&amp;lt;/math&amp;gt;. Так как рекурсия применяется каждый раз при отщеплении одного множителя, то можно заключить, что глубина рекурсии алгоритма составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&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&amp;lt;/math&amp;gt; не факторизуется в произведение простых множителей (то есть &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; - простое число), то данный алгоритм не применим. На замену ему приходит алгоритм Радера, имеющий худшую асимптотику, нежели обычный БПФ, но лучшую чем &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;. Также алгоритм Радера потребует дополнительного взаимодействия процессоров, что приведет к ухудшению эффективности.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Макроструктура БПФ для случая &amp;lt;math&amp;gt;N = km&amp;lt;/math&amp;gt; описывается рекурсивно:&lt;br /&gt;
# &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; независимых преобразований векторов меньшей размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&lt;br /&gt;
#Умножение элементов на поворотные коэффициенты (&amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; умножений)&lt;br /&gt;
# &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; обратных преобразований векторов размерностей &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Рекурсивный метод для случая &amp;lt;math&amp;gt;N = 2^k&amp;lt;/math&amp;gt;, без оптимизации на C++:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;complex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
typedef complex&amp;lt;double&amp;gt; cd;&lt;br /&gt;
typedef vector&amp;lt;cd&amp;gt; vcd;&lt;br /&gt;
&lt;br /&gt;
vcd fft(const vcd &amp;amp;as) { &lt;br /&gt;
  int n = as.size();&lt;br /&gt;
  if (n == 1) return vcd(1, as[0]);&lt;br /&gt;
&lt;br /&gt;
  vcd w(n); // Calculate roots&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    double alpha = 2 * M_PI * i / n;&lt;br /&gt;
    w[i] = cd(cos(alpha), sin(alpha));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  vcd A(n / 2), B(n / 2);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n / 2; i++) {&lt;br /&gt;
    A[i] = as[i * 2];&lt;br /&gt;
    B[i] = as[i * 2 + 1];&lt;br /&gt;
  }&lt;br /&gt;
  vcd Av = fft(A);&lt;br /&gt;
  vcd Bv = fft(B);&lt;br /&gt;
  vcd res(n);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    res[i] =   Av[i % (n / 2)] +&lt;br /&gt;
        w[i] * Bv[i % (n / 2)];&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
В последовательном варианте можно бороться за улучшение константы сложности, предподсчитав значения соответствующих коэффициентов.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Алгоритм состоит из трех этапов, следовательно если &amp;lt;math&amp;gt;N = \prod_{i = 1}^np_i&amp;lt;/math&amp;gt;, то общая сложность составляет порядка &amp;lt;math&amp;gt;O(N\sum_{i=1}^np_i)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Пусть существует некоторое фиксированное число &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, такое что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
p_i \leq P \quad \forall i = 1...n,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
тогда, учитывая, что &amp;lt;math&amp;gt;n \leq \log_{P}{N}&amp;lt;/math&amp;gt;, сложность алгоритма можно записать в виде &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:FourierGraph2.png|center|thumb|640px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф преобразования Кули-Тьюки для &amp;lt;math&amp;gt;N = km = 25, k = 5, m = 5&amp;lt;/math&amp;gt;. Желтые вершины - умножение на поворотные множители.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
В силу независимости всех рекурсивных вызовов и того факта, что путь максимальной длины в графе (так называемый критический путь) имеет порядок &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;, можно утверждать, что параллельная сложность алгоритма составит &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Замечание&#039;&#039;: формально основание логарифма на каждом рекурсивном шаге зависит от множителя в факторизации &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, потому использована О-символика.&lt;br /&gt;
&lt;br /&gt;
При условии ограниченности количества доступных вычислительных узлов рекомендуется выбирать множители в разложении числа $N$ близкими к числу доступных узлов, таким образом доступные ресурсы будут использованы максимально эффективно.&lt;br /&gt;
&lt;br /&gt;
Итого алгоритм относится к логарифмическому классу алгоритмов по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
Чаще всего для обработки сигналов в качестве входных данных для БПФ подается вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вещественных элементов. Но БПФ работает и для случая элементов над комплексным полем. Таким образом, например, можно экономить на количестве применений БПФ в некоторой конкретной задаче путем приведения двух векторов вещественных чисел к одному вектору комплексных чисел с вещественной частью равной первому вектору и комплексной частью равной второму вектору:&lt;br /&gt;
&amp;lt;math&amp;gt;x, y&amp;lt;/math&amp;gt; - исходные сигналы. Обозначим &amp;lt;math&amp;gt;z = x + iy&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;F(k, x)&amp;lt;/math&amp;gt; - это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-ый коэффициент образа Фурье вектора &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. Тогда:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учитывая, что &amp;lt;math&amp;gt;F(k, \bar z) = \overline{F(N - k, z)}&amp;lt;/math&amp;gt;, получаем:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + \overline{F(N-k, z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - \overline{F(N-k,z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Такой способ позволяет приблизительно в два раза уменьшить количество вычислений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
Вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; комплексных чисел - спектр исходного сигнала.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Матрица Вандермонда преобразования &amp;lt;math&amp;gt;A = \{e^{-\frac{2\pi i}{N}(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt; такая, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
A^{-1} = \frac{1}{N}A^*&lt;br /&gt;
&amp;lt;/math&amp;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;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Все запуски программы[http://pastebin.com/raw/xajKb9JN] проводились на с/к Blue Gene/P с использованием библиотеки Intel MKL.&lt;br /&gt;
&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 2048 четырехъядерных вычислительных узлов, с пиковой производительностью 27,9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W&lt;br /&gt;
&lt;br /&gt;
Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 ядра x 3,4 GFlop/sec на ядро = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 2.&#039;&#039;&#039; Время выполнения параллельной реализации БПФ в секундах. Size - число элементов массива. Р - число использованных процессоров.]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efff.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 3.&#039;&#039;&#039; Эффективность параллельной реализации БПФ. Size - число элементов массива. Р - число использованных процессоров. Эффективность в пределах от 0 до 1.]]&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]&lt;br /&gt;
* Реализация от Intel в рамках [//software.intel.com/en-us/intel-mkl Math Kernel Library]&lt;br /&gt;
* Реализация ДПФ на графических картах от NVidia - [//developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
* Описание алгоритма на Википедии: [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 Быстрое преобразование Фурье]&lt;br /&gt;
* Материалы лекций по &amp;quot;Алгоритмам и структурам данных&amp;quot; Школы Анализа Данных Яндекса&lt;/div&gt;</summary>
		<author><name>Kronberg</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=18067</id>
		<title>Быстрое дискретное преобразование Фурье (БПФ)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=18067"/>
		<updated>2016-11-18T09:34:29Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Assignment|IgorS}}&lt;br /&gt;
Авторы: [[Участник:Chachba.A|Чачба А.Н.]], [[Участник:Weasel|Костоев Р.С.]] &lt;br /&gt;
&lt;br /&gt;
[[Участник:Chachba.A|Чачба А.Н.]] заполнил половину всех заполненных пунктов (а именно 1.1, 1.2, 1.6, 1.8, 1.9) и построил информационный граф алгоритма, [[Участник:Weasel|Костоев Р.С.]] написал программу и провел серию запусков на суперкомпьютере и заполнил вторую половину пунктов. Оба после заполнения исправляли найденные ошибки во всех пунктах.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье (БПФ)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Преобразование Фурье - взаимно однозначное отображение одной функции &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; вещественной переменной, называемой таргетным сигналом, с другой функцией &amp;lt;math&amp;gt;\hat{f}&amp;lt;/math&amp;gt; вещественной переменной, называемой образом Фурье, или спектром исходной функции, по формуле:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{f}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}f(x)e^{-ix\omega}\,dx&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Дискретное преобразование Фурье, в свою очередь, есть аналог непрерывного преобразования Фурье, но для дискретного сигнала содержащего &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; отсчетов.&lt;br /&gt;
Широко применяется в цифровой обработке сигналов, теории вероятностей, криптографии и акустике.&lt;br /&gt;
Преобразование Фурье обратимо, причем обратное преобразование имеет практически ту же форму, что и прямое.&lt;br /&gt;
Преобразование Фурье имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, но существует быстрый вариант преобразование Фурье со сложностью &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
Такая скорость достигается благодаря применению схемы &amp;quot;разделяй и властвуй&amp;quot;. Общий вид преобразования позволяет разделить массив на несколько частей таким образом, что, применяя преобразование Фурье к отдельным его частям и объединяя специальным образом результаты (при помощи поворотных множителей и повторного применения преобразования), получать на выходе образ исходного вектора. Но благодаря разделению массива исходного размера на части суммарная асимптотическая сложность алгоритма уменьшается.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Пусть исходный сигнал имеет значения &amp;lt;math&amp;gt;x_n,\quad n = 0,\dots,N-1&amp;lt;/math&amp;gt;, тогда дискретное прямое преобразование Фурье (ДПФ) имеет вид:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
X_k = \sum_{n=0}^{N-1}x_ne^{-\frac{2\pi i}{N}kn},\quad k = 0, \dots, N-1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt; \varepsilon_{N} = e^{-\frac{2\pi i}{N}}&amp;lt;/math&amp;gt;, тогда ДПФ можно перезаписать в матричной форме:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\bar X = A\bar x&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где матрица &amp;lt;math&amp;gt;A = \{\varepsilon_{N}^{(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt;&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Пусть, для простоты &amp;lt;math&amp;gt; N = km&amp;lt;/math&amp;gt;, тогда рекурсивная реализация преобразования Фурье, за счет &amp;lt;math&amp;gt;{k}&amp;lt;/math&amp;gt; рекурсий на первом этапе и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; на последнем этапе, имеет суммарную сложность &amp;lt;math&amp;gt;O(Nk + Nm + km) = O(N(k + m))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В случае, например &amp;lt;math&amp;gt;N = 2^n&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем же случае, когда &amp;lt;math&amp;gt;N = \prod_{i=1}^np_i&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt; O(N(\sum_{i=1}^np_i))&amp;lt;/math&amp;gt;. Так как рекурсия применяется каждый раз при отщеплении одного множителя, то можно заключить, что глубина рекурсии алгоритма составляет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&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&amp;lt;/math&amp;gt; не факторизуется в произведение простых множителей (то есть &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; - простое число), то данный алгоритм не применим. На замену ему приходит алгоритм Радера, имеющий худшую асимптотику, нежели обычный БПФ, но лучшую чем &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;. Также алгоритм Радера потребует дополнительного взаимодействия процессоров, что приведет к ухудшению эффективности.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Макроструктура БПФ для случая &amp;lt;math&amp;gt;N = km&amp;lt;/math&amp;gt; описывается рекурсивно:&lt;br /&gt;
# &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; независимых преобразований векторов меньшей размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&lt;br /&gt;
#Умножение элементов на поворотные коэффициенты (&amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; умножений)&lt;br /&gt;
# &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; обратных преобразований векторов размерностей &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Рекурсивный метод для случая &amp;lt;math&amp;gt;N = 2^k&amp;lt;/math&amp;gt;, без оптимизации на C++:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;complex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
typedef complex&amp;lt;double&amp;gt; cd;&lt;br /&gt;
typedef vector&amp;lt;cd&amp;gt; vcd;&lt;br /&gt;
&lt;br /&gt;
vcd fft(const vcd &amp;amp;as) { &lt;br /&gt;
  int n = as.size();&lt;br /&gt;
  if (n == 1) return vcd(1, as[0]);&lt;br /&gt;
&lt;br /&gt;
  vcd w(n); // Calculate roots&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    double alpha = 2 * M_PI * i / n;&lt;br /&gt;
    w[i] = cd(cos(alpha), sin(alpha));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  vcd A(n / 2), B(n / 2);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n / 2; i++) {&lt;br /&gt;
    A[i] = as[i * 2];&lt;br /&gt;
    B[i] = as[i * 2 + 1];&lt;br /&gt;
  }&lt;br /&gt;
  vcd Av = fft(A);&lt;br /&gt;
  vcd Bv = fft(B);&lt;br /&gt;
  vcd res(n);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    res[i] =   Av[i % (n / 2)] +&lt;br /&gt;
        w[i] * Bv[i % (n / 2)];&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
В последовательном варианте можно бороться за улучшение константы сложности, предподсчитав значения соответствующих коэффициентов.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Алгоритм состоит из трех этапов, следовательно если &amp;lt;math&amp;gt;N = \prod_{i = 1}^np_i&amp;lt;/math&amp;gt;, то общая сложность составляет порядка &amp;lt;math&amp;gt;O(N\sum_{i=1}^np_i)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Пусть существует некоторое фиксированное число &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, такое что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
p_i \leq P \quad \forall i = 1...n,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
тогда, учитывая, что &amp;lt;math&amp;gt;n \leq \log_{P}{N}&amp;lt;/math&amp;gt;, сложность алгоритма можно записать в виде &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:FourierGraph2.png|center|thumb|640px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф преобразования Кули-Тьюки для &amp;lt;math&amp;gt;N = km = 25, k = 5, m = 5&amp;lt;/math&amp;gt;. Желтые вершины - умножение на поворотные множители.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
В силу независимости всех рекурсивных вызовов и того факта, что путь максимальной длины в графе (так называемый критический путь) имеет порядок &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;, можно утверждать, что параллельная сложность алгоритма составит &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Замечание&#039;&#039;: формально основание логарифма на каждом рекурсивном шаге зависит от множителя в факторизации &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, потому использована О-символика.&lt;br /&gt;
&lt;br /&gt;
При условии ограниченности количества доступных вычислительных узлов рекомендуется выбирать множители в разложении числа $N$ близкими к числу доступных узлов, таким образом доступные ресурсы будут использованы максимально эффективно.&lt;br /&gt;
&lt;br /&gt;
Итого алгоритм относится к логарифмическому классу алгоритмов по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
Чаще всего для обработки сигналов в качестве входных данных для БПФ подается вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вещественных элементов. Но БПФ работает и для случая элементов над комплексным полем. Таким образом, например, можно экономить на количестве применений БПФ в некоторой конкретной задаче путем приведения двух векторов вещественных чисел к одному вектору комплексных чисел с вещественной частью равной первому вектору и комплексной частью равной второму вектору:&lt;br /&gt;
&amp;lt;math&amp;gt;x, y&amp;lt;/math&amp;gt; - исходные сигналы. Обозначим &amp;lt;math&amp;gt;z = x + iy&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;F(k, x)&amp;lt;/math&amp;gt; - это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-ый коэффициент образа Фурье вектора &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. Тогда:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учитывая, что &amp;lt;math&amp;gt;F(k, \bar z) = \overline{F(N - k, z)}&amp;lt;/math&amp;gt;, получаем:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + \overline{F(N-k, z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - \overline{F(N-k,z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Такой способ позволяет приблизительно в два раза уменьшить количество вычислений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
Вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; комплексных чисел - спектр исходного сигнала.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Матрица Вандермонда преобразования &amp;lt;math&amp;gt;A = \{e^{-\frac{2\pi i}{N}(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt; такая, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
A^{-1} = \frac{1}{N}A^*&lt;br /&gt;
&amp;lt;/math&amp;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;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Все запуски программы[http://pastebin.com/raw/xajKb9JN] проводились на с/к Blue Gene/P с использованием библиотеки Intel MKL.&lt;br /&gt;
&lt;br /&gt;
IBM Blue Gene/P — массивно-параллельная вычислительная система, которая состоит из двух стоек, включающих 2048 четырехъядерных вычислительных узлов, с пиковой производительностью 27,9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Характеристики системы:&lt;br /&gt;
* две стойки с вычислительными узлами и узлами ввода-вывода&lt;br /&gt;
* 1024 четырехъядерных вычислительных узла в каждой из стоек&lt;br /&gt;
* 16 узлов ввода-вывода в стойке (в текущей конфигурации активны 8, т.е. одна I/O-карта на 128 вычислительных узлов)&lt;br /&gt;
* программирование с использованием MPI, OpenMP/pthreads, POSIX I/O&lt;br /&gt;
* высокая энергоэффективность: ∼ 372 MFlops/W&lt;br /&gt;
&lt;br /&gt;
Вычислительный узел включает в себя четырехъядерный процессор, 2 ГБ общей памяти и сетевые интерфейсы.&lt;br /&gt;
&lt;br /&gt;
Вычислительной узел:&lt;br /&gt;
* четыре микропроцессорных ядра PowerPC 450 (4-way SMP)&lt;br /&gt;
* пиковая производительность: 4 ядра x 3,4 GFlop/sec на ядро = 13,6 GFlop/sec&lt;br /&gt;
* пропускная способность памяти: 13,6 GB/sec&lt;br /&gt;
* 2 ГБ общей памяти&lt;br /&gt;
* 2 x 4 МБ кэш-памяти 2-го уровня (в документации по BG/P носит название L3)&lt;br /&gt;
* легковесное ядро (compute node kernel, CNK), представляющее собой Linux-подобную операционную систему, поддерживающую значительное подмножество Linux-совместимых системных вызовов&lt;br /&gt;
* асинхронные операции межпроцессорных обменов (выполняются параллельно с вычислениями)&lt;br /&gt;
* операции ввода-вывода перенаправляются I/O-картам через сеть коллективных операций&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 2.&#039;&#039;&#039; Время выполнения параллельной реализации БПФ в секундах. Size - число элементов массива. Р - число использованных процессоров.]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efff.PNG|center|thumb|640px|&#039;&#039;&#039;Рисунок 3.&#039;&#039;&#039; Эффективность параллельной реализации БПФ. Size - число элементов массива. Р - число использованных процессоров. Эффективность в пределах от 0 до 1.]]&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]&lt;br /&gt;
* Реализация от Intel в рамках [//software.intel.com/en-us/intel-mkl Math Kernel Library]&lt;br /&gt;
* Реализация ДПФ на графических картах от NVidia - [//developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
* Описание алгоритма на Википедии: [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 Быстрое преобразование Фурье]&lt;br /&gt;
* Материалы лекций по &amp;quot;Алгоритмам и структурам данных&amp;quot; Школы Анализа Данных Яндекса&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Chachba.A&amp;diff=18066</id>
		<title>Обсуждение участника:Chachba.A</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:Chachba.A&amp;diff=18066"/>
		<updated>2016-11-18T09:33:12Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Быстрое дискретное преобразование Фурье&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
* п.п. 1.8, 1.9 не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:57, 25 октября 2016 (MSK)&lt;br /&gt;
** исправлено&lt;br /&gt;
* не указан конкретный вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:57, 25 октября 2016 (MSK)&lt;br /&gt;
** исправлено&lt;br /&gt;
* в п.п. 2.4 следует добавить описание компьютерной платформы, текст исследуемой программы.--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 13:41, 16 ноября 2016 (MSK)&lt;br /&gt;
** сделано&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Быстрое дискретное преобразование Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* Перенесите пожалуйста статью в личное пространство [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK) &lt;br /&gt;
** а как это сделать? У нас нет прав на это, похоже&lt;br /&gt;
*** пункт 1 на этой странице, надо написать модератору: [[Алговики:Студенческие работы]]&lt;br /&gt;
* Раздел 1.1 - в самом первом абзаце стоит вставить обозначения двух фунций - f и f с крышкой, чтобы было ясно, что куда отображается. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK) &lt;br /&gt;
** Done&lt;br /&gt;
* В общее описание алгоритма нужно вставить слова именно об алгоритме, а не только о математической задаче. В чем быстрота, за счет чего она достигается и т.д. Тут, в отличие от математического описания, не стоит увлекаться формулами. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
* Раздел 1.2 - существует обозначение части формулы через эпсилон, однако в следующей формуле он не встречается, странно. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
* Нужно пояснение, что происходит, когда N не представимо в виде произведения: применим ли алгоритм и в каких изменениях он нуждается. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;br /&gt;
* Нет данных о глубине рекурсии, написать об этом явно, скорее всего, в разделе &amp;quot;вычислительное ядро алгоритма&amp;quot;. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
** Done&lt;/div&gt;</summary>
		<author><name>Kronberg</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=18065</id>
		<title>Обсуждение участника:Ivanov.kir.m</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=18065"/>
		<updated>2016-11-18T09:30:04Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье Быстрое дискретное преобразование Фурье */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановке первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Указали в шапке статьи&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено&lt;br /&gt;
* в  п.п. 2.4 следует добавить описание компьютерной платформы--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 13:35, 16 ноября 2016 (MSK)&lt;br /&gt;
** Добавили&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье Быстрое дискретное преобразование Фурье ==&lt;br /&gt;
&lt;br /&gt;
* В разделе &amp;quot;Общее описание алгоритма&amp;quot; следует вставить именно описание алгоритма. Потому что смысл алгоритма проясняется только в следующем разделе, а желательно иметь его общее (с минимумом формул) описание во введении [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Стоит добавить слова о том, что происходит, когда размер массива не представим в виде произведения - можно ли тогда применять алгоритм, и если да, то какие изменения следует сделать [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не вполне понятна логика использования курсива и полужирного начертания [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
* Не хватает данных о глубине рекурсии при использовании алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:12, 11 ноября 2016 (MSK)&lt;br /&gt;
** Сделали&lt;br /&gt;
&lt;br /&gt;
* Все хорошо, осталось только написать недостающие разделы по программной реализации алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 12:30, 18 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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=18064</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=18064"/>
		<updated>2016-11-18T09:28:37Z</updated>

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

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

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

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

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к Участник:Артем_Таран/Графо-ориентированный_алгоритм_кластеризации_CHAMELEON */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к [[Участник:Артем_Таран/Графо-ориентированный_алгоритм_кластеризации_CHAMELEON]] ==&lt;br /&gt;
&lt;br /&gt;
* отсутствует оценка вычислительной мощности алгоритма в п 1.10&lt;br /&gt;
&lt;br /&gt;
* В п.1.1. следует добавить информацию о том, известно ли число кластеров заранее [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:26, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* В п.1.2. наверное стоит отнести параметр k к исходным данным [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:26, 14 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Ruslanlazovskiy&amp;diff=16942</id>
		<title>Обсуждение участника:Ruslanlazovskiy</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:Ruslanlazovskiy&amp;diff=16942"/>
		<updated>2016-11-14T11:09:01Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье про Хамелеон (ещё порция) */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к [[Участник:Ruslanlazovskiy/CHAMELEON]] ==&lt;br /&gt;
&lt;br /&gt;
1) все ссылки в статье не работают&lt;br /&gt;
&lt;br /&gt;
2) список литературы оформлен неправильно - см. как сделано в [[Разложение_Холецкого_(метод_квадратного_корня)]]&lt;br /&gt;
&lt;br /&gt;
3) для оформления (псевдо) кода следует использовать тег &amp;lt;nowiki&amp;gt;&amp;lt;source lang=&amp;quot;...&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Мои правки ==&lt;br /&gt;
&lt;br /&gt;
2) и 3) теперь сделаны как надо. В 1) не удалось поправить самую первую ссылку, ресурс с последовательной реализацией действительно умер.&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье про Хамелеон (ещё порция) ==&lt;br /&gt;
&lt;br /&gt;
* Ссылки на авторов и список литературы - присоединяюсь к замечаниям. Список литературы действительно всего из одного наименования? [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* В разделе 1 пояснить, нужно ли заранее знать число кластеров. Это неясно и из описания входных и выходных данных, должно же оно быть хоть где-нибудь [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.2. Когда вводим граф, желательно пояснить аргументы V и E в скобках. Аналогично с параметром m, который встречается впервые [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.6. Стоит сделать логарифм красивей [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:09, 14 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Karibov&amp;diff=16423</id>
		<title>Обсуждение участника:Karibov</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:Karibov&amp;diff=16423"/>
		<updated>2016-11-11T13:30:39Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Алгоритм кластеризации с использованием представлений&amp;quot; */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Замечания к статье ===&lt;br /&gt;
&lt;br /&gt;
* не указан конкретный вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 16:04, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Алгоритм кластеризации с использованием представлений&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* П.1.2. Мало сказано о входном наборе данных. Если это могут быть точки произвольной размерности, это надо формализовать [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:30, 11 ноября 2016 (MSK)&lt;br /&gt;
* П.1.2. Не сказано, как именно выбираются точки-представители, чтобы они были максимально удалены, это нетривиально [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:30, 11 ноября 2016 (MSK)&lt;br /&gt;
* не сказано об оптимальном выборе параметров алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:30, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Karibov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=16422</id>
		<title>Участник:Karibov/Алгоритм кластеризации с использованием представлений</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:Karibov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=16422"/>
		<updated>2016-11-11T13:25:56Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации с использованием представлений&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[U:Karibov|Карибов Евгений]], [[U:x.kurashov|Курашов Александр]]&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;
Ограничения: эффективен для данных низкой размерности, работает только на числовых данных.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описание алгоритма [1]:&lt;br /&gt;
&lt;br /&gt;
Шаг 1: Построение дерева кластеров, состоящего из каждой строки входного набора данных.&lt;br /&gt;
&lt;br /&gt;
Шаг 2: Формирование «кучи» в оперативной памяти, расчет расстояния до ближайшего кластера&lt;br /&gt;
(строки данных) для каждого кластера. При формировании кучи кластеры сортируются по возрастанию&lt;br /&gt;
дистанции от кластера до ближайшего кластера. Расстояние между кластерами определяется по двум&lt;br /&gt;
ближайшим элементам из соседних кластеров. Для определения расстояния между кластерами&lt;br /&gt;
используются «манхеттенская», «евклидова» метрики или похожие на них функции.&lt;br /&gt;
&lt;br /&gt;
Шаг 3: Слияние ближних кластеров в один кластер. Новый кластер получает все точки входящих в&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;
ближайший кластер, то ближайший кластер меняется. В противном случае ничего не происходит.&lt;br /&gt;
&lt;br /&gt;
Шаг 4: Переход на шаг 3, если не получено требуемое количество кластеров.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Исходные данные: множество данных &amp;lt;math&amp;gt;\{p_i\}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число кластеров, на  которое необходимо разбить множество, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; число точек для кластеризации, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - размерность множества, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; задаёт какая часть от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек будет использована при случайной инициализации.&lt;br /&gt;
&lt;br /&gt;
Вычисляемые данные: множества &amp;lt;math&amp;gt;C_i, i = 1..k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формулы метода:&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; вычисляем центроид: &amp;lt;math&amp;gt;\mu _i = \frac{1}{n_i} \sum^{}_{j \in C_i} {j}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;n_i &amp;lt;/math&amp;gt; - количество точек в кластере &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для вычисления расстояния между кластерами для поиска подходящих для объединения кластеров могут быть использованы следующие формулы: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d_{mean}(C_i, C_j) = \left \|\mu _i - \mu _j\right \|&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d_{ave}(C_i, C_j) = \frac{1}{n_in_j} {\sum^{}_{p\in C_i} {\sum^{}_{p&#039;\in C_j} {\left \|p - p&#039;\right \|}}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d_{max}(C_i, C_j) = \max_{p&#039;\in C_j, p&#039;\in C_j}\left \|p - p&#039;\right \|&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d_{min}(C_i, C_j) = \min_{p&#039;\in C_j, p&#039;\in C_j}\left \|p - p&#039;\right \|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Объединяем ближайшие кластеры.&lt;br /&gt;
&lt;br /&gt;
Затем для каждого кластера определяются &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек представителей максимально удалённых друг от друга.&lt;br /&gt;
&lt;br /&gt;
Затем каждую точку представителя выполняется сдвигают к центроиде соответствующего кластера в &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; раз.&lt;br /&gt;
&lt;br /&gt;
Затем происходит присвоение неиспользованных точек к кластерам.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительное ядро алгоритма представляет собой итерационное построение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров, вычисление их центроидов и назначение точек представителей.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Основную часть метода составляют разбиение случайной выборки строк на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров и нахождение точек-представителей для каждого кластера.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
[[Файл:CURE.jpg|800px|thumb|center|Рисунок 1. Схема реализации [2]]]&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
В худшем случае иерархический алгоритм кластеризации имеет сложность  &amp;lt;math&amp;gt;O(n^2 log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
Для пространств небольших размерностей достигается сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
В начале работы алгоритма все объекты являются отдельными кластерами. На первом шаге наиболее похожие объекты объединяются в кластер. На последующих шагах объединение продолжается до тех пор, пока число кластеров не будет равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:IG_CURE.jpg|800px|thumb|center|Рисунок 2. Информационный граф]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Каждый шаг алгоритма выполняется независимо от других кластеров и их представителей. Это дает предпосылки к возможности хорошего распараллеливания метода кластеризации на каждом этапе алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
Входные данные: множество данных &amp;lt;math&amp;gt;\{p_i\}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число кластеров, на  которое необходимо разбить множество, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; число точек для кластеризации, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - размерность множества, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; задаёт какая часть от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек будет использована при случайной инициализации.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: множества &amp;lt;math&amp;gt;C_i, i = 1..k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Достоинства: выполняет кластеризацию на высоком уровне даже при наличии выбросов, выделяет&lt;br /&gt;
кластеры сложной формы и различных размеров, обладает линейно зависимыми требованиями к месту&lt;br /&gt;
хранения данных и временную сложность для данных высокой размерности.&lt;br /&gt;
&lt;br /&gt;
Недостатки: есть необходимость в задании пороговых значений и количества кластеров [1].&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;
&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;
&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Последовательная реализация CURE-алгоритма для кластеризации для Python/C++ представлена в пакете: [https://github.com/annoviko/pyclustering pyclustering].&lt;br /&gt;
&lt;br /&gt;
Также существует параллельная реализация алгоритма, описанная в сборнике [3].&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Нейский И.М. Классификация и сравнение методов кластеризации http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf.&lt;br /&gt;
&lt;br /&gt;
[2] Guha, Sudipto; Rastogi, Rajeev; Shim, Kyuseok (2001). &amp;quot;CURE: An Efficient Clustering Algorithm for Large Databases&amp;quot; (PDF). Information Systems. 26 (1): 35–58. doi:10.1016/S0306-4379(01)00008-4.&lt;br /&gt;
&lt;br /&gt;
[3] Panagiotis E. Hadjidoukas, Laurent Amsaleg. OpenMP Shared Memory Parallel Programming. Volume 4315 of the series Lecture Notes in Computer Science pp 289-299 Parallelization of a Hierarchical Data Clustering Algorithm Using OpenMP.&lt;br /&gt;
&lt;br /&gt;
[4] Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. - СПб.: БХВ-Петербург, 2002.&lt;/div&gt;</summary>
		<author><name>Kronberg</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0&amp;diff=16421</id>
		<title>Обсуждение участника:Лукьянова Анна</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:%D0%9B%D1%83%D0%BA%D1%8C%D1%8F%D0%BD%D0%BE%D0%B2%D0%B0_%D0%90%D0%BD%D0%BD%D0%B0&amp;diff=16421"/>
		<updated>2016-11-11T13:24:36Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Алгоритм кластеризации с использованием представлений&amp;quot; */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Замечания к статье ===&lt;br /&gt;
&lt;br /&gt;
* содержание не соответствуют шаблону описания алгоритма: отсутствуют п.п. во 2 части--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 16:01, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Алгоритм кластеризации с использованием представлений&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* п.1.1.1. Если взялись за классификацию алгоритмов, лучше разъяснить, что такое агломеративные, дивизивные, &amp;quot;четкие&amp;quot; и &amp;quot;нечеткие&amp;quot; алгоритмы. Было бы здорово даже сделать это в отдельной статье про задачу кластеризации, но это не обязательно [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* п.1.1.2. Метрики и некоторые подробности лучше перенести ниже, к &amp;quot;математическому описанию&amp;quot; [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* п.1.2 выглядит дописанным не до конца. Там нужно разъяснить те неформальные конструкции, которые встречались ранее в п. 1.1.2. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* п.1.5. Псевдокод слишком псевдо, его следует сделать больше похожим на код, с соответствующим форматированием [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;br /&gt;
* следует добавить информацию об оптимальном выборе параметров алгоритма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:24, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:JuliaA&amp;diff=16420</id>
		<title>Обсуждение участника:JuliaA</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:JuliaA&amp;diff=16420"/>
		<updated>2016-11-11T13:12:51Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Статья [[Алгоритм кластеризации с использованием представлений]] =&lt;br /&gt;
* Не указан вклад каждого автора [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* В заголовках разделов 1 и 2 слово &amp;quot;ЧАСТЬ&amp;quot; лишнее [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.2 начиная с шага &amp;quot;Назначение представителей&amp;quot; математическое описание превращается в словесное [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.3 состоит из одного предложения, не дает разумной детализации [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Не исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.4 состоит из одного предложения, не дает разумной детализации [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Не исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.5 состоит из одного рисунка, достаточность информации под вопросом [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Не исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.6 приведенные оценки сложности не обоснованы [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.7 приведен рисунок чего-то, отдаленно напоминающее информационный граф, для одного из шагов алгоритма, информационного графа для алгоритма в целом не приведено [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Не исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.8. не приведено оценок параллельной сложности алгоритма [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Не исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.9 не приведено оценок объема входных и выходных данных [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Не исправлено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* Ссылки на п. 1 и 2 из списка литературы в тексте отсутствуют [[Участник:Coctic|Coctic]] ([[Обсуждение участника:Coctic|обсуждение]]) 12:31, 25 октября 2016 (MSK)&lt;br /&gt;
* Исправлено&lt;br /&gt;
-----&lt;br /&gt;
Неисправленные замечания:&lt;br /&gt;
&lt;br /&gt;
* п. 1.3 состоит из одного предложения, не дает разумной детализации [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.4 состоит из одного предложения, не дает разумной детализации [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.5 состоит из одной подписи к рисунку без самого рисунка, фактически пункт отсутствует [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK) - &amp;quot;Я нашёл небольшую опечатку: одном из блоков IF ветвь &#039;Нет&#039; не была подписана. Попытался перезагрузить картинку несколько раз, но ничего не изменилось&amp;quot; [[Участник:Cookie|С. Шатков]] 12:04, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.7 приведен рисунок чего-то, отдаленно напоминающее информационный граф, для одного из шагов алгоритма, информационного графа для алгоритма в целом не приведено [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.8 не приведено оценок параллельной сложности алгоритма [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
* п. 1.9 не приведено оценок объема входных и выходных данных [[Участник:Coctic|К. Стефанов]] ([[Обсуждение участника:Coctic|обсуждение]]) 11:03, 1 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* В п.1.1. следует явно указать, задается ли число кластеров h заранее, или вычисляется в процессе работы алгоритма. В п.1.4 об этом упомянуто, но нужно сказать раньше, так как это важное свойство [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:12, 11 ноября 2016 (MSK)&lt;br /&gt;
* Не хватает слов о том, из каких соображений выбираются параметры алгоритма, и как их оптимальных выбор зависит от ситуации. Интересуют параметры числа точек-представителей, альфа, гамма [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:12, 11 ноября 2016 (MSK)&lt;br /&gt;
* Понятие &amp;quot;точка-представитель&amp;quot; лучше писать через дефис [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 16:12, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:JuliaA/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=16419</id>
		<title>Участник:JuliaA/Алгоритм кластеризации с использованием представлений</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:JuliaA/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9&amp;diff=16419"/>
		<updated>2016-11-11T12:56:28Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Основные авторы описания: &amp;lt;br&amp;gt;&lt;br /&gt;
[[Участник:JuliaA|Ю.А. Антохина]], разделы [[#Общее описание алгоритма|1.1]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]] - [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]]&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Участник:Cookie|С.А. Шатков]], разделы [[#Математическое описание алгоритма|1.2]] - [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Масштабируемость алгоритма и его реализации|2.4]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Пусть дан набор точек и заданы правила определения расстояния между ними. Задача кластеризации сгруппировать эти точки в определенное число кластеров. Члены одного кластера похожи, а члены разных классов различны.&lt;br /&gt;
&lt;br /&gt;
В алгоритме CURE применяется иерархическая кластеризация. Есть два подхода к иерархической кластеризации. В одном случае один большой кластер на каждом шаге разбивается на несколько, пока число кластеров не будет равно заданному заранее числу кластеров. В другом случае наоборот кластеры, состоящие каждый из одной точки объединяются в большие. Второй метод используется в CURE.&lt;br /&gt;
&lt;br /&gt;
Результат кластеризации зависит от того, как будут представлены кластеры. Например, средним всех точек кластера, или всеми точками кластера. Применение среднего возможно только в евклидовом пространстве. А использование всех точек неустойчиво к случайным выбросам. В CURE используется другой метод: для каждого кластера выбираются точки-представители. Выбирается постоянное число точек, которые будут представлять каждый кластер. Кластеры с наиболее похожими наборами репрезентативных точек объединяются на каждом шаге алгоритма.&lt;br /&gt;
&lt;br /&gt;
За счет использования точек представителей алгоритм CURE устойчив к выбросам и может выделять кластеры сложной формы и различных размеров.&lt;br /&gt;
&lt;br /&gt;
Описание шагов&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1&#039;&#039;&#039;. Если все данные использовать сразу как входные для CURE, то эффективность алгоритма будет низкая, а время выполнения большим. Поэтому на первом шаге мы случайным образом выбираем часть точек которые помещаются в память, затем группируем наиболее похожие с помощью иерархического метода.&lt;br /&gt;
Дальше работаем с кластерами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2&#039;&#039;&#039;. Для каждого кластера выбираем &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек представителей, максимально удаленных друг от друга. Чисто &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; остается постоянным.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3&#039;&#039;&#039;. Объединяем кластеры с наиболее похожими наборами точек представителей.&lt;br /&gt;
Если не достигнуто нужное число кластеров, то перейти на шаг 2.&lt;br /&gt;
Чтобы при объединении кластеров не выбирать каждый раз c точек представителей из всех точек, мы выбираем их только из &amp;lt;math&amp;gt;2c&amp;lt;/math&amp;gt; точек объединенных кластеров.&lt;br /&gt;
&lt;br /&gt;
Выбранные точки сдвигаются на следующем шаге на &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; к центроиду кластера. Алгоритм становится основанным на методе поиска центроида при &amp;lt;math&amp;gt;\alpha = 1&amp;lt;/math&amp;gt;, и основанным на всех точках кластера при &amp;lt;math&amp;gt;\alpha = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Exampleofalgorithmparametrs1.png|400px|thumb|center|Пример работы алгоритма для разных параметров &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; [1].]]&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Пусть дана матрица &amp;lt;math&amp;gt;x \in R^{m\times n}&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt;. Каждая строка матрицы представляет собой точку в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - мерном пространстве.&lt;br /&gt;
&lt;br /&gt;
m число точек для кластеризации, n размерность.&lt;br /&gt;
&lt;br /&gt;
Расстояние &amp;lt;math&amp;gt;\rho(a,b)&amp;lt;/math&amp;gt; между точками &amp;lt;math&amp;gt;a = (a_{1},\dots,a_{n})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b = (b_{1},\dots,b_{n})&amp;lt;/math&amp;gt; будем вычислять по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rho(a,b) = \sqrt{\sum_{i=1}^n (a_{i} - b_{i})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Инициализация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Выбираются &amp;lt;math&amp;gt;\lceil \gamma m\rceil&amp;lt;/math&amp;gt; строк матрицы случайным образом. Например, можно использовать [https://ru.wikipedia.org/wiki/Reservoir_sampling Алгоритм R]. &lt;br /&gt;
&lt;br /&gt;
В основной памяти выделяется куча, часть которой используется для размещения дерева, листьями которого являются выбранные строки, соединённые ребром с вершиной дерева.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
В самом начале шага считаем каждую выбранную точку одноточечным кластером с центроидой в этой же точке.&lt;br /&gt;
&lt;br /&gt;
Далее выполняем следующие шаги 1-2, пока количество кластеров не станет равно &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Шаг 1:&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го кластера вычисляется центроида &amp;lt;math&amp;gt;\mu_{i}&amp;lt;/math&amp;gt; по формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mu_{i} = \frac{1}{s_{i}}\sum_{j\in S_{i}} x^{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S_{i}&amp;lt;/math&amp;gt; - номера строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, входящие в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й кластер; &amp;lt;math&amp;gt;s_{i}&amp;lt;/math&amp;gt; - количество элементов в множестве &amp;lt;math&amp;gt;S_{i}&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;x^{j}&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-я строка матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Шаг 2:&lt;br /&gt;
&lt;br /&gt;
Выполняется поиск пары кластеров, &amp;quot;наиболее подходящих&amp;quot; для объединения. В качестве критерия выбора можно рассматривать: наименьшее расстояние между центроидами кластеров или наименьшее расстояние между элементами, входящими в рассматриваемую пару кластеров. Существуют также другие критерии выбора &amp;quot;подходящих&amp;quot; классов, например, сравнение плотности кластеров до объединения и после. Далее выполняется слияние найденных кластеров. Для дерева это означает добавление новой вершины, соединённой ребром с корнем; поддеревья кластеров становятся ветвями этой новой вершины.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге для каждого кластера определяются &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек-представителей. Для наилучшего отображения формы множества предполагается использование точек, максимально удалённых друг от друга.&lt;br /&gt;
&lt;br /&gt;
Для этого используем следующий алгоритм:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - текущее количество представителей, &amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt; - множество номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, входящие в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-й кластер, &amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt; - множество номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, назначенных представителями &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го кластера, а &amp;lt;math&amp;gt;x^{j}&amp;lt;/math&amp;gt; - вектор-строка матрицы &amp;lt;math&amp;gt;x&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;R_{k}&amp;lt;/math&amp;gt; = [] % Пустое множество&lt;br /&gt;
  a = Choose(&amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt;) % Выбор произвольного номера точки, входящей в кластер &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
  Add(&amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;,a) % Добавление точки а во множество представителей&lt;br /&gt;
  i = 1 % Текущее количество представителей кластера&lt;br /&gt;
  While (i&amp;lt;c) do % Хотим найти с представителей для кластера&lt;br /&gt;
  - rmax = 0&lt;br /&gt;
  - For ( (y &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt;) and (y &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;P_{k}&amp;lt;/math&amp;gt;) ) do % Поиск следующего представителя среди оставшихся точек кластера&lt;br /&gt;
  - - rmin = &amp;lt;math&amp;gt;\rho(x^{a}&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x^{y})&amp;lt;/math&amp;gt;&lt;br /&gt;
  - - For ( (j &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;) and (j &amp;lt;math&amp;gt;\neq&amp;lt;/math&amp;gt; a) ) do % Поиск расстояния от точки с номером y до ближайшего представителя &lt;br /&gt;
  - - - d = &amp;lt;math&amp;gt;\rho(x^{j}&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x^{y})&amp;lt;/math&amp;gt; % вычисление расстояния&lt;br /&gt;
  - - - If (d &amp;lt; rmin) then&lt;br /&gt;
  - - - - rmin = d&lt;br /&gt;
  - - - endif&lt;br /&gt;
  - - enddo&lt;br /&gt;
  - - If (rmin &amp;gt; rmax) % Поиск точки, для которой расстояние до ближайшего представителя является максимальным&lt;br /&gt;
  - - - rmax = rmin&lt;br /&gt;
  - - - b = y % искомая точка&lt;br /&gt;
  - - endif&lt;br /&gt;
  - enddo&lt;br /&gt;
  - Add(&amp;lt;math&amp;gt;R_{k}&amp;lt;/math&amp;gt;,b) % Добавление новой точки в множество представителей&lt;br /&gt;
  - i = i + 1 % текущее число представителей&lt;br /&gt;
  enddo&lt;br /&gt;
  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Для каждого представителя &amp;lt;math&amp;gt;x^{y}&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го кластера выполняется сдвиг к его центроиде &amp;lt;math&amp;gt;\mu_{k}&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; раз: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\hat{x}^{y} = \alpha\mu_{k} + (1-\alpha)x^{y}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Получаем множество &amp;lt;math&amp;gt;\hat{R}_{k}&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; точек, которые будут далее использоваться как представители этого кластера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге происходит присвоение неиспользованных точек (строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;) к кластерам. Для точки-строки &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычисляется расстояние до каждого представителя. Результат: строка &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; добавляется в поддерево кластера, которому принадлежит наиближайший представитель.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Результатом работы алгоритма является дерево, ветвями которого являются отдельные кластеры. Листьями кластеров являются строки из матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма представляет собой итерационное построение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров, вычисление их центроид и назначение точек-представителей.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации по выборке строк из точки-представителей сформировать &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; кластеров, их число задано заранее. Для каждой точки считается расстояние для всех остальных. Затем сравнивается расстояние между кластерами. За определение кластеров отвечают эти же точки. Похожие кластеры объединяются и их точки тоже. На каждом шаге алгоритма точки сдвигаются к центроиде. Алгоритм выполняется, пока не будет достигнуто необходимое число кластеров.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:SubCURE4.jpg|1100px|thumb|center|Схема работы алгоритма кластеризации CURE]]&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Входные данные для кластеризации - это &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; репрезентативных точек в d мерном пространстве и необходимое число &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; кластеров. Для каждой точки необходимо вычислить расстояние до другой. Сложность этого этапа &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;. Если искать ближайший кластер простым перебором, то сложность поиска будет &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для каждого кластера. Но для эффективного поиска сложность составляет &amp;lt;math&amp;gt;log_2{n}&amp;lt;/math&amp;gt;. Эффективный метод описан в статье.&amp;lt;ref&amp;gt;Sudipto Guha (Stanford), Rajeev Rastogi (Bell Labs), Kyuseok Shim (Korea Institute of Technology). CURE: an efficient clustering algorithm for large databases. SIGMOD &#039;98 Proceedings of the 1998 ACM SIGMOD international conference on Management of data, pp 73-84 &amp;lt;/ref&amp;gt; Далее каждую точку необходимо вдвинуть к центроиде, сложность этой операции &amp;lt;math&amp;gt;O(2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Таким образом худшее время выполнения алгоритма &amp;lt;math&amp;gt;O(n^2 log_2{n})&amp;lt;/math&amp;gt;. Для двумерного пространства временная сложность может быть &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;. Так что сложность алгоритма не хуже чем у центроидного. То есть при такой размерности входных данных CURE выполняется за полиномиальное время.&amp;lt;math&amp;gt;~^{[1]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Инициализация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
В зависимости от алгоритма случайная выборка номеров строк матрицы &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; может производится либо зависимо, либо независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Описанный в разделе иерархический алгоритм кластеризации является последовательным, так как на каждом шаге сравниваются расстояния между центроидами кластеров, некоторые из которых меняются при объединении кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Файл:Points c.png|800px|thumb|center|Последовательный информационный граф для точек-представителей]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Данный шаг выполняется независимо для каждой точки-представителя каждого кластера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Вычисление расстояний до точек представителей можно выполнять независимо между собой.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Основные шаги алгоритма информационно зависят от предыдущих шагов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:InfoCURE.jpg|1100px|thumb|center|Общий информационных граф алгоритма]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Кластеризация&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Шаг 1 (вычисление центроид):&lt;br /&gt;
&lt;br /&gt;
Вычисления центроид для кластеров выполняется независимо, возможно их распараллеливание.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Шаг 2 (поиск &amp;quot;наилучших кластеров для объединения):&lt;br /&gt;
&lt;br /&gt;
На данном этапе вычисляются расстояния между кластерами, что можно сделать независимо друг от друга. Если для определения расстояния между кластерами использовать минимальное расстояние между парами элементов (по одному элементу из каждого кластера), то эти расстояния вычисляются опять же независимо друг от друга, и их можно &amp;quot;распараллелить&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Назначение представителей&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Представители внутри каждого кластера определяются независимо от других кластеров и поддаются распараллеливанию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Сдвиг представителей к центроиде&amp;quot;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Изменение положения представителей можно сделать независимо как от представителей данного кластера, так и представителей других кластеров. Это также хорошо поддаётся распараллеливанию.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг &amp;quot;Присваивание&amp;quot;&#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 \in R^{m\times n}&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt;, число точек представителей &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt;, параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;m&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;\alpha&amp;lt;/math&amp;gt; для сжатия точек представления к центру кластера. В оригинальной статье авторы показывают что &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;0.2&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;0.7&amp;lt;/math&amp;gt; помогает хорошо выявлять несферические кластеры и подавлять редкие выбросы.&lt;br /&gt;
&lt;br /&gt;
Параметр &amp;lt;math&amp;gt;\gamma&amp;lt;/math&amp;gt; задаёт какая часть от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; точек будет использована при случайной выборке точек на шаге &amp;quot;Инициализация&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Число точек представителей выбирается в зависимости от данных. Если возможно появление кластеров сложной формы, то точек должно быть больше, чтобы выявить их форму. В оригинальной статье авторы показывают что уже при &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; равном &amp;lt;math&amp;gt;10&amp;lt;/math&amp;gt; достигается неплохая эффективность.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Exampleofalgorithmparameters2.png|400px|thumb|center|Пример работы алгоритма для разных параметров &amp;lt;math&amp;gt;c&amp;lt;/math&amp;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;
&lt;br /&gt;
Алгоритм CURE выполняет иерархическую кластеризацию с использованием набора определяющих точек. Он действует только в евклидовом пространстве и предназначен для кластеризации очень больших наборов числовых данных.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Преимущества&#039;&#039;&#039;: выполняет кластеризацию на высоком уровне даже при наличии выбросов, выделяет кластеры сложной формы и различных размеров, обладает линейно зависимыми требованиями к месту хранения данных и квадратичную временную сложность для данных высокой размерности.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Недостатки&#039;&#039;&#039;: есть необходимость в задании пороговых значений и количества кластеров, работает только на числовых данных, эффективен только для данных низкой размерности&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;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
Данный раздел будет заполнен к 15 ноября.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Последовательная реализация CURE-алгоритма для кластеризации для Python/C++ представлена в пакете: [https://github.com/annoviko/pyclustering pyclustering].&lt;br /&gt;
&lt;br /&gt;
Также существует параллельная реализация алгоритма, описанная в сборнике &amp;lt;ref&amp;gt;Panagiotis E. Hadjidoukas, Laurent Amsaleg. OpenMP Shared Memory Parallel Programming.&lt;br /&gt;
Volume 4315 of the series Lecture Notes in Computer Science pp 289-299&lt;br /&gt;
Parallelization of a Hierarchical Data Clustering Algorithm Using OpenMP&amp;lt;/ref&amp;gt;. Алгоритм предложенный в статье использует массив данных состоящий из размеров кластера, его центроида и репрезентативных точек, а при объединении кластеров информацию о новом кластере просто записывают в строчку предыдущего. Также для каждого кластера требуется считать индекс и расстояние до ближайшего кластера. Именно их поиск авторы статьи и предлагают распараллелить.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Vagabond&amp;diff=16391</id>
		<title>Обсуждение участника:Vagabond</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:Vagabond&amp;diff=16391"/>
		<updated>2016-11-11T11:08:12Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Алгоритм быстрого дискретного преобразования Фурье&amp;quot; */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Замечания к статье ===&lt;br /&gt;
&lt;br /&gt;
* содержание не соответствуют шаблону описания алгоритма: неверная нумерация, отсутствуют п.п.--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:30, 25 октября 2016 (MSK)   &lt;br /&gt;
* не указан конкретный вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:30, 25 октября 2016 (MSK)&lt;br /&gt;
* п.п. 1.9 не раскрыт--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:30, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Алгоритм быстрого дискретного преобразования Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* Раздел 1.1 - вместо общего описания алгоритма написано именно что математическое описание. В общем описании должна быть поставлена задача и изложена основная идея, с минимумом формул [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:08, 11 ноября 2016 (MSK)&lt;br /&gt;
* Раздел 1.2 - не очень ясны слова про &amp;quot;проиллюстрированные на рисунке 2 в разделе Информационный граф&amp;quot;. Раздел &amp;quot;Информационный граф&amp;quot; содержит рисунок 1. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:08, 11 ноября 2016 (MSK)&lt;br /&gt;
* Если упоминаем рекурсию, нужны слова о глубине рекурсии [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 14:08, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=16390</id>
		<title>Участник:Vagabond/Алгоритм быстрого дискретного пребразования Фурье</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=16390"/>
		<updated>2016-11-11T11:07:28Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Данилишин А.Р.&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Главная идея БПФ состоит в сокращении многочисленных повторов сложения и умножения, которые выполняются в ДПФ (повторяющиеся множители: &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; Например при условии, что  &amp;lt;math&amp;gt;(pk)mod(n)=const&amp;lt;/math&amp;gt;, при всех &amp;lt;math&amp;gt;k=\overline{0,n-1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p=\overline{0,n-1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Напомним формулу ДПФ: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2kpi\pi}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
За счет использования данного факта, происходит значительное сокращение количества вычислительных операций. В данной статье используется алгоритм Cooley – Tukey, который предполагает собой разбиение исходного множества входных сигналов на два равных подмножества. При этом множество входных сигналов должно быть степенью двойки. Далее операция деления множества продолжается для образовавшихся подмножеств, и так далее. В результате над каждым из полученных подмножеств выполняется БПФ. Данные вычисления можно выполнять независимо друг от друга. Этот факт используется для реализации параллельной версии алгоритма. В общем алгоритм Cooley – Tukey строится из двух основных шагов:&lt;br /&gt;
&lt;br /&gt;
* Преобразование входного массива данных (бит-реверсирование).&lt;br /&gt;
* Вычисление БПФ.&lt;br /&gt;
Приведем более детальное описание БПФ (математическое описание):&lt;br /&gt;
*Пусть &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;\omega _{n}^{kp}&amp;lt;/math&amp;gt;.&lt;br /&gt;
*Разобьем исходный сигнал &amp;lt;math&amp;gt;x(n)&amp;lt;/math&amp;gt; на два подмножества, с четными &amp;lt;math&amp;gt;x_{1}(2n)&amp;lt;/math&amp;gt; и нечетными &amp;lt;math&amp;gt;x_{2}(2n+1)&amp;lt;/math&amp;gt; номерами.&lt;br /&gt;
Тогда получаем: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}\omega _{n}^{kp}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{(2k+1)p}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выпишем два свойства омеги:&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{\frac{n}{2}}=e^{2(\frac{-2i\pi}{n})}=\omega_{n}^{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{n}^{\frac{n}{2}}=e^{\frac{-2ni\pi}{2n}}=e^{-i\pi}=-1&amp;lt;/math&amp;gt;&lt;br /&gt;
Воспользуемся первым: &amp;lt;math&amp;gt;y_{p}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{2kp}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;. Или, если обозначить &amp;lt;math&amp;gt;y_{1}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;y_{2}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, то получим формулу для вычисления координат изображения БПФ при &amp;lt;math&amp;gt;p=\overline{0...n/2}&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;y_(p)=y_{1}(p)+\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;math&amp;gt;y(p); y_{1}(p); y_{2}(p)&amp;lt;/math&amp;gt; обладают свойством периодичности, с периодом &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;. Покажем это для &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;y_{1}(p+\frac{n}{2})=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{k(p+\frac{n}{2})}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}\omega_{\frac{n}{2}}^{\frac{kn}{2}}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как: &amp;lt;math&amp;gt;\omega_{\frac{n}{2}}^{\frac{kn}{2}}=e^{\frac{-4kni\pi}{2n}}=e^{-2ki\pi}=1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее необходимо определить &amp;lt;math&amp;gt;y(p)&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;p\geq \frac{n}{2}&amp;lt;/math&amp;gt;. Теперь воспользуемся свойством периодичности обнаруженными нами ранее: &amp;lt;math&amp;gt;y(p+\frac{n}{2})=y_{1}(p+\frac{n}{2})+\omega_{n}^{(p+\frac{n}{2})}y_{2}(p+\frac{n}{2})&amp;lt;/math&amp;gt;. Воспользуемся вторым свойством омеги: &amp;lt;math&amp;gt;y(p)=y_{1}(p)-\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В конечном итоге мы пришли к конечной формуле для первого этапа вычисления БПФ. Дальнейшие этапы делят подмножества полученные на предыдущим этапе вычисления БПФ.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Бит-реверсирование&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Бит-реверсирование – это преобразование двоичного числа путем изменения порядка следования бит в нем на противоположный. Бит-реверсирование применимо только к индексам элементов массива и предназначено для изменения порядка следования этих элементов, при этом значения самих элементов не изменяются. Так, к примеру, четырехмерный вектор &amp;lt;math&amp;gt;(x_{0},x_{1},x_{2},x_{3})&amp;lt;/math&amp;gt; изменится на &amp;lt;math&amp;gt;(x_{0},x_{2},x_{1},x_{3})&amp;lt;/math&amp;gt;. Данная процедура необходима для разделения исходного сигнала на элементы подмножеств с четными и нечетными номерами, которые необходимы для вычисления &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычисление БПФ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
После выполнения этапа бит-реверсирования выполняются вычисления проиллюстрированные &#039;&#039;на рисунке 2&#039;&#039; в разделе [[#Информационный граф|Информационный граф]]. Рисунок 2 приведен для случая четырехмерного входного сигнала. Он предполагает совершение двух этапов. В общем же количество этапов равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Каждый этап содержит в себе вычисление одной и той же операции &amp;quot;бабочки&amp;quot;, где на вход подаются два числа &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, на выходе же получаются &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;- это &#039;&#039;коэффициент поворота&#039;&#039;. Общая формула для коэффициента поворота: &amp;lt;math&amp;gt;e^{\frac{-ji\pi}{k}}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- размер шага &amp;quot;бабочки&amp;quot;, &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;- номер итерации вычислений над группой элементов, размером &amp;lt;math&amp;gt;2k&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0\leq j &amp;lt; k )&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из двух вышеописанных этапов БПФ, бит-реверсирование является самым быстровыполнимым. Поэтому вычислительное ядро алгоритма будет лежать именно во втором этапе. А именно, им будет являться вычисление &amp;quot;бабочек&amp;quot;. Количество этапов выполнения БПФ, как описывалось выше, равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Учитывая количество этапов, размерность входного сигнала и идею деления на подмножества, всего &amp;quot;бабочек&amp;quot; получается &amp;lt;math&amp;gt;\frac{nlog_{2}n}{2}&amp;lt;/math&amp;gt;. При этом операция умножения выполняется &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; раз. Это связано с тем, что результаты вычисления &amp;quot;бабочки&amp;quot; &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из всего вышесказанного заключаем, что макроструктуру алгоритма легче всего описать при помощи рекурсии: &lt;br /&gt;
*&amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; преобразований Фурье порядка 2&lt;br /&gt;
*умножение &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; пар комплексных чисел&lt;br /&gt;
*&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;
Программный код (описан на языке С++) строится из ряда функций:&lt;br /&gt;
*бит-реверсирование&lt;br /&gt;
*инициализация размерности входного сигнала &lt;br /&gt;
*инициализация входного сигнала (при помощи генератора случайных чисел)&lt;br /&gt;
*процесс очистки памяти&lt;br /&gt;
*выполнение операции &amp;quot;бабочка&amp;quot;&lt;br /&gt;
*выполнение БПФ&lt;br /&gt;
*функция вывода результата (для тестирования)&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;главный программный код БПФ:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; *inputSignal = NULL;   &lt;br /&gt;
	complex&amp;lt;double&amp;gt; *outputSignal = NULL; &lt;br /&gt;
	int size = 0;&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;FFT. Serial version.&amp;quot; &amp;lt;&amp;lt; endl;       &lt;br /&gt;
	ProcessInitialization(inputSignal, outputSignal, size);&lt;br /&gt;
	bit(inputSignal, outputSignal, size);&lt;br /&gt;
	SerialFFTCalculation(outputSignal, size);&lt;br /&gt;
	PrintSignal(outputSignal, size); //в случае тестирования&lt;br /&gt;
	ProcessTermination(inputSignal, outputSignal);&lt;br /&gt;
	cin.get();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== функция бит-реверсирования ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void bit(complex&amp;lt;double&amp;gt; inputSignal[], complex&amp;lt;double&amp;gt; *outputSignal, int size)&lt;br /&gt;
{ // процесс бит-реверсирования&lt;br /&gt;
	int j = 0, i = 0;&lt;br /&gt;
	while (i &amp;lt; size) &lt;br /&gt;
	{&lt;br /&gt;
		if (j &amp;gt; i) &lt;br /&gt;
		{ &lt;br /&gt;
			outputSignal[i] = inputSignal[j];       &lt;br /&gt;
			outputSignal[j] = inputSignal[i]; &lt;br /&gt;
		}&lt;br /&gt;
		else       &lt;br /&gt;
			if (j == i)          &lt;br /&gt;
				outputSignal[i] = inputSignal[i];&lt;br /&gt;
		&lt;br /&gt;
		int m = size &amp;gt;&amp;gt; 1;     &lt;br /&gt;
		while ((m &amp;gt;= 1) &amp;amp;&amp;amp; (j &amp;gt;= m)) &lt;br /&gt;
		{ &lt;br /&gt;
			j -= m;       &lt;br /&gt;
			m = m &amp;gt;&amp;gt; 1; &lt;br /&gt;
		}     &lt;br /&gt;
		j += m;     &lt;br /&gt;
		i++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация размерности входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessInitialization(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal, int &amp;amp;size) &lt;br /&gt;
{&lt;br /&gt;
	// инициализация размера входного сигнала  &lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		cout &amp;lt;&amp;lt; &amp;quot;Enter the input signal length: &amp;quot;;&lt;br /&gt;
		cin &amp;gt;&amp;gt; size;&lt;br /&gt;
		if (size &amp;lt; 4)&lt;br /&gt;
			cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be &amp;gt;= 4&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			int tmpSize = size;&lt;br /&gt;
			while (tmpSize != 1)&lt;br /&gt;
			{&lt;br /&gt;
				if (tmpSize % 2 != 0)&lt;br /&gt;
				{&lt;br /&gt;
					cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be powers of two&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
					size = -1;&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				tmpSize /= 2;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while (size &amp;lt; 4);  &lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Input signal length = &amp;quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl; &lt;br /&gt;
	inputSignal = new complex&amp;lt;double&amp;gt;[size];   &lt;br /&gt;
	outputSignal = new complex&amp;lt;double&amp;gt;[size];&lt;br /&gt;
	DummyDataInitialization(inputSignal, size);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void DummyDataInitialization(complex&amp;lt;double&amp;gt;* mas, int size)&lt;br /&gt;
{	&lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)&lt;br /&gt;
	{&lt;br /&gt;
		mas[i] = int(rand() % 100);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== процесс очистки памяти ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessTermination(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal) &lt;br /&gt;
{ &lt;br /&gt;
	//очистка памяти&lt;br /&gt;
	delete[] inputSignal;   &lt;br /&gt;
	inputSignal = NULL;   &lt;br /&gt;
	delete[] outputSignal;   &lt;br /&gt;
	outputSignal = NULL; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение операции &amp;quot;бабочка&amp;quot; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void Butterfly(complex&amp;lt;double&amp;gt; *signal, complex&amp;lt;double&amp;gt; &amp;amp;u,int offset, int butterflySize)&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; tem = signal[offset + butterflySize] * u;&lt;br /&gt;
	signal[offset + butterflySize] = signal[offset] - tem;   &lt;br /&gt;
	signal[offset] += tem;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение БПФ ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void SerialFFTCalculation(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	int m = 0;   &lt;br /&gt;
	for (int tmp_size = size; tmp_size &amp;gt; 1; tmp_size /= 2, m++);&lt;br /&gt;
	for (int p = 0; p &amp;lt; m; p++)&lt;br /&gt;
	{&lt;br /&gt;
		int butterflyOffset = 1 &amp;lt;&amp;lt; (p + 1);&lt;br /&gt;
		int butterflySize = butterflyOffset &amp;gt;&amp;gt; 1;&lt;br /&gt;
		double coeff = M_PI / butterflySize;&lt;br /&gt;
&lt;br /&gt;
		for (int i = 0; i &amp;lt; size / butterflyOffset; i++)&lt;br /&gt;
			for (int j = 0; j &amp;lt; butterflySize; j++)&lt;br /&gt;
				Butterfly(signal,complex&amp;lt;double&amp;gt;(cos(-j*coeff),sin(-j*coeff)),j+i*butterflyOffset,butterflySize);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== функция вывода результата (для тестирования) ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void PrintSignal(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Result signal&amp;quot; &amp;lt;&amp;lt; endl;   &lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)     &lt;br /&gt;
		cout &amp;lt;&amp;lt; signal[i] &amp;lt;&amp;lt; endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Рассмотренный алгоритм  Cooley – Tukey выполняется за &amp;lt;math&amp;gt;nlog_{2}n&amp;lt;/math&amp;gt; операций комплексного сложения и &amp;lt;math&amp;gt;\frac{n}{2}log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения. Таким образом последовательная сложность алгоритма является &#039;&#039;&#039;линейно-логарифмической&#039;&#039;&#039;.&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[file:Граф.jpg|center|thumb|800px|Рисунок 1. Алгоритм Cooley – Tukey для n=4, при предварительно сделанном бит-реверсировании]]&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
[[file:Парал.jpg|center|thumb|800px|Рисунок 2. Распараллеливание алгоритма Cooley – Tukey для n=8]]&lt;br /&gt;
Рисунок 2 получен из информационного графа, главной идеей которого является процедура деления множеств полученных на предыдущем шаге на два равных по мощности подмножества и применение к ним БПФ. То есть процесс вычисления распределяется между &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; процессорами. И так как при последовательной реализации алгоритма было &amp;lt;math&amp;gt;\frac{n}{2}\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;n\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. Получается, что при параллельной реализации алгоритма мы будем иметь &amp;lt;math&amp;gt;\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;2\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. При этом, если используется функция HT(Hyper threading), то параллельная сложность быстрого преобразования Фурье (базовый алгоритм Кули-Тьюки) для векторов с длиной, равной степени двойки составляет &amp;lt;math&amp;gt;\log_{2}n&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;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{R}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
*&#039;&#039;&#039;Комплексное ДФП&#039;&#039;&#039;&lt;br /&gt;
**входные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
*Соотношение последовательной и параллельной сложности является &#039;&#039;линейным&#039;&#039;. Это хорошо видно из пункта [[#Ресурс параллелизма алгоритма|ресурс параллелизма алгоритма]].&lt;br /&gt;
*Вычислительная мощность алгоритма является &#039;&#039;логарифмической&#039;&#039;.&lt;br /&gt;
*Устойчивость. &lt;br /&gt;
**Рассмотрим вопрос об устойчивости поближе. Так как БПФ отличается от ДПФ простым лишь сокращением одинаковых операций, то вопрос устойчивости можно рассматривать на примере ДПФ. Так, к примеру, возьмем формулу ДПФ и добавим возмущение: &amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}(x_{k}+\varepsilon )e^{\frac{-2*\pi*k*p*i}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;. После чего преобразуем ее: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2*\pi*k*p*i}{n}}+\varepsilon\sum_{k=0}^{n-1} e^{\frac{-2*\pi*k*p*i}{n}}&amp;lt;/math&amp;gt; и оценим вторую сумму, которая является геометрической прогрессией со знаменателем &amp;lt;math&amp;gt;q=e^{\frac{-2*\pi*p*i}{n}}&amp;lt;/math&amp;gt;. Таким образом оцениваемая сумма будет равняться: &amp;lt;math&amp;gt;\varepsilon\frac{1-e^{\frac{-2*\pi*p*i*(n-1))}{n}}}{1-e^{\frac{-2*\pi*p*i}{n}}}&amp;lt;/math&amp;gt;. При достаточно больших размерностях входного сигнала данное выражение сводится к: &amp;lt;math&amp;gt;\varepsilon (1-e^{-2*\pi*p*i})&amp;lt;/math&amp;gt;. В тригонометрической форме данного выражения: &amp;lt;math&amp;gt;\varepsilon (cos(\frac{2*\pi*p*(n-1)}{n})-i*sin(\frac{2*\pi*p*(n-1)}{n}))&amp;lt;/math&amp;gt; видно, что принимая во внимание целочисленность индексов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; получается 0. Таким образом, мы можем говорить об устойчивости алгоритма в целом, и абсолютной устойчивости алгоритма в асимптотике.&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;
[[file:масш.jpg|center|thumb|1000px|Рисунок 3. Параллельная реализация алгоритма. Изменение эффективности(%)в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
Как описывалось выше, каждый процессор получает достаточно низкий объем работы, что приводит к неэффективному распараллеливанию алгоритма (высокие затраты на передачу данных между процессорами по сравнению с вычислениями на них). Поэтому необходимо находить оптимальное количество процессоров для каждой отдельно взятой задачи. В данном примере, при размерности входного сигнала менее 262144, оптимальное количество процессоров составляет 1. При размерности 262144-4194304 оптимум достигался при 8 процессорах. И при размерности более 4194304- 16 процессоров. Вычисления проводились на суперкомпьютере «Ломоносов». &lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Самой распространенной и известной реализацией дискретного БПФ является библиотека [http://www.fftw.org/ FFTW], разработанная Маттео Фриго и Стивеном Г. Джонсоном в Массачусетском технологическом институте. Данная библиотека существует для языков C и FORTRAN и позволяет производить вычисления в одном или нескольких измерениях. Входной сигнал может быть как действительным так и комплексным, а его размерность не обязательно должна быть степенью двойки. На данный момент последняя версия FFTW 3.3.5. Другие особенности библиотеки:&lt;br /&gt;
*Скорость. (Поддержка SSE / SSE2 / Altivec, начиная с версии 3.0. Версия 3.3.1 поддерживает AVX и ARM Neon.)&lt;br /&gt;
*Параллельные вычисления (OpenMP, MPI).&lt;br /&gt;
*[http://www.fftw.org/fftw3_doc/ Документация] в форматах HTML, PDF.&lt;br /&gt;
*Данное программное обеспечение является свободным и распространяется по лицензии GNU (General Public License).&lt;br /&gt;
Здесь приводится пример реализации прямого БПФ для комплексных данных входного сигнала на языке C++. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fftw3-mpi.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
# include &amp;lt;time.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	const ptrdiff_t N0 = 4194304;&lt;br /&gt;
	fftw_plan planForw;&lt;br /&gt;
	fftw_complex *data, *dataOut;&lt;br /&gt;
	ptrdiff_t alloc_local, local_ni, local_i_start, i, j, local_no, local_o_start;&lt;br /&gt;
	int index, size;&lt;br /&gt;
	double startwtime, endwtime;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	fftw_mpi_init();&lt;br /&gt;
	MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;index);&lt;br /&gt;
	MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;size);&lt;br /&gt;
&lt;br /&gt;
	/* get local data size and allocate */&lt;br /&gt;
	alloc_local = fftw_mpi_local_size_1d(N0, MPI_COMM_WORLD, FFTW_FORWARD, FFTW_ESTIMATE,&lt;br /&gt;
		&amp;amp;local_ni, &amp;amp;local_i_start, &amp;amp;local_no, &amp;amp;local_o_start);&lt;br /&gt;
	data = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	dataOut = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	/* create plan  */&lt;br /&gt;
	planForw = fftw_mpi_plan_dft_1d(N0, data, dataOut, MPI_COMM_WORLD,&lt;br /&gt;
		FFTW_FORWARD, FFTW_ESTIMATE);&lt;br /&gt;
	/* initialize data to some function my_function(x,y) */&lt;br /&gt;
	for (i = 0; i &amp;lt; local_ni; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		data[i][0] = rand() / (double)RAND_MAX;&lt;br /&gt;
		data[i][1] = rand() / (double)RAND_MAX;&lt;br /&gt;
	}&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		startwtime = MPI_Wtime();&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_execute(planForw);&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		endwtime = MPI_Wtime();&lt;br /&gt;
		printf(&amp;quot;wall clock time = %f\n&amp;quot;,&lt;br /&gt;
			endwtime - startwtime);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_destroy_plan(planForw);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[http://www.fftw.org/download.html Скачав] библиотеку и установив ее на рабочий компьютер, можно поэкспериментировать, используя вышеописанный исходный код. При этом, на стадии компиляции необходимо указывать директорию, где находятся заголовочные файлы и директорию, где находятся библиотечные файлы. Пример: mpicxx name.cpp -I/path/to/include/files -L/path/to/lib/files  -lfftw3_mpi -lfftw3 -lm. Для версии 2.1.4 описание на русском языке приводится [https://parallel.ru/cluster/fftw.html здесь].&lt;br /&gt;
&lt;br /&gt;
Остальные реализации:&lt;br /&gt;
*[http://www.gnu.org/software/gsl/ GSL - GNU Scientific Library]&lt;br /&gt;
*[https://developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
*[http://www-03.ibm.com/systems/power/software/essl/ Engineering and Scientific Subroutine Library (ESSL) and Parallel ESSL]&lt;br /&gt;
=Литература=&lt;br /&gt;
[1] Бахвалов Н. С., Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний &lt;br /&gt;
&lt;br /&gt;
[2] Г. Нуссбаумер. Быстрое преобразование Фурье и алгоритмы вычисления сверток &lt;br /&gt;
&lt;br /&gt;
[3] А.О. Лапаев Параллельное вычисление дискретного преобразования Фурье полинома в простом поле&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=16389</id>
		<title>Участник:Vagabond/Алгоритм быстрого дискретного пребразования Фурье</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=16389"/>
		<updated>2016-11-11T11:07:00Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Данилишин А.Р.&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Главная идея БПФ состоит в сокращении многочисленных повторов сложения и умножения, которые выполняются в ДПФ (повторяющиеся множители: &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; Например при условии, что  &amp;lt;math&amp;gt;(pk)mod(n)=const&amp;lt;/math&amp;gt;, при всех &amp;lt;math&amp;gt;k=\overline{0,n-1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p=\overline{0,n-1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Напомним формулу ДПФ: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2kpi\pi}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
За счет использования данного факта, происходит значительное сокращение количества вычислительных операций. В данной статье используется алгоритм Cooley – Tukey, который предполагает собой разбиение исходного множества входных сигналов на два равных подмножества. При этом множество входных сигналов должно быть степенью двойки. Далее операция деления множества продолжается для образовавшихся подмножеств, и так далее. В результате над каждым из полученных подмножеств выполняется БПФ. Данные вычисления можно выполнять независимо друг от друга. Этот факт используется для реализации параллельной версии алгоритма. В общем алгоритм Cooley – Tukey строится из двух основных шагов:&lt;br /&gt;
&lt;br /&gt;
* Преобразование входного массива данных (бит-реверсирование).&lt;br /&gt;
* Вычисление БПФ.&lt;br /&gt;
Приведем более детальное описание БПФ (математическое описание):&lt;br /&gt;
*Пусть &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;\omega _{n}^{kp}&amp;lt;/math&amp;gt;.&lt;br /&gt;
*Разобьем исходный сигнал &amp;lt;math&amp;gt;x(n)&amp;lt;/math&amp;gt; на два подмножества, с четными &amp;lt;math&amp;gt;x_{1}(2n)&amp;lt;/math&amp;gt; и нечетными &amp;lt;math&amp;gt;x_{2}(2n+1)&amp;lt;/math&amp;gt; номерами.&lt;br /&gt;
Тогда получаем: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}\omega _{n}^{kp}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{(2k+1)p}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выпишем два свойства омеги:&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{\frac{n}{2}}=e^{2(\frac{-2i\pi}{n})}=\omega_{n}^{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{n}^{\frac{n}{2}}=e^{\frac{-2ni\pi}{2n}}=e^{-i\pi}=-1&amp;lt;/math&amp;gt;&lt;br /&gt;
Воспользуемся первым: &amp;lt;math&amp;gt;y_{p}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{2kp}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;. Или, если обозначить &amp;lt;math&amp;gt;y_{1}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;y_{2}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, то получим формулу для вычисления координат изображения БПФ при &amp;lt;math&amp;gt;p=\overline{0...n/2}&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;y_(p)=y_{1}(p)+\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;math&amp;gt;y(p); y_{1}(p); y_{2}(p)&amp;lt;/math&amp;gt; обладают свойством периодичности, с периодом &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;. Покажем это для &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;y_{1}(p+\frac{n}{2})=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{k(p+\frac{n}{2})}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}\omega_{\frac{n}{2}}^{\frac{kn}{2}}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как: &amp;lt;math&amp;gt;\omega_{\frac{n}{2}}^{\frac{kn}{2}}=e^{\frac{-4kni\pi}{2n}}=e^{-2ki\pi}=1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее необходимо определить &amp;lt;math&amp;gt;y(p)&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;p\geq \frac{n}{2}&amp;lt;/math&amp;gt;. Теперь воспользуемся свойством периодичности обнаруженными нами ранее: &amp;lt;math&amp;gt;y(p+\frac{n}{2})=y_{1}(p+\frac{n}{2})+\omega_{n}^{(p+\frac{n}{2})}y_{2}(p+\frac{n}{2})&amp;lt;/math&amp;gt;. Воспользуемся вторым свойством омеги: &amp;lt;math&amp;gt;y(p)=y_{1}(p)-\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В конечном итоге мы пришли к конечной формуле для первого этапа вычисления БПФ. Дальнейшие этапы делят подмножества полученные на предыдущим этапе вычисления БПФ.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Бит-реверсирование&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Бит-реверсирование – это преобразование двоичного числа путем изменения порядка следования бит в нем на противоположный. Бит-реверсирование применимо только к индексам элементов массива и предназначено для изменения порядка следования этих элементов, при этом значения самих элементов не изменяются. Так, к примеру, четырехмерный вектор &amp;lt;math&amp;gt;(x_{0},x_{1},x_{2},x_{3})&amp;lt;/math&amp;gt; изменится на &amp;lt;math&amp;gt;(x_{0},x_{2},x_{1},x_{3})&amp;lt;/math&amp;gt;. Данная процедура необходима для разделения исходного сигнала на элементы подмножеств с четными и нечетными номерами, которые необходимы для вычисления &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычисление БПФ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
После выполнения этапа бит-реверсирования выполняются вычисления проиллюстрированные &#039;&#039;на рисунке 2&#039;&#039; в разделе [[#Информационный граф|Информационный граф]]. Рисунок 2 приведен для случая четырехмерного входного сигнала. Он предполагает совершение двух этапов. В общем же количество этапов равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Каждый этап содержит в себе вычисление одной и той же операции &amp;quot;бабочки&amp;quot;, где на вход подаются два числа &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, на выходе же получаются &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;- это &#039;&#039;коэффициент поворота&#039;&#039;. Общая формула для коэффициента поворота: &amp;lt;math&amp;gt;e^{\frac{-ji\pi}{k}}&amp;lt;/math&amp;gt;. Где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- размер шага &amp;quot;бабочки&amp;quot;, &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;- номер итерации вычислений над группой элементов, размером &amp;lt;math&amp;gt;2k&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0\leq j &amp;lt; k )&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из двух вышеописанных этапов БПФ, бит-реверсирование является самым быстровыполнимым. Поэтому вычислительное ядро алгоритма будет лежать именно во втором этапе. А именно, им будет являться вычисление &amp;quot;бабочек&amp;quot;. Количество этапов выполнения БПФ, как описывалось выше, равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Учитывая количество этапов, размерность входного сигнала и идею деления на подмножества, всего &amp;quot;бабочек&amp;quot; получается &amp;lt;math&amp;gt;\frac{nlog_{2}n}{2}&amp;lt;/math&amp;gt;. При этом операция умножения выполняется &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; раз. Это связано с тем, что результаты вычисления &amp;quot;бабочки&amp;quot; &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из всего вышесказанного заключаем, что макроструктуру алгоритма легче всего описать при помощи рекурсии: &lt;br /&gt;
*&amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; преобразований Фурье порядка 2&lt;br /&gt;
*умножение &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; пар комплексных чисел&lt;br /&gt;
*&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;
Программный код (описан на языке С++) строится из ряда функций:&lt;br /&gt;
*бит-реверсирование&lt;br /&gt;
*инициализация размерности входного сигнала &lt;br /&gt;
*инициализация входного сигнала (при помощи генератора случайных чисел)&lt;br /&gt;
*процесс очистки памяти&lt;br /&gt;
*выполнение операции &amp;quot;бабочка&amp;quot;&lt;br /&gt;
*выполнение БПФ&lt;br /&gt;
*функция вывода результата (для тестирования)&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;главный программный код БПФ:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; *inputSignal = NULL;   &lt;br /&gt;
	complex&amp;lt;double&amp;gt; *outputSignal = NULL; &lt;br /&gt;
	int size = 0;&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;FFT. Serial version.&amp;quot; &amp;lt;&amp;lt; endl;       &lt;br /&gt;
	ProcessInitialization(inputSignal, outputSignal, size);&lt;br /&gt;
	bit(inputSignal, outputSignal, size);&lt;br /&gt;
	SerialFFTCalculation(outputSignal, size);&lt;br /&gt;
	PrintSignal(outputSignal, size); //в случае тестирования&lt;br /&gt;
	ProcessTermination(inputSignal, outputSignal);&lt;br /&gt;
	cin.get();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== функция бит-реверсирования ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void bit(complex&amp;lt;double&amp;gt; inputSignal[], complex&amp;lt;double&amp;gt; *outputSignal, int size)&lt;br /&gt;
{ // процесс бит-реверсирования&lt;br /&gt;
	int j = 0, i = 0;&lt;br /&gt;
	while (i &amp;lt; size) &lt;br /&gt;
	{&lt;br /&gt;
		if (j &amp;gt; i) &lt;br /&gt;
		{ &lt;br /&gt;
			outputSignal[i] = inputSignal[j];       &lt;br /&gt;
			outputSignal[j] = inputSignal[i]; &lt;br /&gt;
		}&lt;br /&gt;
		else       &lt;br /&gt;
			if (j == i)          &lt;br /&gt;
				outputSignal[i] = inputSignal[i];&lt;br /&gt;
		&lt;br /&gt;
		int m = size &amp;gt;&amp;gt; 1;     &lt;br /&gt;
		while ((m &amp;gt;= 1) &amp;amp;&amp;amp; (j &amp;gt;= m)) &lt;br /&gt;
		{ &lt;br /&gt;
			j -= m;       &lt;br /&gt;
			m = m &amp;gt;&amp;gt; 1; &lt;br /&gt;
		}     &lt;br /&gt;
		j += m;     &lt;br /&gt;
		i++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация размерности входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessInitialization(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal, int &amp;amp;size) &lt;br /&gt;
{&lt;br /&gt;
	// инициализация размера входного сигнала  &lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		cout &amp;lt;&amp;lt; &amp;quot;Enter the input signal length: &amp;quot;;&lt;br /&gt;
		cin &amp;gt;&amp;gt; size;&lt;br /&gt;
		if (size &amp;lt; 4)&lt;br /&gt;
			cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be &amp;gt;= 4&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			int tmpSize = size;&lt;br /&gt;
			while (tmpSize != 1)&lt;br /&gt;
			{&lt;br /&gt;
				if (tmpSize % 2 != 0)&lt;br /&gt;
				{&lt;br /&gt;
					cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be powers of two&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
					size = -1;&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				tmpSize /= 2;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while (size &amp;lt; 4);  &lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Input signal length = &amp;quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl; &lt;br /&gt;
	inputSignal = new complex&amp;lt;double&amp;gt;[size];   &lt;br /&gt;
	outputSignal = new complex&amp;lt;double&amp;gt;[size];&lt;br /&gt;
	DummyDataInitialization(inputSignal, size);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void DummyDataInitialization(complex&amp;lt;double&amp;gt;* mas, int size)&lt;br /&gt;
{	&lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)&lt;br /&gt;
	{&lt;br /&gt;
		mas[i] = int(rand() % 100);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== процесс очистки памяти ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessTermination(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal) &lt;br /&gt;
{ &lt;br /&gt;
	//очистка памяти&lt;br /&gt;
	delete[] inputSignal;   &lt;br /&gt;
	inputSignal = NULL;   &lt;br /&gt;
	delete[] outputSignal;   &lt;br /&gt;
	outputSignal = NULL; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение операции &amp;quot;бабочка&amp;quot; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void Butterfly(complex&amp;lt;double&amp;gt; *signal, complex&amp;lt;double&amp;gt; &amp;amp;u,int offset, int butterflySize)&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; tem = signal[offset + butterflySize] * u;&lt;br /&gt;
	signal[offset + butterflySize] = signal[offset] - tem;   &lt;br /&gt;
	signal[offset] += tem;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение БПФ ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void SerialFFTCalculation(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	int m = 0;   &lt;br /&gt;
	for (int tmp_size = size; tmp_size &amp;gt; 1; tmp_size /= 2, m++);&lt;br /&gt;
	for (int p = 0; p &amp;lt; m; p++)&lt;br /&gt;
	{&lt;br /&gt;
		int butterflyOffset = 1 &amp;lt;&amp;lt; (p + 1);&lt;br /&gt;
		int butterflySize = butterflyOffset &amp;gt;&amp;gt; 1;&lt;br /&gt;
		double coeff = M_PI / butterflySize;&lt;br /&gt;
&lt;br /&gt;
		for (int i = 0; i &amp;lt; size / butterflyOffset; i++)&lt;br /&gt;
			for (int j = 0; j &amp;lt; butterflySize; j++)&lt;br /&gt;
				Butterfly(signal,complex&amp;lt;double&amp;gt;(cos(-j*coeff),sin(-j*coeff)),j+i*butterflyOffset,butterflySize);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== функция вывода результата (для тестирования) ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void PrintSignal(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Result signal&amp;quot; &amp;lt;&amp;lt; endl;   &lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)     &lt;br /&gt;
		cout &amp;lt;&amp;lt; signal[i] &amp;lt;&amp;lt; endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Рассмотренный алгоритм  Cooley – Tukey выполняется за &amp;lt;math&amp;gt;nlog_{2}n&amp;lt;/math&amp;gt; операций комплексного сложения и &amp;lt;math&amp;gt;\frac{n}{2}log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения. Таким образом последовательная сложность алгоритма является &#039;&#039;&#039;линейно-логарифмической&#039;&#039;&#039;.&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[file:Граф.jpg|center|thumb|800px|Рисунок 1. Алгоритм Cooley – Tukey для n=4, при предварительно сделанном бит-реверсировании]]&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
[[file:Парал.jpg|center|thumb|800px|Рисунок 2. Распараллеливание алгоритма Cooley – Tukey для n=8]]&lt;br /&gt;
Рисунок 2 получен из информационного графа, главной идеей которого является процедура деления множеств полученных на предыдущем шаге на два равных по мощности подмножества и применение к ним БПФ. То есть процесс вычисления распределяется между &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; процессорами. И так как при последовательной реализации алгоритма было &amp;lt;math&amp;gt;\frac{n}{2}\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;n\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. Получается, что при параллельной реализации алгоритма мы будем иметь &amp;lt;math&amp;gt;\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;2\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. При этом, если используется функция HT(Hyper threading), то параллельная сложность быстрого преобразования Фурье (базовый алгоритм Кули-Тьюки) для векторов с длиной, равной степени двойки составляет &amp;lt;math&amp;gt;\log_{2}n&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;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{R}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
*&#039;&#039;&#039;Комплексное ДФП&#039;&#039;&#039;&lt;br /&gt;
**входные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
*Соотношение последовательной и параллельной сложности является &#039;&#039;линейным&#039;&#039;. Это хорошо видно из пункта [[#Ресурс параллелизма алгоритма|ресурс параллелизма алгоритма]].&lt;br /&gt;
*Вычислительная мощность алгоритма является &#039;&#039;логарифмической&#039;&#039;.&lt;br /&gt;
*Устойчивость. &lt;br /&gt;
**Рассмотрим вопрос об устойчивости поближе. Так как БПФ отличается от ДПФ простым лишь сокращением одинаковых операций, то вопрос устойчивости можно рассматривать на примере ДПФ. Так, к примеру, возьмем формулу ДПФ и добавим возмущение: &amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}(x_{k}+\varepsilon )e^{\frac{-2*\pi*k*p*i}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;. После чего преобразуем ее: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2*\pi*k*p*i}{n}}+\varepsilon\sum_{k=0}^{n-1} e^{\frac{-2*\pi*k*p*i}{n}}&amp;lt;/math&amp;gt; и оценим вторую сумму, которая является геометрической прогрессией со знаменателем &amp;lt;math&amp;gt;q=e^{\frac{-2*\pi*p*i}{n}}&amp;lt;/math&amp;gt;. Таким образом оцениваемая сумма будет равняться: &amp;lt;math&amp;gt;\varepsilon\frac{1-e^{\frac{-2*\pi*p*i*(n-1))}{n}}}{1-e^{\frac{-2*\pi*p*i}{n}}}&amp;lt;/math&amp;gt;. При достаточно больших размерностях входного сигнала данное выражение сводится к: &amp;lt;math&amp;gt;\varepsilon (1-e^{-2*\pi*p*i})&amp;lt;/math&amp;gt;. В тригонометрической форме данного выражения: &amp;lt;math&amp;gt;\varepsilon (cos(\frac{2*\pi*p*(n-1)}{n})-i*sin(\frac{2*\pi*p*(n-1)}{n}))&amp;lt;/math&amp;gt; видно, что принимая во внимание целочисленность индексов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; получается 0. Таким образом, мы можем говорить об устойчивости алгоритма в целом, и абсолютной устойчивости алгоритма в асимптотике.&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;
[[file:масш.jpg|center|thumb|1000px|Рисунок 3. Параллельная реализация алгоритма. Изменение эффективности(%)в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
Как описывалось выше, каждый процессор получает достаточно низкий объем работы, что приводит к неэффективному распараллеливанию алгоритма (высокие затраты на передачу данных между процессорами по сравнению с вычислениями на них). Поэтому необходимо находить оптимальное количество процессоров для каждой отдельно взятой задачи. В данном примере, при размерности входного сигнала менее 262144, оптимальное количество процессоров составляет 1. При размерности 262144-4194304 оптимум достигался при 8 процессорах. И при размерности более 4194304- 16 процессоров. Вычисления проводились на суперкомпьютере «Ломоносов». &lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Самой распространенной и известной реализацией дискретного БПФ является библиотека [http://www.fftw.org/ FFTW], разработанная Маттео Фриго и Стивеном Г. Джонсоном в Массачусетском технологическом институте. Данная библиотека существует для языков C и FORTRAN и позволяет производить вычисления в одном или нескольких измерениях. Входной сигнал может быть как действительным так и комплексным, а его размерность не обязательно должна быть степенью двойки. На данный момент последняя версия FFTW 3.3.5. Другие особенности библиотеки:&lt;br /&gt;
*Скорость. (Поддержка SSE / SSE2 / Altivec, начиная с версии 3.0. Версия 3.3.1 поддерживает AVX и ARM Neon.)&lt;br /&gt;
*Параллельные вычисления (OpenMP, MPI).&lt;br /&gt;
*[http://www.fftw.org/fftw3_doc/ Документация] в форматах HTML, PDF.&lt;br /&gt;
*Данное программное обеспечение является свободным и распространяется по лицензии GNU (General Public License).&lt;br /&gt;
Здесь приводится пример реализации прямого БПФ для комплексных данных входного сигнала на языке C++. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fftw3-mpi.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
# include &amp;lt;time.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	const ptrdiff_t N0 = 4194304;&lt;br /&gt;
	fftw_plan planForw;&lt;br /&gt;
	fftw_complex *data, *dataOut;&lt;br /&gt;
	ptrdiff_t alloc_local, local_ni, local_i_start, i, j, local_no, local_o_start;&lt;br /&gt;
	int index, size;&lt;br /&gt;
	double startwtime, endwtime;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	fftw_mpi_init();&lt;br /&gt;
	MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;index);&lt;br /&gt;
	MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;size);&lt;br /&gt;
&lt;br /&gt;
	/* get local data size and allocate */&lt;br /&gt;
	alloc_local = fftw_mpi_local_size_1d(N0, MPI_COMM_WORLD, FFTW_FORWARD, FFTW_ESTIMATE,&lt;br /&gt;
		&amp;amp;local_ni, &amp;amp;local_i_start, &amp;amp;local_no, &amp;amp;local_o_start);&lt;br /&gt;
	data = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	dataOut = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	/* create plan  */&lt;br /&gt;
	planForw = fftw_mpi_plan_dft_1d(N0, data, dataOut, MPI_COMM_WORLD,&lt;br /&gt;
		FFTW_FORWARD, FFTW_ESTIMATE);&lt;br /&gt;
	/* initialize data to some function my_function(x,y) */&lt;br /&gt;
	for (i = 0; i &amp;lt; local_ni; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		data[i][0] = rand() / (double)RAND_MAX;&lt;br /&gt;
		data[i][1] = rand() / (double)RAND_MAX;&lt;br /&gt;
	}&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		startwtime = MPI_Wtime();&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_execute(planForw);&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		endwtime = MPI_Wtime();&lt;br /&gt;
		printf(&amp;quot;wall clock time = %f\n&amp;quot;,&lt;br /&gt;
			endwtime - startwtime);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_destroy_plan(planForw);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[http://www.fftw.org/download.html Скачав] библиотеку и установив ее на рабочий компьютер, можно поэкспериментировать, используя вышеописанный исходный код. При этом, на стадии компиляции необходимо указывать директорию, где находятся заголовочные файлы и директорию, где находятся библиотечные файлы. Пример: mpicxx name.cpp -I/path/to/include/files -L/path/to/lib/files  -lfftw3_mpi -lfftw3 -lm. Для версии 2.1.4 описание на русском языке приводится [https://parallel.ru/cluster/fftw.html здесь].&lt;br /&gt;
&lt;br /&gt;
Остальные реализации:&lt;br /&gt;
*[http://www.gnu.org/software/gsl/ GSL - GNU Scientific Library]&lt;br /&gt;
*[https://developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
*[http://www-03.ibm.com/systems/power/software/essl/ Engineering and Scientific Subroutine Library (ESSL) and Parallel ESSL]&lt;br /&gt;
=Литература=&lt;br /&gt;
[1] Бахвалов Н. С., Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний &lt;br /&gt;
&lt;br /&gt;
[2] Г. Нуссбаумер. Быстрое преобразование Фурье и алгоритмы вычисления сверток &lt;br /&gt;
&lt;br /&gt;
[3] А.О. Лапаев Параллельное вычисление дискретного преобразования Фурье полинома в простом поле&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=16388</id>
		<title>Участник:Vagabond/Алгоритм быстрого дискретного пребразования Фурье</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:Vagabond/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=16388"/>
		<updated>2016-11-11T11:06:35Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Данилишин А.Р.&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Главная идея БПФ состоит в сокращении многочисленных повторов сложения и умножения, которые выполняются в ДПФ (повторяющиеся множители: &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; Например при условии, что  &amp;lt;math&amp;gt;(pk)mod(n)=const&amp;lt;/math&amp;gt;, при всех &amp;lt;math&amp;gt;k=\overline{0,n-1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p=\overline{0,n-1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Напомним формулу ДПФ: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2kpi\pi}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
За счет использования данного факта, происходит значительное сокращение количества вычислительных операций. В данной статье используется алгоритм Cooley – Tukey, который предполагает собой разбиение исходного множества входных сигналов на два равных подмножества. При этом множество входных сигналов должно быть степенью двойки. Далее операция деления множества продолжается для образовавшихся подмножеств, и так далее. В результате над каждым из полученных подмножеств выполняется БПФ. Данные вычисления можно выполнять независимо друг от друга. Этот факт используется для реализации параллельной версии алгоритма. В общем алгоритм Cooley – Tukey строится из двух основных шагов:&lt;br /&gt;
&lt;br /&gt;
* Преобразование входного массива данных (бит-реверсирование).&lt;br /&gt;
* Вычисление БПФ.&lt;br /&gt;
Приведем более детальное описание БПФ (математическое описание):&lt;br /&gt;
*Пусть &amp;lt;math&amp;gt;e^{\frac{-2kpi\pi}{n}}&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;\omega _{n}^{kp}&amp;lt;/math&amp;gt;.&lt;br /&gt;
*Разобьем исходный сигнал &amp;lt;math&amp;gt;x(n)&amp;lt;/math&amp;gt; на два подмножества, с четными &amp;lt;math&amp;gt;x_{1}(2n)&amp;lt;/math&amp;gt; и нечетными &amp;lt;math&amp;gt;x_{2}(2n+1)&amp;lt;/math&amp;gt; номерами.&lt;br /&gt;
Тогда получаем: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}\omega _{n}^{kp}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{(2k+1)p}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выпишем два свойства омеги:&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{\frac{n}{2}}=e^{2(\frac{-2i\pi}{n})}=\omega_{n}^{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
*&amp;lt;math&amp;gt;\omega_{n}^{\frac{n}{2}}=e^{\frac{-2ni\pi}{2n}}=e^{-i\pi}=-1&amp;lt;/math&amp;gt;&lt;br /&gt;
Воспользуемся первым: &amp;lt;math&amp;gt;y_{p}= \sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{n}^{2kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{n}^{2kp}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}+\omega_{n}^{p}\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;. Или, если обозначить &amp;lt;math&amp;gt;y_{1}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;y_{2}(p)=\sum_{k=0}^{\frac{n}{2}-1}x_{2k+1}\omega _{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;, то получим формулу для вычисления координат изображения БПФ при &amp;lt;math&amp;gt;p=\overline{0...n/2}&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;y_(p)=y_{1}(p)+\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;math&amp;gt;y(p); y_{1}(p); y_{2}(p)&amp;lt;/math&amp;gt; обладают свойством периодичности, с периодом &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt;. Покажем это для &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;y_{1}(p+\frac{n}{2})=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{k(p+\frac{n}{2})}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}\omega_{\frac{n}{2}}^{\frac{kn}{2}}=\sum_{k=0}^{\frac{n}{2}-1}x_{2k}\omega_{\frac{n}{2}}^{kp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как: &amp;lt;math&amp;gt;\omega_{\frac{n}{2}}^{\frac{kn}{2}}=e^{\frac{-4kni\pi}{2n}}=e^{-2ki\pi}=1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее необходимо определить &amp;lt;math&amp;gt;y(p)&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;p\geq \frac{n}{2}&amp;lt;/math&amp;gt;. Теперь воспользуемся свойством периодичности обнаруженными нами ранее: &amp;lt;math&amp;gt;y(p+\frac{n}{2})=y_{1}(p+\frac{n}{2})+\omega_{n}^{(p+\frac{n}{2})}y_{2}(p+\frac{n}{2})&amp;lt;/math&amp;gt;. Воспользуемся вторым свойством омеги: &amp;lt;math&amp;gt;y(p)=y_{1}(p)-\omega_{n}^{p}y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В конечном итоге мы пришли к конечной формуле для первого этапа вычисления БПФ. Дальнейшие этапы делят подмножества полученные на предыдущим этапе вычисления БПФ.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Бит-реверсирование&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Бит-реверсирование – это преобразование двоичного числа путем изменения порядка следования бит в нем на противоположный. Бит-реверсирование применимо только к индексам элементов массива и предназначено для изменения порядка следования этих элементов, при этом значения самих элементов не изменяются. Так, к примеру, четырехмерный вектор &amp;lt;math&amp;gt;(x_{0},x_{1},x_{2},x_{3})&amp;lt;/math&amp;gt; изменится на &amp;lt;math&amp;gt;(x_{0},x_{2},x_{1},x_{3})&amp;lt;/math&amp;gt;. Данная процедура необходима для разделения исходного сигнала на элементы подмножеств с четными и нечетными номерами, которые необходимы для вычисления &amp;lt;math&amp;gt;y_{1}(p)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y_{2}(p)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычисление БПФ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
После выполнения этапа бит-реверсирования выполняются вычисления проиллюстрированные &#039;&#039;на рисунке 2&#039;&#039; в разделе [[#Информационный граф|Информационный граф]]. Рисунок 2 приведен для случая четырехмерного входного сигнала. Он предполагает совершение двух этапов. В общем же количество этапов равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Каждый этап содержит в себе вычисление одной и той же операции &amp;quot;бабочки&amp;quot;, где на вход подаются два числа &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, на выходе же получаются &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;. Где &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;- это &#039;&#039;коэффициент поворота&#039;&#039;. Общая формула для коэффициента поворота: &amp;lt;math&amp;gt;e^{\frac{-ji\pi}{k}}&amp;lt;/math&amp;gt;. Где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- размер шага &amp;quot;бабочки&amp;quot;, &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;- номер итерации вычислений над группой элементов, размером &amp;lt;math&amp;gt;2k&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0\leq j &amp;lt; k )&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из двух вышеописанных этапов БПФ, бит-реверсирование является самым быстровыполнимым. Поэтому вычислительное ядро алгоритма будет лежать именно во втором этапе. А именно, им будет являться вычисление &amp;quot;бабочек&amp;quot;. Количество этапов выполнения БПФ, как описывалось выше, равно &amp;lt;math&amp;gt;log_{2}n&amp;lt;/math&amp;gt;. Учитывая количество этапов, размерность входного сигнала и идею деления на подмножества, всего &amp;quot;бабочек&amp;quot; получается &amp;lt;math&amp;gt;\frac{nlog_{2}n}{2}&amp;lt;/math&amp;gt;. При этом операция умножения выполняется &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; раз. Это связано с тем, что результаты вычисления &amp;quot;бабочки&amp;quot; &amp;lt;math&amp;gt;a+bU&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a-bU&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Из всего вышесказанного заключаем, что макроструктуру алгоритма легче всего описать при помощи рекурсии: &lt;br /&gt;
*&amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; преобразований Фурье порядка 2&lt;br /&gt;
*умножение &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; пар комплексных чисел&lt;br /&gt;
*&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;
Программный код (описан на языке С++) строится из ряда функций:&lt;br /&gt;
*бит-реверсирование&lt;br /&gt;
*инициализация размерности входного сигнала &lt;br /&gt;
*инициализация входного сигнала (при помощи генератора случайных чисел)&lt;br /&gt;
*процесс очистки памяти&lt;br /&gt;
*выполнение операции &amp;quot;бабочка&amp;quot;&lt;br /&gt;
*выполнение БПФ&lt;br /&gt;
*функция вывода результата (для тестирования)&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;главный программный код БПФ:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; *inputSignal = NULL;   &lt;br /&gt;
	complex&amp;lt;double&amp;gt; *outputSignal = NULL; &lt;br /&gt;
	int size = 0;&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;FFT. Serial version.&amp;quot; &amp;lt;&amp;lt; endl;       &lt;br /&gt;
	ProcessInitialization(inputSignal, outputSignal, size);&lt;br /&gt;
	bit(inputSignal, outputSignal, size);&lt;br /&gt;
	SerialFFTCalculation(outputSignal, size);&lt;br /&gt;
	PrintSignal(outputSignal, size); //в случае тестирования&lt;br /&gt;
	ProcessTermination(inputSignal, outputSignal);&lt;br /&gt;
	cin.get();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== функция бит-реверсирования ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void bit(complex&amp;lt;double&amp;gt; inputSignal[], complex&amp;lt;double&amp;gt; *outputSignal, int size)&lt;br /&gt;
{ // процесс бит-реверсирования&lt;br /&gt;
	int j = 0, i = 0;&lt;br /&gt;
	while (i &amp;lt; size) &lt;br /&gt;
	{&lt;br /&gt;
		if (j &amp;gt; i) &lt;br /&gt;
		{ &lt;br /&gt;
			outputSignal[i] = inputSignal[j];       &lt;br /&gt;
			outputSignal[j] = inputSignal[i]; &lt;br /&gt;
		}&lt;br /&gt;
		else       &lt;br /&gt;
			if (j == i)          &lt;br /&gt;
				outputSignal[i] = inputSignal[i];&lt;br /&gt;
		&lt;br /&gt;
		int m = size &amp;gt;&amp;gt; 1;     &lt;br /&gt;
		while ((m &amp;gt;= 1) &amp;amp;&amp;amp; (j &amp;gt;= m)) &lt;br /&gt;
		{ &lt;br /&gt;
			j -= m;       &lt;br /&gt;
			m = m &amp;gt;&amp;gt; 1; &lt;br /&gt;
		}     &lt;br /&gt;
		j += m;     &lt;br /&gt;
		i++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация размерности входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessInitialization(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal, int &amp;amp;size) &lt;br /&gt;
{&lt;br /&gt;
	// инициализация размера входного сигнала  &lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		cout &amp;lt;&amp;lt; &amp;quot;Enter the input signal length: &amp;quot;;&lt;br /&gt;
		cin &amp;gt;&amp;gt; size;&lt;br /&gt;
		if (size &amp;lt; 4)&lt;br /&gt;
			cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be &amp;gt;= 4&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			int tmpSize = size;&lt;br /&gt;
			while (tmpSize != 1)&lt;br /&gt;
			{&lt;br /&gt;
				if (tmpSize % 2 != 0)&lt;br /&gt;
				{&lt;br /&gt;
					cout &amp;lt;&amp;lt; &amp;quot;Input signal length should be powers of two&amp;quot; &amp;lt;&amp;lt; endl;&lt;br /&gt;
					size = -1;&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				tmpSize /= 2;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while (size &amp;lt; 4);  &lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Input signal length = &amp;quot; &amp;lt;&amp;lt; size &amp;lt;&amp;lt; endl; &lt;br /&gt;
	inputSignal = new complex&amp;lt;double&amp;gt;[size];   &lt;br /&gt;
	outputSignal = new complex&amp;lt;double&amp;gt;[size];&lt;br /&gt;
	DummyDataInitialization(inputSignal, size);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== инициализация входного сигнала ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void DummyDataInitialization(complex&amp;lt;double&amp;gt;* mas, int size)&lt;br /&gt;
{	&lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)&lt;br /&gt;
	{&lt;br /&gt;
		mas[i] = int(rand() % 100);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== процесс очистки памяти ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void ProcessTermination(complex&amp;lt;double&amp;gt;* &amp;amp;inputSignal, complex&amp;lt;double&amp;gt;* &amp;amp;outputSignal) &lt;br /&gt;
{ &lt;br /&gt;
	//очистка памяти&lt;br /&gt;
	delete[] inputSignal;   &lt;br /&gt;
	inputSignal = NULL;   &lt;br /&gt;
	delete[] outputSignal;   &lt;br /&gt;
	outputSignal = NULL; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение операции &amp;quot;бабочка&amp;quot; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void Butterfly(complex&amp;lt;double&amp;gt; *signal, complex&amp;lt;double&amp;gt; &amp;amp;u,int offset, int butterflySize)&lt;br /&gt;
{&lt;br /&gt;
	complex&amp;lt;double&amp;gt; tem = signal[offset + butterflySize] * u;&lt;br /&gt;
	signal[offset + butterflySize] = signal[offset] - tem;   &lt;br /&gt;
	signal[offset] += tem;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== выполнение БПФ ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void SerialFFTCalculation(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	int m = 0;   &lt;br /&gt;
	for (int tmp_size = size; tmp_size &amp;gt; 1; tmp_size /= 2, m++);&lt;br /&gt;
	for (int p = 0; p &amp;lt; m; p++)&lt;br /&gt;
	{&lt;br /&gt;
		int butterflyOffset = 1 &amp;lt;&amp;lt; (p + 1);&lt;br /&gt;
		int butterflySize = butterflyOffset &amp;gt;&amp;gt; 1;&lt;br /&gt;
		double coeff = M_PI / butterflySize;&lt;br /&gt;
&lt;br /&gt;
		for (int i = 0; i &amp;lt; size / butterflyOffset; i++)&lt;br /&gt;
			for (int j = 0; j &amp;lt; butterflySize; j++)&lt;br /&gt;
				Butterfly(signal,complex&amp;lt;double&amp;gt;(cos(-j*coeff),sin(-j*coeff)),j+i*butterflyOffset,butterflySize);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== функция вывода результата (для тестирования) ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
void PrintSignal(complex&amp;lt;double&amp;gt; *signal, int size) &lt;br /&gt;
{&lt;br /&gt;
	cout &amp;lt;&amp;lt; &amp;quot;Result signal&amp;quot; &amp;lt;&amp;lt; endl;   &lt;br /&gt;
	for (int i = 0; i &amp;lt; size; i++)     &lt;br /&gt;
		cout &amp;lt;&amp;lt; signal[i] &amp;lt;&amp;lt; endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Рассмотренный алгоритм  Cooley – Tukey выполняется за &amp;lt;math&amp;gt;nlog_{2}n&amp;lt;/math&amp;gt; операций комплексного сложения и &amp;lt;math&amp;gt;\frac{n}{2}log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения. Таким образом последовательная сложность алгоритма является &#039;&#039;&#039;линейно-логарифмической&#039;&#039;&#039;.&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[file:Граф.jpg|center|thumb|800px|Рисунок 1. Алгоритм Cooley – Tukey для n=4, при предварительно сделанном бит-реверсировании]]&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
[[file:Парал.jpg|center|thumb|800px|Рисунок 2. Распараллеливание алгоритма Cooley – Tukey для n=8]]&lt;br /&gt;
Рисунок 2 получен из информационного графа, главной идеей которого является процедура деления множеств полученных на предыдущем шаге на два равных по мощности подмножества и применение к ним БПФ. То есть процесс вычисления распределяется между &amp;lt;math&amp;gt;\frac{n}{2}&amp;lt;/math&amp;gt; процессорами. И так как при последовательной реализации алгоритма было &amp;lt;math&amp;gt;\frac{n}{2}\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;n\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. Получается, что при параллельной реализации алгоритма мы будем иметь &amp;lt;math&amp;gt;\log_{2}n&amp;lt;/math&amp;gt; операций комплексного умножения и &amp;lt;math&amp;gt;2\log_{2}n&amp;lt;/math&amp;gt; комплексного сложения/вычитания. При этом, если используется функция HT(Hyper threading), то параллельная сложность быстрого преобразования Фурье (базовый алгоритм Кули-Тьюки) для векторов с длиной, равной степени двойки составляет &amp;lt;math&amp;gt;\log_{2}n&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;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{R}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
*&#039;&#039;&#039;Комплексное ДФП&#039;&#039;&#039;&lt;br /&gt;
**входные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{a}=(a_{1},a_{2},...,a_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
**выходные данные: &lt;br /&gt;
***вектор &amp;lt;math&amp;gt;\overline{b}=(b_{1},b_{2},...,b_{N})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;b_{i}\in \mathbb{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
***размерность: &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; точек&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
*Соотношение последовательной и параллельной сложности является &#039;&#039;линейным&#039;&#039;. Это хорошо видно из пункта [[#Ресурс параллелизма алгоритма|ресурс параллелизма алгоритма]].&lt;br /&gt;
*Вычислительная мощность алгоритма является &#039;&#039;логарифмической&#039;&#039;.&lt;br /&gt;
*Устойчивость. &lt;br /&gt;
**Рассмотрим вопрос об устойчивости поближе. Так как БПФ отличается от ДПФ простым лишь сокращением одинаковых операций, то вопрос устойчивости можно рассматривать на примере ДПФ. Так, к примеру, возьмем формулу ДПФ и добавим возмущение: &amp;lt;math&amp;gt; y_{p}=\sum_{k=0}^{n-1}(x_{k}+\varepsilon )e^{\frac{-2*\pi*k*p*i}{n}}, p=\overline{0,n-1}&amp;lt;/math&amp;gt;. После чего преобразуем ее: &amp;lt;math&amp;gt;y_{p}=\sum_{k=0}^{n-1}x_{k}e^{\frac{-2*\pi*k*p*i}{n}}+\varepsilon\sum_{k=0}^{n-1} e^{\frac{-2*\pi*k*p*i}{n}}&amp;lt;/math&amp;gt; и оценим вторую сумму, которая является геометрической прогрессией со знаменателем &amp;lt;math&amp;gt;q=e^{\frac{-2*\pi*p*i}{n}}&amp;lt;/math&amp;gt;. Таким образом оцениваемая сумма будет равняться: &amp;lt;math&amp;gt;\varepsilon\frac{1-e^{\frac{-2*\pi*p*i*(n-1))}{n}}}{1-e^{\frac{-2*\pi*p*i}{n}}}&amp;lt;/math&amp;gt;. При достаточно больших размерностях входного сигнала данное выражение сводится к: &amp;lt;math&amp;gt;\varepsilon (1-e^{-2*\pi*p*i})&amp;lt;/math&amp;gt;. В тригонометрической форме данного выражения: &amp;lt;math&amp;gt;\varepsilon (cos(\frac{2*\pi*p*(n-1)}{n})-i*sin(\frac{2*\pi*p*(n-1)}{n}))&amp;lt;/math&amp;gt; видно, что принимая во внимание целочисленность индексов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; получается 0. Таким образом, мы можем говорить об устойчивости алгоритма в целом, и абсолютной устойчивости алгоритма в асимптотике.&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;
[[file:масш.jpg|center|thumb|1000px|Рисунок 3. Параллельная реализация алгоритма. Изменение эффективности(%)в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
Как описывалось выше, каждый процессор получает достаточно низкий объем работы, что приводит к неэффективному распараллеливанию алгоритма (высокие затраты на передачу данных между процессорами по сравнению с вычислениями на них). Поэтому необходимо находить оптимальное количество процессоров для каждой отдельно взятой задачи. В данном примере, при размерности входного сигнала менее 262144, оптимальное количество процессоров составляет 1. При размерности 262144-4194304 оптимум достигался при 8 процессорах. И при размерности более 4194304- 16 процессоров. Вычисления проводились на суперкомпьютере «Ломоносов». &lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Самой распространенной и известной реализацией дискретного БПФ является библиотека [http://www.fftw.org/ FFTW], разработанная Маттео Фриго и Стивеном Г. Джонсоном в Массачусетском технологическом институте. Данная библиотека существует для языков C и FORTRAN и позволяет производить вычисления в одном или нескольких измерениях. Входной сигнал может быть как действительным так и комплексным, а его размерность не обязательно должна быть степенью двойки. На данный момент последняя версия FFTW 3.3.5. Другие особенности библиотеки:&lt;br /&gt;
*Скорость. (Поддержка SSE / SSE2 / Altivec, начиная с версии 3.0. Версия 3.3.1 поддерживает AVX и ARM Neon.)&lt;br /&gt;
*Параллельные вычисления (OpenMP, MPI).&lt;br /&gt;
*[http://www.fftw.org/fftw3_doc/ Документация] в форматах HTML, PDF.&lt;br /&gt;
*Данное программное обеспечение является свободным и распространяется по лицензии GNU (General Public License).&lt;br /&gt;
Здесь приводится пример реализации прямого БПФ для комплексных данных входного сигнала на языке C++. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;С++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fftw3-mpi.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
# include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
# include &amp;lt;time.h&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	const ptrdiff_t N0 = 4194304;&lt;br /&gt;
	fftw_plan planForw;&lt;br /&gt;
	fftw_complex *data, *dataOut;&lt;br /&gt;
	ptrdiff_t alloc_local, local_ni, local_i_start, i, j, local_no, local_o_start;&lt;br /&gt;
	int index, size;&lt;br /&gt;
	double startwtime, endwtime;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	fftw_mpi_init();&lt;br /&gt;
	MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;index);&lt;br /&gt;
	MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;size);&lt;br /&gt;
&lt;br /&gt;
	/* get local data size and allocate */&lt;br /&gt;
	alloc_local = fftw_mpi_local_size_1d(N0, MPI_COMM_WORLD, FFTW_FORWARD, FFTW_ESTIMATE,&lt;br /&gt;
		&amp;amp;local_ni, &amp;amp;local_i_start, &amp;amp;local_no, &amp;amp;local_o_start);&lt;br /&gt;
	data = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	dataOut = fftw_alloc_complex(alloc_local);&lt;br /&gt;
	/* create plan  */&lt;br /&gt;
	planForw = fftw_mpi_plan_dft_1d(N0, data, dataOut, MPI_COMM_WORLD,&lt;br /&gt;
		FFTW_FORWARD, FFTW_ESTIMATE);&lt;br /&gt;
	/* initialize data to some function my_function(x,y) */&lt;br /&gt;
	for (i = 0; i &amp;lt; local_ni; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		data[i][0] = rand() / (double)RAND_MAX;&lt;br /&gt;
		data[i][1] = rand() / (double)RAND_MAX;&lt;br /&gt;
	}&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		startwtime = MPI_Wtime();&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_execute(planForw);&lt;br /&gt;
	if (index == 0){&lt;br /&gt;
		endwtime = MPI_Wtime();&lt;br /&gt;
		printf(&amp;quot;wall clock time = %f\n&amp;quot;,&lt;br /&gt;
			endwtime - startwtime);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fftw_destroy_plan(planForw);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[http://www.fftw.org/download.html Скачав] библиотеку и установив ее на рабочий компьютер, можно поэкспериментировать, используя вышеописанный исходный код. При этом, на стадии компиляции необходимо указывать директорию, где находятся заголовочные файлы и директорию, где находятся библиотечные файлы. Пример: mpicxx name.cpp -I/path/to/include/files -L/path/to/lib/files  -lfftw3_mpi -lfftw3 -lm. Для версии 2.1.4 описание на русском языке приводится [https://parallel.ru/cluster/fftw.html здесь].&lt;br /&gt;
&lt;br /&gt;
Остальные реализации:&lt;br /&gt;
*[http://www.gnu.org/software/gsl/ GSL - GNU Scientific Library]&lt;br /&gt;
*[https://developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
*[http://www-03.ibm.com/systems/power/software/essl/ Engineering and Scientific Subroutine Library (ESSL) and Parallel ESSL]&lt;br /&gt;
=Литература=&lt;br /&gt;
[1] Бахвалов Н. С., Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний &lt;br /&gt;
&lt;br /&gt;
[2] Г. Нуссбаумер. Быстрое преобразование Фурье и алгоритмы вычисления сверток &lt;br /&gt;
&lt;br /&gt;
[3] А.О. Лапаев Параллельное вычисление дискретного преобразования Фурье полинома в простом поле&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Kronberg&amp;diff=16387</id>
		<title>Участник:Kronberg</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:Kronberg&amp;diff=16387"/>
		<updated>2016-11-11T10:57:18Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: Новая страница: «Кронберг Дмитрий Анатольевич, к.ф.-м.н., ассистент кафедры суперкомпьютеров и квантовой и…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Кронберг Дмитрий Анатольевич, к.ф.-м.н., ассистент кафедры суперкомпьютеров и квантовой информатики ВМК МГУ.&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Victoria_Stepanischeva&amp;diff=16386</id>
		<title>Обсуждение участника:Victoria Stepanischeva</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:Victoria_Stepanischeva&amp;diff=16386"/>
		<updated>2016-11-11T10:56:32Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Быстрое преобразование Фурье&amp;quot; */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Замечания к статье ===&lt;br /&gt;
&lt;br /&gt;
* содержание не соответствуют шаблону описания алгоритма--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:21, 25 октября 2016 (MSK)&lt;br /&gt;
* не указан конкретный вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:21, 25 октября 2016 (MSK)&lt;br /&gt;
* п.п. 1.9 не раскрыт--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:21, 25 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Быстрое преобразование Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* Раздел про общее описание алгоритма слишком большой и нарезан на много частей. Было бы лучше написать статью про саму задачу преобразования Фурье, где и упоминались бы в частности области применения, а также разные алгоритмы с краткими описаниями (а впоследствии со ссылками на статьи с полным описанием) [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:56, 11 ноября 2016 (MSK)&lt;br /&gt;
* При упоминании рекурсивного решения задачи следует сказать о глубине рекурсии [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:56, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Timokhinivan&amp;diff=16385</id>
		<title>Обсуждение участника:Timokhinivan</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:Timokhinivan&amp;diff=16385"/>
		<updated>2016-11-11T10:49:48Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Быстрое преобразование Фурье&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
* п.п. 2.4-2.6 не соответствуют содержанию описания алгоритма--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:10, 25 октября 2016 (MSK)&lt;br /&gt;
* не указаны авторы статьи и их вклад--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:10, 25 октября 2016 (MSK)&lt;br /&gt;
** Автор один, я (Тимохин Иван, 603 гр. ВМК МГУ) --[[Участник:Timokhinivan|Timokhinivan]] ([[Обсуждение участника:Timokhinivan|обсуждение]]) 14:50, 27 октября 2016 (MSK)&lt;br /&gt;
*** Так укажите себя, чтобы не было таких вопросов--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:41, 31 октября 2016 (MSK).&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Быстрое преобразование Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* В общем описании алгоритма нужны слова именно про алгоритм: следует рассказать идею быстрого преобразования, с минимумом формул [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* Не вполне понятны слова &amp;quot;первообразный корень из 1 степени N&amp;quot; [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* В последней формуле раздела 1.2 индексы k и l, кажется, должны идти через запятую. Если там рассматривается их произведение, следует уточнить, какие значения принимает каждый из них [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* Раздел 1.2.3 - понятие &amp;quot;порядок преобразования&amp;quot; нуждается в пояснении [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* Не хватает слов о глубине рекурсии [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Timokhinivan&amp;diff=16384</id>
		<title>Обсуждение участника:Timokhinivan</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:Timokhinivan&amp;diff=16384"/>
		<updated>2016-11-11T10:46:07Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Быстрое преобразование Фурье&amp;quot; */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
* п.п. 2.4-2.6 не соответствуют содержанию описания алгоритма--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:10, 25 октября 2016 (MSK)&lt;br /&gt;
* не указаны авторы статьи и их вклад--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 15:10, 25 октября 2016 (MSK)&lt;br /&gt;
** Автор один, я (Тимохин Иван, 603 гр. ВМК МГУ) --[[Участник:Timokhinivan|Timokhinivan]] ([[Обсуждение участника:Timokhinivan|обсуждение]]) 14:50, 27 октября 2016 (MSK)&lt;br /&gt;
*** Так укажите себя, чтобы не было таких вопросов--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:41, 31 октября 2016 (MSK).&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Быстрое преобразование Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* В общем описании алгоритма нужны слова именно про алгоритм: следует рассказать идею быстрого преобразования, с минимумом формул [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* Не вполне понятны слова &amp;quot;первообразный корень из 1 степени N&amp;quot; [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* В обозначении эпсилона не хватает индекса, к которому он привязан (встретится в дальнейшем) [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* В последней формуле раздела 1.2 индексы k и l, кажется, должны идти через запятую. Если там рассматривается их произведение, следует уточнить, какие значения принимает каждый из них [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* Раздел 1.2.3 - понятие &amp;quot;порядок преобразования&amp;quot; нуждается в пояснении [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;br /&gt;
* Не хватает слов о глубине рекурсии [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:46, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Chachba.A&amp;diff=16382</id>
		<title>Обсуждение участника:Chachba.A</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:Chachba.A&amp;diff=16382"/>
		<updated>2016-11-11T10:31:20Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье &amp;quot;Быстрое дискретное преобразование Фурье&amp;quot; */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
* п.п. 1.8, 1.9 не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:57, 25 октября 2016 (MSK)&lt;br /&gt;
** исправлено&lt;br /&gt;
* не указан конкретный вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:57, 25 октября 2016 (MSK)&lt;br /&gt;
** исправлено&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье &amp;quot;Быстрое дискретное преобразование Фурье&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
* Перенесите пожалуйста статью в личное пространство [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
* Раздел 1.1 - в самом первом абзаце стоит вставить обозначения двух фунций - f и f с крышкой, чтобы было ясно, что куда отображается. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
* В общее описание алгоритма нужно вставить слова именно об алгоритме, а не только о математической задаче. В чем быстрота, за счет чего она достигается и т.д. Тут, в отличие от математического описания, не стоит увлекаться формулами. [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
* Раздел 1.2 - существует обозначение части формулы через эпсилон, однако в следующей формуле он не встречается, странно [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
* Нужно пояснение, что происходит, когда N не представимо в виде произведения: применим ли алгоритм и в каких изменениях он нуждается [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;br /&gt;
* Нет данных о глубине рекурсии, написать об этом явно, скорее всего, в разделе &amp;quot;вычислительное ядро алгоритма&amp;quot; [[Участник:Kronberg|Kronberg]] ([[Обсуждение участника:Kronberg|обсуждение]]) 13:31, 11 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Kronberg</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=16381</id>
		<title>Быстрое дискретное преобразование Фурье (БПФ)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%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_(%D0%91%D0%9F%D0%A4)&amp;diff=16381"/>
		<updated>2016-11-11T10:23:42Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|IgorS}}&lt;br /&gt;
Авторы: [[Участник:Chachba.A|Чачба А.Н.]], [[Участник:Weasel|Костоев Р.С.]] &lt;br /&gt;
&lt;br /&gt;
[[Участник:Chachba.A|Чачба А.Н.]] заполнил половину всех заполненных пунктов (а именно 1.1, 1.2, 1.6, 1.8, 1.9) и построил информационный граф алгоритма, [[Участник:Weasel|Костоев Р.С.]] написал программу и провел серию запусков на суперкомпьютере и заполнил вторую половину пунктов. Оба после заполнения исправляли найденные ошибки во всех пунктах.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье (БПФ)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(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;N&amp;lt;/math&amp;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;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\hat{f}(\omega)=\frac{1}{\sqrt{2\pi}}\int\limits_{-\infty}^{\infty}f(x)e^{-ix\omega}\,dx&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Дискретное преобразование Фурье, в свою очередь, есть аналог непрерывного преобразования Фурье, но для дискретного сигнала содержащего &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; отсчетов.&lt;br /&gt;
Широко применяется в цифровой обработке сигналов, теории вероятностей, криптографии и акустике.&lt;br /&gt;
Преобразование Фурье обратимо, причем обратное преобразование имеет практически ту же форму, что и прямое.&lt;br /&gt;
Преобразование Фурье имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;, но существует быстрый вариант преобразование Фурье со сложностью &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Пусть исходный сигнал имеет значения &amp;lt;math&amp;gt;x_n,\quad n = 0,\dots,N-1&amp;lt;/math&amp;gt;, тогда дискретное прямое преобразование Фурье (ДПФ) имеет вид:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
X_k = \sum_{n=0}^{N-1}x_ne^{-\frac{2\pi i}{N}kn},\quad k = 0, \dots, N-1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt; \varepsilon_{N} = e^{-\frac{2\pi i}{N}}&amp;lt;/math&amp;gt;, тогда ДПФ можно перезаписать в матричной форме:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\bar X = A\bar x&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где матрица &amp;lt;math&amp;gt;A = \{e^{-\frac{2\pi i}{N}(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt;&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Пусть, для простоты &amp;lt;math&amp;gt; N = km&amp;lt;/math&amp;gt;, тогда рекурсивная реализация преобразования Фурье, за счет &amp;lt;math&amp;gt;{k}&amp;lt;/math&amp;gt; рекурсий на первом этапе и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; на последнем этапе, имеет суммарную сложность &amp;lt;math&amp;gt;O(Nk + Nm + km) = O(N(k + m))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В случае, например &amp;lt;math&amp;gt;N = 2^n&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем же случае, когда &amp;lt;math&amp;gt;N = \prod_{i=1}^np_i&amp;lt;/math&amp;gt; сложность БПФ составляет &amp;lt;math&amp;gt; O(N(\sum_{i=1}^np_i))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Макроструктура БПФ для случая &amp;lt;math&amp;gt;N = km&amp;lt;/math&amp;gt; описывается рекурсивно:&lt;br /&gt;
# &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; независимых преобразований векторов меньшей размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&lt;br /&gt;
#Умножение элементов на поворотные коэффициенты (&amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; умножений)&lt;br /&gt;
# &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; обратных преобразований векторов размерностей &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
Рекурсивный метод для случая &amp;lt;math&amp;gt;N = 2^k&amp;lt;/math&amp;gt;, без оптимизации на C++:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;complex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
typedef complex&amp;lt;double&amp;gt; cd;&lt;br /&gt;
typedef vector&amp;lt;cd&amp;gt; vcd;&lt;br /&gt;
&lt;br /&gt;
vcd fft(const vcd &amp;amp;as) { &lt;br /&gt;
  int n = as.size();&lt;br /&gt;
  if (n == 1) return vcd(1, as[0]);&lt;br /&gt;
&lt;br /&gt;
  vcd w(n); // Calculate roots&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    double alpha = 2 * M_PI * i / n;&lt;br /&gt;
    w[i] = cd(cos(alpha), sin(alpha));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  vcd A(n / 2), B(n / 2);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n / 2; i++) {&lt;br /&gt;
    A[i] = as[i * 2];&lt;br /&gt;
    B[i] = as[i * 2 + 1];&lt;br /&gt;
  }&lt;br /&gt;
  vcd Av = fft(A);&lt;br /&gt;
  vcd Bv = fft(B);&lt;br /&gt;
  vcd res(n);&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    res[i] =   Av[i % (n / 2)] +&lt;br /&gt;
        w[i] * Bv[i % (n / 2)];&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
В последовательном варианте можно бороться за улучшение константы сложности, предподсчитав значения соответствующих коэффициентов.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Алгоритм состоит из трех этапов, следовательно если &amp;lt;math&amp;gt;N = \prod_{i = 1}^np_i&amp;lt;/math&amp;gt;, то общая сложность составляет порядка &amp;lt;math&amp;gt;O(N\sum_{i=1}^np_i)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Пусть существует некоторое фиксированное число &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, такое что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
p_i \leq P \quad \forall i = 1...n,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
тогда, учитывая, что &amp;lt;math&amp;gt;n \leq \log_{P}{N}&amp;lt;/math&amp;gt;, сложность алгоритма можно записать в виде &amp;lt;math&amp;gt;O(N\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:FourierGraph2.png|center|thumb|640px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф преобразования Кули-Тьюки для &amp;lt;math&amp;gt;N = km = 25, k = 5, m = 5&amp;lt;/math&amp;gt;. Желтые вершины - умножение на поворотные множители.]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
В силу независимости всех рекурсивных вызовов и того факта, что путь максимальной длины в графе (так называемый критический путь) имеет порядок &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;, можно утверждать, что параллельная сложность алгоритма составит &amp;lt;math&amp;gt;O(\log{N})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Замечание&#039;&#039;: формально основание логарифма на каждом рекурсивном шаге зависит от множителя в факторизации &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, потому использована О-символика.&lt;br /&gt;
&lt;br /&gt;
При условии ограниченности количества доступных вычислительных узлов рекомендуется выбирать множители в разложении числа $N$ близкими к числу доступных узлов, таким образом доступные ресурсы будут использованы максимально эффективно.&lt;br /&gt;
&lt;br /&gt;
Итого алгоритм относится к логарифмическому классу алгоритмов по параллельной сложности.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
Чаще всего для обработки сигналов в качестве входных данных для БПФ подается вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вещественных элементов. Но БПФ работает и для случая элементов над комплексным полем. Таким образом, например, можно экономить на количестве применений БПФ в некоторой конкретной задаче путем приведения двух векторов вещественных чисел к одному вектору комплексных чисел с вещественной частью равной первому вектору и комплексной частью равной второму вектору:&lt;br /&gt;
&amp;lt;math&amp;gt;x, y&amp;lt;/math&amp;gt; - исходные сигналы. Обозначим &amp;lt;math&amp;gt;z = x + iy&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;F(k, x)&amp;lt;/math&amp;gt; - это &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-ый коэффициент образа Фурье вектора &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. Тогда:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - F(k, \bar z)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учитывая, что &amp;lt;math&amp;gt;F(k, \bar z) = \overline{F(N - k, z)}&amp;lt;/math&amp;gt;, получаем:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, x) = \frac{F(k, z) + \overline{F(N-k, z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;F(k, y) = -i\frac{F(k, z) - \overline{F(N-k,z)}}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Такой способ позволяет приблизительно в два раза уменьшить количество вычислений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
Вектор размерности &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; комплексных чисел - спектр исходного сигнала.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Матрица Вандермонда преобразования &amp;lt;math&amp;gt;A = \{e^{-\frac{2\pi i}{N}(i - 1)(j - 1)}\}_{i,j=1}^{N}&amp;lt;/math&amp;gt; такая, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
A^{-1} = \frac{1}{N}A^*&lt;br /&gt;
&amp;lt;/math&amp;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;
&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;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* Самая популярная библиотека для работы с преобразованиями Фурье это [http://www.fftw.org FFTW]&lt;br /&gt;
* Реализация от Intel в рамках [//software.intel.com/en-us/intel-mkl Math Kernel Library]&lt;br /&gt;
* Реализация ДПФ на графических картах от NVidia - [//developer.nvidia.com/cufft cuFFT]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* Бахвалов Н. С.,  Жидков Н. П., Кобельков. Г. М. — 6-е изд. — М. : БИНОМ. Лаборатория знаний, 2008. — 636 с.&lt;br /&gt;
* Описание алгоритма на Вкипедии: [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 Быстрое преобразование Фурье]&lt;br /&gt;
* Материалы лекций по &amp;quot;Алгоритмам и структурам данных&amp;quot; Школы Анализа Данных Яндекса&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Timokhinivan/%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&amp;diff=16380</id>
		<title>Участник:Timokhinivan/Быстрое преобразование Фурье</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:Timokhinivan/%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&amp;diff=16380"/>
		<updated>2016-11-11T10:15:13Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&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;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[U:Timokhinivan|Тимохин И.В.]], ВМК МГУ.&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;
вычисления этого преобразования.  На сегодняшний день наилучшие из&lt;br /&gt;
них имеют сложность &amp;lt;math&amp;gt;O(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Преобразование Фурье задаётся следующей формулой:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Y_k = \sum_{l = 0}^{N-1} X_l \epsilon^{kl}_N,\qquad k = \overline{0,N-1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\epsilon_N = e^{-\frac{2i\pi}{N}}&amp;lt;/math&amp;gt; — первообразный корень&lt;br /&gt;
из 1 степени &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, преобразование Фурье является линейным и задаётся&lt;br /&gt;
матрицей &amp;lt;math&amp;gt;F = \left\{ \epsilon^{kl}_N \right\}_{k\,l = 0}^{N-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Многомерное преобразование Фурье ====&lt;br /&gt;
Естественным обобщением описанного одномерного преобразования Фурье является многомерный вариант:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Y(l_1,\ldots,l_d) = \sum_{k_1 = 0}^{N_1-1} \cdots \sum_{k_d = 0}^{N_d-1} X(k_1,\ldots,k_d) \prod_{j=1}^{d} \epsilon_{N_j}^{l_j k_j}&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;N = \prod_{j=1}^d N_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Сведение к (почти) двумерному преобразованию (алгоритм Кули-Тьюки) ====&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; — составное, т.е. &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt;, то возможно&lt;br /&gt;
существенно сократить вычислительные расходы за счёт сведения к&lt;br /&gt;
двумерному преобразованию Фурье.  А именно, положим &amp;lt;math&amp;gt;X(l_1, l_2)&lt;br /&gt;
= X_{l_1 n + l_2}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; Y(k_1, k_2) = Y_{k_1 m + k_2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;l_1, k_2 = \overline{0, m-1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l_2, k_1 =&lt;br /&gt;
\overline{0, n-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В этом случае&amp;lt;ref name=&amp;quot;bachv&amp;quot;&amp;gt;Н.С. Бахвалов, Н.П. Жидков, Г.М. Кобельков, &#039;&#039;Численные методы&#039;&#039;, 2003 г., ISBN: 5-94774-060-5&amp;lt;/ref&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{align}&lt;br /&gt;
    Y(k_1, k_2) &amp;amp;=&amp;amp; \sum_{l_2=0}^{n-1} (\epsilon^{k_2 l_2}_N \hat{X}(k_2, l_2)) \epsilon^{k_1 l_2}_n \\&lt;br /&gt;
    \hat{X}(k_2, l_2) &amp;amp;=&amp;amp; \sum_{l_1=0}^{m-1} X(l_1, l_2) \epsilon^{k_2 l_1}_m&lt;br /&gt;
  \end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это даёт следующий алгоритм вычисления преобразования:&lt;br /&gt;
&lt;br /&gt;
# Записываем исходный вектор в матрицу &amp;lt;math&amp;gt;m\times n&amp;lt;/math&amp;gt; по строкам.&lt;br /&gt;
# Применяем к каждому столбцу преобразование Фурье.&lt;br /&gt;
# Умножаем элемент в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;\epsilon^{ij}_N&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Применяем к каждой строке преобразование Фурье.&lt;br /&gt;
# Результат записан в получившейся матрице по столбцам.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм уже даёт существенный выигрыш по сравнению с обычным&lt;br /&gt;
умножением матрицы на вектор: &amp;lt;math&amp;gt; O(m^2 n + n^2 m) &amp;lt;/math&amp;gt; против&lt;br /&gt;
&amp;lt;math&amp;gt;O(m^2 n^2)&amp;lt;/math&amp;gt;.  Однако наилучших результатов можно добиться,&lt;br /&gt;
если применять этот алгоритм рекурсивно на этапах 2 и 4.&lt;br /&gt;
&lt;br /&gt;
Так, если &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;, то целесообразно&lt;br /&gt;
на каждом уровне рекурсии «отщеплять» одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.  В этом&lt;br /&gt;
случае задача сводится к вычислению &amp;lt;math&amp;gt; \prod_{i\neq j} p_i &amp;lt;/math&amp;gt;&lt;br /&gt;
преобразований Фурье порядка &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt; для всех&lt;br /&gt;
&amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.  При небольших &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; (например, 2), это&lt;br /&gt;
можно проделывать «в лоб».&lt;br /&gt;
&lt;br /&gt;
==== Преобразование Фурье с большим простым порядком (алгоритм Радера) ====&lt;br /&gt;
&lt;br /&gt;
В случае, если порядок преобразования простой, но ещё слишком большой, чтобы вычислять его непосредственно, задача усложняется.  Алгоритм в этом случае такой:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_0 = \sum_{l=0}^{N-1} X_l&amp;lt;/math&amp;gt; вычисляется непосредственно.&lt;br /&gt;
# Любым способом вычисляется &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; — первообразный корень мультипликативной группы поля &amp;lt;math&amp;gt;\mathbb{Z}_{N}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_{g^{N-p} \bmod N} = X_0 + \sum_{q=0}^{N-2} X_{g^q \bmod N} \epsilon_N^{g^{q-p}\bmod N} &amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Последнее выражение представляет собой свёртку и вычисляется с помощью всё того же преобразования Фурье.  В принципе, это можно проделывать непосредственно, поскольку порядок свёртки &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;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;
&amp;lt;math&amp;gt;O(mn (\log m + \log n))&amp;lt;/math&amp;gt; операций против&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; для умножения на поправочные коэффициенты (шаг 3).&lt;br /&gt;
&lt;br /&gt;
То же верно и для алгоритма в целом в случае &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;;&lt;br /&gt;
а именно, суммарно наибольшие вычислительные затраты связаны с&lt;br /&gt;
вычислением преобразований Фурье порядка &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Фактически, макроструктура алгоритма уже описана в разделе&lt;br /&gt;
математического описания.  На каждом уровне рекурсии, алгоритм состоит&lt;br /&gt;
из&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; рекурсивных вызовов алгоритма.&lt;br /&gt;
# Умножение всех элементов рабочего вектора на поправочные коэффициенты.&lt;br /&gt;
# Ещё &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рекурсивных вызовов.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Псевдокод (для метода Кули-Тьюки):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fft(X, N)&lt;br /&gt;
begin&lt;br /&gt;
  (m, n) = factor(N)&lt;br /&gt;
&lt;br /&gt;
  if (n == 1) // Предполагается, что n &amp;lt;= m&lt;br /&gt;
    direct_fft(X, N) // Через умножение на матрицу&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  transpose(X, m, n) //Транспонирует X как матрицу m x n, хранящуюся по строкам&lt;br /&gt;
   &lt;br /&gt;
  // 2&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    fft(X(i * m : (i + 1) * m - 1), m)&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    do j = 0, m - 1&lt;br /&gt;
      X(i * m + j) *= eps(i * j, N)&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  transpose(X, n, m)&lt;br /&gt;
&lt;br /&gt;
  // 5&lt;br /&gt;
  do j = 0, m - 1&lt;br /&gt;
    fft(X(j * n : (j + 1) * n - 1), n)&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  transpose(X, m, n)&lt;br /&gt;
end&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;code&amp;gt;eps&amp;lt;/code&amp;gt; может как вычислять соответствующие коэффициенты, так и брать их из заранее заготовленной таблицы.  Последний способ требует увеличения затрат по памяти, но позволяет существенно сэкономить на вычислениях, а потому является предпочтительным.&lt;br /&gt;
&lt;br /&gt;
Кроме того, шаги 3 и 4 можно переставлять произвольным образом (с очевидными изменениями в шаге 3).&lt;br /&gt;
&lt;br /&gt;
Особо следует отметить, что если порядок элементов в выходном массиве не важен, шаг 6 можно пропустить.  &lt;br /&gt;
&lt;br /&gt;
Так, например, если преобразование Фурье используется для вычисления свёртки векторов, то конкретный порядок элементов в их образах Фурье совершенно не важен, лишь бы он был одинаков для обоих векторов.  В этом случае в прямом преобразовании можно опустить шаг 6, а в обратном — 1.&lt;br /&gt;
&lt;br /&gt;
Если необходимо учитывать случай большого простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; (что может и не требоваться, в зависимости от задачи), то в первом условном операторе следует дополнительно проверить величину &amp;lt;math&amp;gt;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;
&lt;br /&gt;
Непосредственно из описания алгоритма получаем, что если сложность&lt;br /&gt;
вычисления преобразования Фурье обозначить &amp;lt;math&amp;gt;f_N&amp;lt;/math&amp;gt;, то&lt;br /&gt;
&amp;lt;math&amp;gt;f_{mn} = n f_{m} + m f_{n} + mn&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N = \prod_{i=1}^K p_i&amp;lt;/math&amp;gt;, и на каждом шаге&lt;br /&gt;
«отщеплять» от него одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;, а преобразование Фурье для&lt;br /&gt;
&amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; вычислять «в лоб», то общее количество операций&lt;br /&gt;
составит &amp;lt;math&amp;gt;O\left(N\sum_{i=1}^K p_i\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В частности, если &amp;lt;math&amp;gt;p_i \leq P&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;K \leq \log_P N&amp;lt;/math&amp;gt;&lt;br /&gt;
и для сложности получаем &amp;lt;math&amp;gt;O(NP\log_P N)&amp;lt;/math&amp;gt;.  Полагая&lt;br /&gt;
&amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; фиксированным, получаем заявленную сложность &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:FourierRecursiveGraph.svg|center|thumb|600px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 15&amp;lt;/math&amp;gt;. Исходные данные обозначены фиолетовым, результат — красным.  Преобразования Фурье для &amp;lt;math&amp;gt;m = 5&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n = 3&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты представлено оранжевыми узлами.]]&lt;br /&gt;
&lt;br /&gt;
Каждый уровень рекурсивного вызова состоит из трёх этапов: рекурсивный вызов по столбцам, умножение на поправочные коэффициенты и рекурсивного вызова по строкам. Таким образом для высоты ЯПФ имеем&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 h(mn) = 1 + h(m) + h(n)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для «элементарных» БПФ высота ЯПФ будет та же, что и для [[Умножение плотной матрицы на вектор|умножения матрицы на вектор]].&lt;br /&gt;
&lt;br /&gt;
Таким образом, с учётом разложения &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;h(N) = O\left(\sum_{i=1}^K p_i\right) = O(P \log_P N) = O(\log N)&amp;lt;/math&amp;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;
&lt;br /&gt;
При этом, в отличие от традиционной реализации, в которой на каждом этапе один из множителей берётся&lt;br /&gt;
малым, при параллельной реализации целесообразно взять и&lt;br /&gt;
&amp;lt;math&amp;gt;m&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;
алгоритм всего с одной внутренней пересылкой.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
В общем случае на входе и на выходе имеются комплексные векторы&lt;br /&gt;
порядка &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.  Однако в частных случаях, возможны более компактные представления для параметров и результатов.&lt;br /&gt;
&lt;br /&gt;
Для описания этих случаев нам понадобится матрица&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Q = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;   \\ &lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — вещественный, то &amp;lt;math&amp;gt;Y = Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — чисто мнимый, то &amp;lt;math&amp;gt;Y = -Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — вещественный.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = -Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — чисто мнимый.&lt;br /&gt;
&lt;br /&gt;
В каждом случае, &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; требуют для своего хранения в два раза меньше памяти, чем в общем случае.&lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии дополнительной информации о входном векторе, возможно сократить объём памяти используемый для хранения входных и выходных данных.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Обозначая матрицу преобразования &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;, имеем&amp;lt;ref name=&amp;quot;voevodtyrt&amp;quot;&amp;gt;В.В. Воеводин, Е.Е. Тыртышников, &#039;&#039;Вычислительные процессы с теплицевыми матрицами&#039;&#039;, Москва, «Наука», 1987 г.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;F = F^T&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^{-1} = \tfrac{1}{N} F^*&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^* = QF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Свойство 2 представляет особый интерес, поскольку позволяет вычислять обратное преобразование Фурье по тем же алгоритмам, что и прямое, поскольку &amp;lt;math&amp;gt;F^*&amp;lt;/math&amp;gt; представляет собой матрицу того же вида, что и &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;, но с &amp;lt;math&amp;gt;\bar{\epsilon}&amp;lt;/math&amp;gt; вместо &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt;.  В сущности, свойство 3 позволяет свести задачу к исходной непосредственно, но ценой дополнительных пересылок.&lt;br /&gt;
&lt;br /&gt;
Кроме того, из второго свойства следует&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 \|F x\|_2 = \sqrt{N} \|x\|_2&lt;br /&gt;
&amp;lt;/math&amp;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;
==== Поворотные коэффициенты ====&lt;br /&gt;
&lt;br /&gt;
Как уже упоминалось, поворотные коэффициенты имеет смысл вычислить заранее и хранить в специальном массиве.  Отдельное внимание при этом стоит обратить на передачу этого массива в рекурсивные вызовы.&lt;br /&gt;
&lt;br /&gt;
В самом деле, поскольку &amp;lt;math&amp;gt;\epsilon^{l}_{n} = \epsilon^{lm}_{mn} = \epsilon^{lm}_{N}&amp;lt;/math&amp;gt;, все необходимые рекурсивному вызову значения поворотных коэффициентов уже имеются в используемом в текущем вызове массиве, а потому его достаточно подготовить один раз на весь алгоритм.&lt;br /&gt;
&lt;br /&gt;
==== Нерекурсивный случай ====&lt;br /&gt;
&lt;br /&gt;
На нижнем уровне рекурсии преобразование Фурье вычисляется путём умножения на матрицу.  Поскольку этот алгоритм, вообще говоря, более пригоден для векторизации, чем рекурсивная реализация, имеет смысл не разбивать &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; на &#039;&#039;совсем&#039;&#039; мелкие множители (&amp;lt;math&amp;gt;p_i \equiv 2&amp;lt;/math&amp;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;
==== Предварительные вычисления ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Многие&#039;&#039; используемые в алгоритме данные зависят только от порядка преобразования — массив поворотных коэффициентов, факторизация &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; в алгоритме Радера, конкретная задаваемая им перестановка и оптимальный порядок свёртки там же.  Вычисление некоторых из них сопоставимо по сложности со всем остальным алгоритмом.  Имея это ввиду, если планируется вычисление нескольких преобразований Фурье одного порядка, имеет смысл подготовить все эти данные заранее.&lt;br /&gt;
&lt;br /&gt;
==== Численная устойчивость ====&lt;br /&gt;
&lt;br /&gt;
Поскольку все преобразования в алгоритме являются, с точностью до константы &amp;lt;math&amp;gt;\leq \sqrt{N}&amp;lt;/math&amp;gt;, унитарными, катастрофического роста ошибок округления не предвидится, а потому все вычисления возможно проводить с целевой точностью.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
[[Файл:IT_FFTMemaccess.png|center|thumb|800px|Рисунок 2. Профиль обращений в память простого алгоритма Кули-Тьюки с &amp;lt;math&amp;gt;p = (2, 2, 3, 5, 2)&amp;lt;/math&amp;gt;. Сверху вниз: вспомогательная память для прямого умножения матрицы на вектор, память для хранения поворотных коэффициентов, рабочий вектор. Реализация не использует транспонирования векторов в явном виде.]]&lt;br /&gt;
&lt;br /&gt;
Как видно, алгоритм в целом обладает невысокой локальностью, что связано с его структурой (множество рекурсивных шагов, каждый из которых охватывает существенную часть рабочего вектора) и организацией доступа к поворотным коэффициентам. Некоторого улучшения локальности можно добиться, если транспонировать рабочую «матрицу» в памяти, но это либо требует дополнительной памяти, либо, если транспонировать «на месте», существенно усложняет алгоритм.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В случае составного &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; с &#039;&#039;достаточно&#039;&#039; большими делителями, наиболее очевидным и простым вариантом является распределение по процессорам рекурсивных вызовов алгоритма Кули-Тьюки. В этом случае на весь алгоритм приходится только три пересылки, но зато они проходят по схеме «все-со-всеми» (транспонирование матрицы) и не могут выполняться параллельно с вычислениями.&lt;br /&gt;
&lt;br /&gt;
Отдельно заметим, что, как уже упоминалось, первой либо последней группы пересылок можно избежать, если порядок элементов результата не важен.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; простое либо один из множителей в &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt; всегда мал, ситуация усложняется. В этом случае придётся применять алгоритм Радера на межпроцессорном уровне, что связано с более сложной схемой пересылок и большим их количеством (т.к. приходится осуществлять преобразование Фурье дважды: прямое и обратное). &lt;br /&gt;
&lt;br /&gt;
В связи с этим, некоторые существующие реализации (например, Intel® MKL) просто не поддерживают этот случай. Впрочем, некоторые реализации не поддерживают большие простые делители &#039;&#039;вовсе&#039;&#039;.&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;
* [//software.intel.com/en-us/intel-mkl Intel® Math Kernel Library] — содержит в том числе и версию для распределённых систем.  Имеет интерфейсы для C, C++ и Fortran.&lt;br /&gt;
* [http://www.fftw.org FFTW] — содержит распределённую реализацию начиная с версии 3.3.  Имеет интерфейсы для C и Fortran.&lt;br /&gt;
* [//www.gnu.org/software/gsl GNU Scientific Library] — имеет интерфейс для C.&lt;br /&gt;
* [//developer.nvidia.com/cufft cuFFT] — реализация алгоритма для GPU от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft clFFT] — реализация алгоритма на OpenCL для CPU и GPU.  Поддерживает только &amp;lt;math&amp;gt;N = 2^p 3^q 5^r 7^s&amp;lt;/math&amp;gt;&lt;br /&gt;
* [http://www.ibm.com/systems/power/software/essl IBM® Engineering and Scientific Subroutine Library] — для процессоров IBM® POWER™, имеет интерфейсы для C, C++ и Fortran, существует версия для многопроцессорных систем.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</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:Timokhinivan/%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&amp;diff=16379</id>
		<title>Участник:Timokhinivan/Быстрое преобразование Фурье</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:Timokhinivan/%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&amp;diff=16379"/>
		<updated>2016-11-11T10:14:47Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Общее описание алгоритма */&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;O(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;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[U:Timokhinivan|Тимохин И.В.]], ВМК МГУ.&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;
вычисления этого преобразование.  На сегодняшний день, наилучшие из&lt;br /&gt;
них имеют сложность &amp;lt;math&amp;gt;O(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Преобразование Фурье задаётся следующей формулой:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Y_k = \sum_{l = 0}^{N-1} X_l \epsilon^{kl}_N,\qquad k = \overline{0,N-1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\epsilon_N = e^{-\frac{2i\pi}{N}}&amp;lt;/math&amp;gt; — первообразный корень&lt;br /&gt;
из 1 степени &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, преобразование Фурье является линейным и задаётся&lt;br /&gt;
матрицей &amp;lt;math&amp;gt;F = \left\{ \epsilon^{kl}_N \right\}_{k\,l = 0}^{N-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Многомерное преобразование Фурье ====&lt;br /&gt;
Естественным обобщением описанного одномерного преобразования Фурье является многомерный вариант:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Y(l_1,\ldots,l_d) = \sum_{k_1 = 0}^{N_1-1} \cdots \sum_{k_d = 0}^{N_d-1} X(k_1,\ldots,k_d) \prod_{j=1}^{d} \epsilon_{N_j}^{l_j k_j}&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;N = \prod_{j=1}^d N_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Сведение к (почти) двумерному преобразованию (алгоритм Кули-Тьюки) ====&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; — составное, т.е. &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt;, то возможно&lt;br /&gt;
существенно сократить вычислительные расходы за счёт сведения к&lt;br /&gt;
двумерному преобразованию Фурье.  А именно, положим &amp;lt;math&amp;gt;X(l_1, l_2)&lt;br /&gt;
= X_{l_1 n + l_2}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; Y(k_1, k_2) = Y_{k_1 m + k_2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;l_1, k_2 = \overline{0, m-1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l_2, k_1 =&lt;br /&gt;
\overline{0, n-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В этом случае&amp;lt;ref name=&amp;quot;bachv&amp;quot;&amp;gt;Н.С. Бахвалов, Н.П. Жидков, Г.М. Кобельков, &#039;&#039;Численные методы&#039;&#039;, 2003 г., ISBN: 5-94774-060-5&amp;lt;/ref&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{align}&lt;br /&gt;
    Y(k_1, k_2) &amp;amp;=&amp;amp; \sum_{l_2=0}^{n-1} (\epsilon^{k_2 l_2}_N \hat{X}(k_2, l_2)) \epsilon^{k_1 l_2}_n \\&lt;br /&gt;
    \hat{X}(k_2, l_2) &amp;amp;=&amp;amp; \sum_{l_1=0}^{m-1} X(l_1, l_2) \epsilon^{k_2 l_1}_m&lt;br /&gt;
  \end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это даёт следующий алгоритм вычисления преобразования:&lt;br /&gt;
&lt;br /&gt;
# Записываем исходный вектор в матрицу &amp;lt;math&amp;gt;m\times n&amp;lt;/math&amp;gt; по строкам.&lt;br /&gt;
# Применяем к каждому столбцу преобразование Фурье.&lt;br /&gt;
# Умножаем элемент в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;\epsilon^{ij}_N&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Применяем к каждой строке преобразование Фурье.&lt;br /&gt;
# Результат записан в получившейся матрице по столбцам.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм уже даёт существенный выигрыш по сравнению с обычным&lt;br /&gt;
умножением матрицы на вектор: &amp;lt;math&amp;gt; O(m^2 n + n^2 m) &amp;lt;/math&amp;gt; против&lt;br /&gt;
&amp;lt;math&amp;gt;O(m^2 n^2)&amp;lt;/math&amp;gt;.  Однако наилучших результатов можно добиться,&lt;br /&gt;
если применять этот алгоритм рекурсивно на этапах 2 и 4.&lt;br /&gt;
&lt;br /&gt;
Так, если &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;, то целесообразно&lt;br /&gt;
на каждом уровне рекурсии «отщеплять» одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.  В этом&lt;br /&gt;
случае задача сводится к вычислению &amp;lt;math&amp;gt; \prod_{i\neq j} p_i &amp;lt;/math&amp;gt;&lt;br /&gt;
преобразований Фурье порядка &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt; для всех&lt;br /&gt;
&amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.  При небольших &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; (например, 2), это&lt;br /&gt;
можно проделывать «в лоб».&lt;br /&gt;
&lt;br /&gt;
==== Преобразование Фурье с большим простым порядком (алгоритм Радера) ====&lt;br /&gt;
&lt;br /&gt;
В случае, если порядок преобразования простой, но ещё слишком большой, чтобы вычислять его непосредственно, задача усложняется.  Алгоритм в этом случае такой:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_0 = \sum_{l=0}^{N-1} X_l&amp;lt;/math&amp;gt; вычисляется непосредственно.&lt;br /&gt;
# Любым способом вычисляется &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; — первообразный корень мультипликативной группы поля &amp;lt;math&amp;gt;\mathbb{Z}_{N}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_{g^{N-p} \bmod N} = X_0 + \sum_{q=0}^{N-2} X_{g^q \bmod N} \epsilon_N^{g^{q-p}\bmod N} &amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Последнее выражение представляет собой свёртку и вычисляется с помощью всё того же преобразования Фурье.  В принципе, это можно проделывать непосредственно, поскольку порядок свёртки &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;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;
&amp;lt;math&amp;gt;O(mn (\log m + \log n))&amp;lt;/math&amp;gt; операций против&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; для умножения на поправочные коэффициенты (шаг 3).&lt;br /&gt;
&lt;br /&gt;
То же верно и для алгоритма в целом в случае &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;;&lt;br /&gt;
а именно, суммарно наибольшие вычислительные затраты связаны с&lt;br /&gt;
вычислением преобразований Фурье порядка &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Фактически, макроструктура алгоритма уже описана в разделе&lt;br /&gt;
математического описания.  На каждом уровне рекурсии, алгоритм состоит&lt;br /&gt;
из&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; рекурсивных вызовов алгоритма.&lt;br /&gt;
# Умножение всех элементов рабочего вектора на поправочные коэффициенты.&lt;br /&gt;
# Ещё &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рекурсивных вызовов.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Псевдокод (для метода Кули-Тьюки):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fft(X, N)&lt;br /&gt;
begin&lt;br /&gt;
  (m, n) = factor(N)&lt;br /&gt;
&lt;br /&gt;
  if (n == 1) // Предполагается, что n &amp;lt;= m&lt;br /&gt;
    direct_fft(X, N) // Через умножение на матрицу&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  transpose(X, m, n) //Транспонирует X как матрицу m x n, хранящуюся по строкам&lt;br /&gt;
   &lt;br /&gt;
  // 2&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    fft(X(i * m : (i + 1) * m - 1), m)&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    do j = 0, m - 1&lt;br /&gt;
      X(i * m + j) *= eps(i * j, N)&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  transpose(X, n, m)&lt;br /&gt;
&lt;br /&gt;
  // 5&lt;br /&gt;
  do j = 0, m - 1&lt;br /&gt;
    fft(X(j * n : (j + 1) * n - 1), n)&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  transpose(X, m, n)&lt;br /&gt;
end&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;code&amp;gt;eps&amp;lt;/code&amp;gt; может как вычислять соответствующие коэффициенты, так и брать их из заранее заготовленной таблицы.  Последний способ требует увеличения затрат по памяти, но позволяет существенно сэкономить на вычислениях, а потому является предпочтительным.&lt;br /&gt;
&lt;br /&gt;
Кроме того, шаги 3 и 4 можно переставлять произвольным образом (с очевидными изменениями в шаге 3).&lt;br /&gt;
&lt;br /&gt;
Особо следует отметить, что если порядок элементов в выходном массиве не важен, шаг 6 можно пропустить.  &lt;br /&gt;
&lt;br /&gt;
Так, например, если преобразование Фурье используется для вычисления свёртки векторов, то конкретный порядок элементов в их образах Фурье совершенно не важен, лишь бы он был одинаков для обоих векторов.  В этом случае в прямом преобразовании можно опустить шаг 6, а в обратном — 1.&lt;br /&gt;
&lt;br /&gt;
Если необходимо учитывать случай большого простого &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; (что может и не требоваться, в зависимости от задачи), то в первом условном операторе следует дополнительно проверить величину &amp;lt;math&amp;gt;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;
&lt;br /&gt;
Непосредственно из описания алгоритма получаем, что если сложность&lt;br /&gt;
вычисления преобразования Фурье обозначить &amp;lt;math&amp;gt;f_N&amp;lt;/math&amp;gt;, то&lt;br /&gt;
&amp;lt;math&amp;gt;f_{mn} = n f_{m} + m f_{n} + mn&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N = \prod_{i=1}^K p_i&amp;lt;/math&amp;gt;, и на каждом шаге&lt;br /&gt;
«отщеплять» от него одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;, а преобразование Фурье для&lt;br /&gt;
&amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; вычислять «в лоб», то общее количество операций&lt;br /&gt;
составит &amp;lt;math&amp;gt;O\left(N\sum_{i=1}^K p_i\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В частности, если &amp;lt;math&amp;gt;p_i \leq P&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;K \leq \log_P N&amp;lt;/math&amp;gt;&lt;br /&gt;
и для сложности получаем &amp;lt;math&amp;gt;O(NP\log_P N)&amp;lt;/math&amp;gt;.  Полагая&lt;br /&gt;
&amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; фиксированным, получаем заявленную сложность &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:FourierRecursiveGraph.svg|center|thumb|600px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 15&amp;lt;/math&amp;gt;. Исходные данные обозначены фиолетовым, результат — красным.  Преобразования Фурье для &amp;lt;math&amp;gt;m = 5&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n = 3&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты представлено оранжевыми узлами.]]&lt;br /&gt;
&lt;br /&gt;
Каждый уровень рекурсивного вызова состоит из трёх этапов: рекурсивный вызов по столбцам, умножение на поправочные коэффициенты и рекурсивного вызова по строкам. Таким образом для высоты ЯПФ имеем&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 h(mn) = 1 + h(m) + h(n)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для «элементарных» БПФ высота ЯПФ будет та же, что и для [[Умножение плотной матрицы на вектор|умножения матрицы на вектор]].&lt;br /&gt;
&lt;br /&gt;
Таким образом, с учётом разложения &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;h(N) = O\left(\sum_{i=1}^K p_i\right) = O(P \log_P N) = O(\log N)&amp;lt;/math&amp;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;
&lt;br /&gt;
При этом, в отличие от традиционной реализации, в которой на каждом этапе один из множителей берётся&lt;br /&gt;
малым, при параллельной реализации целесообразно взять и&lt;br /&gt;
&amp;lt;math&amp;gt;m&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;
алгоритм всего с одной внутренней пересылкой.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
В общем случае на входе и на выходе имеются комплексные векторы&lt;br /&gt;
порядка &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;.  Однако в частных случаях, возможны более компактные представления для параметров и результатов.&lt;br /&gt;
&lt;br /&gt;
Для описания этих случаев нам понадобится матрица&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Q = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;   \\ &lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — вещественный, то &amp;lt;math&amp;gt;Y = Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — чисто мнимый, то &amp;lt;math&amp;gt;Y = -Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — вещественный.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = -Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — чисто мнимый.&lt;br /&gt;
&lt;br /&gt;
В каждом случае, &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; требуют для своего хранения в два раза меньше памяти, чем в общем случае.&lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии дополнительной информации о входном векторе, возможно сократить объём памяти используемый для хранения входных и выходных данных.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Обозначая матрицу преобразования &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;, имеем&amp;lt;ref name=&amp;quot;voevodtyrt&amp;quot;&amp;gt;В.В. Воеводин, Е.Е. Тыртышников, &#039;&#039;Вычислительные процессы с теплицевыми матрицами&#039;&#039;, Москва, «Наука», 1987 г.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;F = F^T&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^{-1} = \tfrac{1}{N} F^*&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^* = QF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Свойство 2 представляет особый интерес, поскольку позволяет вычислять обратное преобразование Фурье по тем же алгоритмам, что и прямое, поскольку &amp;lt;math&amp;gt;F^*&amp;lt;/math&amp;gt; представляет собой матрицу того же вида, что и &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;, но с &amp;lt;math&amp;gt;\bar{\epsilon}&amp;lt;/math&amp;gt; вместо &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt;.  В сущности, свойство 3 позволяет свести задачу к исходной непосредственно, но ценой дополнительных пересылок.&lt;br /&gt;
&lt;br /&gt;
Кроме того, из второго свойства следует&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 \|F x\|_2 = \sqrt{N} \|x\|_2&lt;br /&gt;
&amp;lt;/math&amp;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;
==== Поворотные коэффициенты ====&lt;br /&gt;
&lt;br /&gt;
Как уже упоминалось, поворотные коэффициенты имеет смысл вычислить заранее и хранить в специальном массиве.  Отдельное внимание при этом стоит обратить на передачу этого массива в рекурсивные вызовы.&lt;br /&gt;
&lt;br /&gt;
В самом деле, поскольку &amp;lt;math&amp;gt;\epsilon^{l}_{n} = \epsilon^{lm}_{mn} = \epsilon^{lm}_{N}&amp;lt;/math&amp;gt;, все необходимые рекурсивному вызову значения поворотных коэффициентов уже имеются в используемом в текущем вызове массиве, а потому его достаточно подготовить один раз на весь алгоритм.&lt;br /&gt;
&lt;br /&gt;
==== Нерекурсивный случай ====&lt;br /&gt;
&lt;br /&gt;
На нижнем уровне рекурсии преобразование Фурье вычисляется путём умножения на матрицу.  Поскольку этот алгоритм, вообще говоря, более пригоден для векторизации, чем рекурсивная реализация, имеет смысл не разбивать &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; на &#039;&#039;совсем&#039;&#039; мелкие множители (&amp;lt;math&amp;gt;p_i \equiv 2&amp;lt;/math&amp;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;
==== Предварительные вычисления ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Многие&#039;&#039; используемые в алгоритме данные зависят только от порядка преобразования — массив поворотных коэффициентов, факторизация &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; в алгоритме Радера, конкретная задаваемая им перестановка и оптимальный порядок свёртки там же.  Вычисление некоторых из них сопоставимо по сложности со всем остальным алгоритмом.  Имея это ввиду, если планируется вычисление нескольких преобразований Фурье одного порядка, имеет смысл подготовить все эти данные заранее.&lt;br /&gt;
&lt;br /&gt;
==== Численная устойчивость ====&lt;br /&gt;
&lt;br /&gt;
Поскольку все преобразования в алгоритме являются, с точностью до константы &amp;lt;math&amp;gt;\leq \sqrt{N}&amp;lt;/math&amp;gt;, унитарными, катастрофического роста ошибок округления не предвидится, а потому все вычисления возможно проводить с целевой точностью.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
[[Файл:IT_FFTMemaccess.png|center|thumb|800px|Рисунок 2. Профиль обращений в память простого алгоритма Кули-Тьюки с &amp;lt;math&amp;gt;p = (2, 2, 3, 5, 2)&amp;lt;/math&amp;gt;. Сверху вниз: вспомогательная память для прямого умножения матрицы на вектор, память для хранения поворотных коэффициентов, рабочий вектор. Реализация не использует транспонирования векторов в явном виде.]]&lt;br /&gt;
&lt;br /&gt;
Как видно, алгоритм в целом обладает невысокой локальностью, что связано с его структурой (множество рекурсивных шагов, каждый из которых охватывает существенную часть рабочего вектора) и организацией доступа к поворотным коэффициентам. Некоторого улучшения локальности можно добиться, если транспонировать рабочую «матрицу» в памяти, но это либо требует дополнительной памяти, либо, если транспонировать «на месте», существенно усложняет алгоритм.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В случае составного &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; с &#039;&#039;достаточно&#039;&#039; большими делителями, наиболее очевидным и простым вариантом является распределение по процессорам рекурсивных вызовов алгоритма Кули-Тьюки. В этом случае на весь алгоритм приходится только три пересылки, но зато они проходят по схеме «все-со-всеми» (транспонирование матрицы) и не могут выполняться параллельно с вычислениями.&lt;br /&gt;
&lt;br /&gt;
Отдельно заметим, что, как уже упоминалось, первой либо последней группы пересылок можно избежать, если порядок элементов результата не важен.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; простое либо один из множителей в &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt; всегда мал, ситуация усложняется. В этом случае придётся применять алгоритм Радера на межпроцессорном уровне, что связано с более сложной схемой пересылок и большим их количеством (т.к. приходится осуществлять преобразование Фурье дважды: прямое и обратное). &lt;br /&gt;
&lt;br /&gt;
В связи с этим, некоторые существующие реализации (например, Intel® MKL) просто не поддерживают этот случай. Впрочем, некоторые реализации не поддерживают большие простые делители &#039;&#039;вовсе&#039;&#039;.&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;
* [//software.intel.com/en-us/intel-mkl Intel® Math Kernel Library] — содержит в том числе и версию для распределённых систем.  Имеет интерфейсы для C, C++ и Fortran.&lt;br /&gt;
* [http://www.fftw.org FFTW] — содержит распределённую реализацию начиная с версии 3.3.  Имеет интерфейсы для C и Fortran.&lt;br /&gt;
* [//www.gnu.org/software/gsl GNU Scientific Library] — имеет интерфейс для C.&lt;br /&gt;
* [//developer.nvidia.com/cufft cuFFT] — реализация алгоритма для GPU от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft clFFT] — реализация алгоритма на OpenCL для CPU и GPU.  Поддерживает только &amp;lt;math&amp;gt;N = 2^p 3^q 5^r 7^s&amp;lt;/math&amp;gt;&lt;br /&gt;
* [http://www.ibm.com/systems/power/software/essl IBM® Engineering and Scientific Subroutine Library] — для процессоров IBM® POWER™, имеет интерфейсы для C, C++ и Fortran, существует версия для многопроцессорных систем.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kronberg</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=16378</id>
		<title>Обсуждение участника:Ivanov.kir.m</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Ivanov.kir.m&amp;diff=16378"/>
		<updated>2016-11-11T10:13:17Z</updated>

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

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

		<summary type="html">&lt;p&gt;Kronberg: /* Замечания к статье Быстрое дискретное преобразование Фурье */ новая тема&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Замечания к статье ==&lt;br /&gt;
&lt;br /&gt;
=== Неправильная структура статьи ===&lt;br /&gt;
* привести в соответствие 1 и 2 часть описания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
* «рекурсивное описание» вынести в подпункт к основному пункту содержания--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Выполнено&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующие части ===&lt;br /&gt;
* отсутствуют п.п. 2.2, 2.3, 2.5, 2.6--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Согласно постановке первого домашнего задания по курсу Суперкомпьютерное моделирование и технологии ([http://parallel.ru/ss16 Сайт курса]) пункты 2.1, 2.2, 2.3, 2.5, 2.6 по общей структуре статьи (см. [http://algowiki-project.org/ru/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2 Структура описания свойств алгоритмов]) являются необязательными к заполнению (см. [http://parallel.ru/sites/default/files/info/education/superk-modelir-1-2016.pdf Первая лекция по курсу СкМиТ] слайд 62)&lt;br /&gt;
* отсутствует вклад каждого автора--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Указали в шапке статьи&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* п.п. 1.9 и 1.10 (в нумерации автора) не раскрыты--[[Участник:IgorS|IgorS]] ([[Обсуждение участника:IgorS|обсуждение]]) 14:31, 25 октября 2016 (MSK)&lt;br /&gt;
** Исправлено&lt;br /&gt;
&lt;br /&gt;
== Замечания к статье Быстрое дискретное преобразование Фурье ==&lt;br /&gt;
&lt;br /&gt;
* В разделе &amp;quot;Общее описание алгоритма&amp;quot; следует вставить именно описание алгоритма. Потому что смысл алгоритма проясняется только в следующем разделе, а желательно иметь его общее (с минимумом формул) описание во введении&lt;br /&gt;
&lt;br /&gt;
* Стоит добавить слова о том, что происходит, когда размер массива не представим в виде произведения - можно ли тогда применять алгоритм, и если да, то какие изменения следует сделать&lt;br /&gt;
&lt;br /&gt;
* Не вполне понятна логика использования курсива и полужирного начертания&lt;br /&gt;
&lt;br /&gt;
* Не хватает данных о глубине рекурсии при использовании алгоритма&lt;/div&gt;</summary>
		<author><name>Kronberg</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=16369</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=16369"/>
		<updated>2016-11-11T10:01:38Z</updated>

		<summary type="html">&lt;p&gt;Kronberg: /* Вычислительное ядро алгоритма */&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;&#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>Kronberg</name></author>
	</entry>
</feed>