<?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=Zhum</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=Zhum"/>
	<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/Zhum"/>
	<updated>2026-10-22T19:53:45Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=20254</id>
		<title>Участник:Sarseev/Алгоритм кластеризации, основанный на минимальном покрывающем дереве</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=20254"/>
		<updated>2016-12-07T14:18:02Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Арсеев С. П., группа 620&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Данный алгоритм нацелен на решение задачи кластеризации, то есть, разделения множества объектов на группы, называемые кластерами, так, чтобы внутри одного кластера оказались наиболее похожие (по некоторой метрике) друг на друга объекты, а объекты из разных кластеров как можно сильнее отличались друг от друга.&lt;br /&gt;
Сам алгоритм состоит из следующих шагов:&lt;br /&gt;
# Сведение набора объектов к графу. Объекты становятся вершинами графа, и каждые две вершины соединяются ребром, длина которого равняется расстоянию между этими объектами по некоторой метрике. Выбор метрики зависит от предметной области и желаемых свойств алгоритма. Например, метрикой может служить евклидово расстояние.&lt;br /&gt;
# Построение минимального покрывающего (остовного) дерева в этом графе. Более подробно этот шаг описан в [[Построение_минимального_остовного_дерева_(MST)|соответствующей статье]].&lt;br /&gt;
# Кластеризация путём удаления некоторого количества рёбер в этом дереве. В случае, если заранее известно количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, достаточно удалить &amp;lt;math&amp;gt;k-1&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;n&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, которые нужно кластеризовать. Каждый такой объект можно рассматривать как вектор или элемент некоторого метрического пространства. На пространстве, которому принадлежат &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, задана метрика &amp;lt;math&amp;gt;d_{ij}=d(a_i,a_j)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Первый шаг алгоритма строит граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа &amp;lt;math&amp;gt;(|V|=n)&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество его рёбер &amp;lt;math&amp;gt;(|E| = \frac{n*(n-1)}{2})&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
На втором этапе строится минимальное покрывающее дерево &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt;. После удаления рёбер дерево распадается на поддеревья &amp;lt;math&amp;gt;MST_i&amp;lt;/math&amp;gt;. Вершины, входящие в &amp;lt;math&amp;gt;i&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;
# Преобразование исходных данных. Сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск минимального покрывающего дерева. Последовательная сложность всех трёх основных алгоритмов ([[Алгоритм_Борувки|Борувки]], [[Алгоритм_Крускала|Крускала]] и [[Алгоритм_Прима|Прима]]) &amp;lt;math&amp;gt;O(|E|*log(|E|)) = O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Разбиение на кластеры. Вычислительная сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для простых стратегий разбиения.&lt;br /&gt;
Таким образом, вычислительным ядром алгоритма является алгоритм построения минимального покрывающего дерева, поскольку он имеет наибольшую сложность. Стратегии распараллеливания алгоритма основаны на распараллеливании алгоритма построения минимального покрывающего дерева.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В состав алгоритма входит алгоритм [[Построение_минимального_остовного_дерева_(MST)|построения минимального покрывающего дерева]], являющийся его вычислительным ядром. Также в отдельную часть можно выделить алгоритм разбиения получившегося покрывающего дерева на кластеры.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Описание алгоритма на псевдокоде:&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
MST_Cluster(object[n] objects){&lt;br /&gt;
    g = Pairs(objects, metric);&lt;br /&gt;
    mst = Create_MST(graph);&lt;br /&gt;
    parts = Split_MST(mst);&lt;br /&gt;
    for i in range(parts){&lt;br /&gt;
         clusters[i] = parts[i].vertices;&lt;br /&gt;
    }&lt;br /&gt;
    return clusters&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма определяется самой сложной его частью (см. пункт 1.3) и равна &amp;lt;math&amp;gt;O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:MST_cluster.png|thumb|center|1043px|Информационный граф алгоритма. Показана последовательная реализация (большой прямоугольник) и схема распараллеливания алгоритма построения минимального покрывающего дерева.]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Ресурс параллелизма алгоритма основан на свойстве ассоциативности рёбер: &amp;lt;br&amp;gt;&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес (множество покрывающих деревьев) графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&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;
Сложность параллельного алгоритма зависит от соотношения объёма входных данных и числа процессов. Существуют распределённые алгоритмы построения минимального покрывающего дерева, имеющие асимптотическую сложность &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;(o_1, o_2, ..., o_n)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Выходом алгоритма является множество непересекающихся кластеров: &amp;lt;math&amp;gt;C=(C_1, C_2, ..., C_k); C_i = (o_{i,1}, o_{i,2}, ..., o_{i, n_i}); o(i, l) \ne o(j, m); i \ne j; \forall l, m; \sum{n_i} = n &amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Важным свойством алгоритма является то, что при его распараллеливании по описанной выше стратегии важно правильно выбирать количество процессов. При увеличении количества процессов возрастают затраты на фазу слияния деревьев, полученных каждым процессом: растёт число этапов, в которые производится слияние, а следовательно, возрастают затраты на коммуникацию между процессами, и распределение нагрузки становится более неравномерным из-за того, что часть процессов должна проделать значительно большее количество работы по слиянию деревьев. &amp;lt;br&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма имеет порядок &amp;lt;math&amp;gt;O(n*log(n^2))&amp;lt;/math&amp;gt;. &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;
[[Файл:MST_cluster_time.png|thumb|center|600px|Время работы алгоритма в зависимости от числа процессов и объёма входных данных.]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:Mst_scale2.PNG|thumb|center|600px|Время работы алгоритма при меньшем числе процессов и объёме входных данных.]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:Mst_cluster_efficiency.PNG|thumb|center|600px|Эффективность распараллеливания алгоритма.]] &amp;lt;br&amp;gt;&lt;br /&gt;
На первом и втором графике на логарифмической шкале показана зависимость времени работы алгоритма от числа процессов. Приведены измерения для девфти наборов данных, различающихся количеством входных векторов: от 512 до 131072 элементов. Использовалась собственная реализация алгоритма, основанная на [https://github.com/gpiskas/MinSpanTree_Kruskal_MPI_OpenMP сторонней реализации] алгоритма построения минимального покрывающего дерева (без OpenMP-части для более точного измерения масштабируемости описанной выше архитектуры). Изменения относительно приведённой реализации в основном коснулись генерации входных данных для алгоритма построения остовного дерева: в качестве функции расстояния между элементами при генерации графа бралось случайное число от 0 до 1. Изменения времени работы алгоритма при разных запусках (и, соответственно, с другим набором случайных чисел) незначительны. &amp;lt;br&amp;gt;&lt;br /&gt;
Полный текст изменённой части программы расположен по адресу [http://pastebin.com/5US84UgR]. &amp;lt;br&amp;gt;&lt;br /&gt;
Запуск проводился на аппаратной платформе [http://hpc.cmc.msu.ru/bgp IBM BlueGene/P]. Для каждого размера входных данных были проведены четыре теста: на 128, 256, 512 и 1024 вычислительных узлах соответственно. При дальнейшем увеличении объёма данных время выполнения программы превысило доступный предел (15 минут для 128 узлов). &amp;lt;br&amp;gt;&lt;br /&gt;
Из первого и второго графиков видно, что время работы алгоритма возрастает с увеличением объёма входных данных с зависимостью, близкой к квадратической, что соответствует теоретической оценке сложности алгоритма (&amp;lt;math&amp;gt;O(n^2*log(n^2))&amp;lt;/math&amp;gt;). Вычислительная сложность последовательной части алгоритма (кластеризация уже построенного остовного дерева) мала по сравнению со сложностью его параллельной части, что приводит к существенному снижению времени работы алгоритма при увеличении количества вычислительных узлов. &amp;lt;br&amp;gt;&lt;br /&gt;
На третьем графике показана зависимость эффективности распараллеливания алгоритма в зависимости от числа вычислительных уздов и объёма входных данных. Видно, что эффективность быстро падает для малого объёма входных данных (из-за возрастающих затрат на обмен информацией) и остаётся высокой на графе из 2048 вершин. При большом объёме входных данных и большом числе вычислительных узлов эффективность распараллеливания начинает снижаться. Это вызвано возрастанием затрат на фазу слияния, приводящим к неравномерной загрузке процессора. График эффективности распараллеливания для объёмов данных, показанных на графике 1, построить невозможно из-за аппаратных ограничений (при выполнении алгоритма на одном процессоре рёбра графа не поместятся в память вычислительного узла, а время выполнения окажется слишком большим), но тенденция снижения эффективности распараллеливания видна на третьем графике. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Основным вычислительным ядром алгоритма является алгоритм построения минимального покрывающего (остовного) дерева, применяющийся для многих задач, и многие библиотеки ограничиваются его реализацией, без реализации основанного на нём алгоритма кластеризации. Примеры реализаций алгоритмов построения MST: &lt;br /&gt;
* С++:  [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/ Boost graph library] (алгоритмы Борувки и Крускала)&lt;br /&gt;
* C++, MPI: [http://www.boost.org/doc/libs/1_62_0/libs/graph_parallel/doc/html/index.html Parallel boost graph library] (алгоритм Борувки)&lt;br /&gt;
* Java: [http://jgrapht.org/ JGraphT] (алгоритмы Крускала и Прима)&lt;br /&gt;
* Python: [http://docs.scipy.org/doc/scipy-0.15.1/reference/index.html SciPy]&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации &amp;lt;br&amp;gt;&lt;br /&gt;
# C. Kingsford. Minimum Spanning Trees &amp;amp; Clustering ([https://www.cs.umd.edu/class/fall2009/cmsc451/lectures/Lec07a-mst.pdf])&lt;br /&gt;
# Y. Zhou et al. Clustering with Minimum Spanning Trees&lt;/div&gt;</summary>
		<author><name>Zhum</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:Tereshinvs/MST_Clusterization&amp;diff=20163</id>
		<title>Участник:Tereshinvs/MST Clusterization</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:Tereshinvs/MST_Clusterization&amp;diff=20163"/>
		<updated>2016-12-06T12:29:50Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&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; V = \left\{ v_1, v_2, \ldots, v_N \right\} &amp;lt;/math&amp;gt; в некотором метрическом пространстве с метрикой &amp;lt;math&amp;gt; \rho(x, y) &amp;lt;/math&amp;gt;&lt;br /&gt;
# Матрица отношений между объектами: симметричная матрица вещественных чисел размером &amp;lt;math&amp;gt; N \times N &amp;lt;/math&amp;gt; с неотрицательными элементами с нулевой диагональю.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается постановка с известным количеством кластеров &amp;lt;math&amp;gt; K &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается алгоритм алгоритм кластеризации на основе MST (Minimum spanning tree — минимальное остовное дерево).&lt;br /&gt;
&lt;br /&gt;
Минимальное остовное дерево (или минимальное покрывающее дерево) в связанном взвешенном неориентированном графе — это остовное дерево этого графа, имеющее минимальный возможный вес, где под весом дерева понимается сумма весов входящих в него рёбер.&lt;br /&gt;
&lt;br /&gt;
Общая схема алгоритма:&lt;br /&gt;
# Построение минимального остовного дерева одним из известных методов (алгоритмы Крускала, Прима, Борувки и их комбинации; наиболее пригодным для параллельного исполнения считается алгоритм Борувки)&lt;br /&gt;
# Удаление &amp;lt;math&amp;gt; K-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;
Пусть &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; — множество объектов, &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — множество номеров (имён, меток) кластеров. Задана функция расстояния между объектами &amp;lt;math&amp;gt;\rho(x,y)&amp;lt;/math&amp;gt;. Требуется разбить выборку на непересекающиеся подмножества, называемые &#039;&#039;кластерами&#039;&#039;, так, чтобы каждый кластер состоял из объектов, близких по метрике &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt;, а объекты разных кластеров существенно отличались. При этом каждому объекту &amp;lt;math&amp;gt;v_i\in V&amp;lt;/math&amp;gt;&lt;br /&gt;
приписывается номер кластера &amp;lt;math&amp;gt;y_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Алгоритм кластеризации&#039;&#039; — это функция &amp;lt;math&amp;gt;a\colon V\to Y&amp;lt;/math&amp;gt;, которая любому объекту &amp;lt;math&amp;gt;v\in V&amp;lt;/math&amp;gt; ставит в соответствие номер кластера &amp;lt;math&amp;gt;y\in Y&amp;lt;/math&amp;gt;. Множество &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; в некоторых случаях известно заранее, однако чаще ставится задача определить оптимальное число кластеров, с точки зрения того или иного &#039;&#039;критерия качества&#039;&#039; кластеризации.&lt;br /&gt;
&lt;br /&gt;
В данном случае задачу можно сформулировать так: выбрать пороговое значение веса ребра &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt;, так чтобы объекты, соединённые ребром весом меньше &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; лежали в одном кластере, а соединённые ребром весом больше &amp;lt;math&amp;gt;W&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;
# Добавление найденных наименьших вершин в MST&lt;br /&gt;
# &amp;quot;Склейка&amp;quot; компонент связности MST&lt;br /&gt;
# Нахождение веса &amp;lt;math&amp;gt;W_{K-1}&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(K-1)&amp;lt;/math&amp;gt;-ого наибольшего ребра&lt;br /&gt;
# Удаление рёбер весом не меньше чем &amp;lt;math&amp;gt;W_{K-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot; line start=&amp;quot;0&amp;quot; highlight=&amp;quot;1,9&amp;quot;&amp;gt;&lt;br /&gt;
  Ввод: граф V&lt;br /&gt;
&lt;br /&gt;
  Поместить каждую вершину в отдельное дерево леса T&lt;br /&gt;
  Пока в T больше одной компоненты связности:&lt;br /&gt;
    Для каждой компоненты C из T:&lt;br /&gt;
      S — пустое множество рёбер&lt;br /&gt;
      Для каждой вершины v в C:&lt;br /&gt;
        Найти кратчайшее ребро с одним концом v, а другим не из C, и поместить его в S&lt;br /&gt;
      Добавить кратчайшее ребро из S в T&lt;br /&gt;
    Объединить компоненты связности&lt;br /&gt;
  T — MST&lt;br /&gt;
  &lt;br /&gt;
  Найти W — вес (K-1)-ого максимального ребра в T&lt;br /&gt;
  Удалить из T рёбра с весом большим или равным W&lt;br /&gt;
&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;
&amp;lt;!--# Для приближённого построения триангуляции Делонэ существуют алгоритмы разделяй-и-властвуй, имеющие гарантированную сложность &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;gt;&lt;br /&gt;
# Нахождение ребер наименьшего веса для всех компонент связности требует &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; операций на каждом шаге, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; — число рёбер в графе.&lt;br /&gt;
# Добавление новых рёбер в MST требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; операций за всё время работы алгоритма.&lt;br /&gt;
# При использовании структуры данных система непересекающихся множеств с ранговой эвристикой, объединение деревьев требует примерно , где &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
# Доказано, что количество шагов алгоритма &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итого &amp;lt;math&amp;gt;O(V \log N)&amp;lt;/math&amp;gt; операций. При использовании триангуляции Делонэ в некоторых случаях &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; имеет тот же порядок, что и &amp;lt;math&amp;gt;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^2 \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удаление &amp;lt;math&amp;gt;K-1&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(N^2 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[File:Tereshinvs_mst_scheme.png|frame|center|alt=Информационный граф|Информационный граф алгоритма кластеризации на основе минимального остовного дерева]]&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины находятся в своих собственных деревьях (стадия вершин). Затем с помощью нахождения кратчайших рёбер, соединяющих разные поддеревья, происходит слияние поддеревьев (стадия объединения деревьев) до тех пор, пока не останется одно дерево, являющееся минимальным остовным деревом исходного графа. После этого нужно будет лишь удалить нужное количество рёбер (выделение кластеров).&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Основным местом для распараллеливания алгоритма является поиск наименьшего ребра из одной компоненты связности в другие. Так как компоненты связности в данном случае независимы, то отдельные узлы вычислительной системы могут выполнять поиск наименьших рёбер независимо.&lt;br /&gt;
&lt;br /&gt;
Узким местом является нахождение k-ой порядковой статистики для выделения кластеров, а так же объединение компонент связности. Но всего объединений компонент связности &amp;lt;math&amp;gt;N-1&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; N &amp;lt;/math&amp;gt; (количество элементов рассматриваемого множества) и матрица вещественных чисел размера &amp;lt;math&amp;gt; N \times N &amp;lt;/math&amp;gt; отношений между элементами этого множества. Матрица предполагается симметричной, а её диагональные элементы равными нулю, поэтому её размер можно оценить как &amp;lt;math&amp;gt; \tfrac{N(N-1)}{2} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Кроме того, имеется натуральное число &amp;lt;math&amp;gt; K \leqslant 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; 1 &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;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Интересной особенностью использования библиотеки Boost::graph::distributed является возможность задания изначального распределения вершин графа по процессам. К сложностям использования можно отнести нечитаемые сообщения об ошибках инстанцирования шаблонов С++ и отсутствие большого числа примеров использования библиотеки в интернете.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
В этом параграфе описывается масштабируемость параллельной реализации алгоритма из библиотеки Boost::graph::distributed.&lt;br /&gt;
&lt;br /&gt;
С использованием библиотеки Boost::graph::distributed программу можно реализовать следующим образом (указана только часть построения MST):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot; line start=&amp;quot;0&amp;quot; highlight=&amp;quot;1,9&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/use_mpi.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/mpi_process_group.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/adjacency_list.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/vertex_list_adaptor.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/metis.hpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;fstream&amp;gt;&lt;br /&gt;
#include &amp;lt;vector&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef boost::adjacency_list&amp;lt;&lt;br /&gt;
	boost::listS,&lt;br /&gt;
	boost::distributedS&amp;lt;&lt;br /&gt;
		boost::graph::distributed::mpi_process_group,&lt;br /&gt;
		boost::vecS&lt;br /&gt;
	&amp;gt;,&lt;br /&gt;
	boost::undirectedS,&lt;br /&gt;
	boost::no_property,&lt;br /&gt;
	boost::property&amp;lt;&lt;br /&gt;
		boost::edge_weight_t,&lt;br /&gt;
		float&lt;br /&gt;
	&amp;gt;&lt;br /&gt;
&amp;gt; Graph;&lt;br /&gt;
&lt;br /&gt;
typedef boost::graph_traits&amp;lt;Graph&amp;gt;::edge_descriptor Edge;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[])&lt;br /&gt;
{&lt;br /&gt;
	boost::mpi::environment env(argc, argv);&lt;br /&gt;
&lt;br /&gt;
	std::ifstream in(&amp;quot;big.graph&amp;quot;);&lt;br /&gt;
	boost::graph::metis_reader reader(in);&lt;br /&gt;
&lt;br /&gt;
	Graph graph(&lt;br /&gt;
		reader.begin(), reader.end(),&lt;br /&gt;
		reader.weight_begin(),&lt;br /&gt;
		reader.num_vertices()&lt;br /&gt;
	);&lt;br /&gt;
&lt;br /&gt;
	typedef boost::property_map&amp;lt;Graph, boost::edge_weight_t&amp;gt;::type WeightMap;&lt;br /&gt;
	WeightMap weight_map = boost::get(boost::edge_weight, graph);&lt;br /&gt;
&lt;br /&gt;
	std::vector&amp;lt;Edge&amp;gt; mst_edges;&lt;br /&gt;
&lt;br /&gt;
	boost::graph::distributed::dense_boruvka_minimum_spanning_tree(&lt;br /&gt;
            boost::graph::make_vertex_list_adaptor(graph),&lt;br /&gt;
	    weight_map,&lt;br /&gt;
	    std::back_inserter(mst_edges)&lt;br /&gt;
	);&lt;br /&gt;
&lt;br /&gt;
	if (process_id(graph.process_group()) == 0)&lt;br /&gt;
            std::cout &amp;lt;&amp;lt; &amp;quot;Finished&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
К сожалению, не удалось запустить программу, использующую данную библиотеку на суперкомпьютере IBM BlueGene/P, поэтому замеры производительности производились на компьютере с процессором Intel Core i5 4210U (два ядра и четыре потока, тактовая частота 1.7ГГц, до 2.7ГГц в режиме Turbo Boost), 6GB оперативной памяти DDR3, жёстким диском с частотой вращения шпинделя 5400 об/мин под управлением Windows 10 64-bit с использованием Windows Subsystem for Linux.&lt;br /&gt;
&lt;br /&gt;
На графике представлена зависимость времени работы программы на случайном полном графе из 5000 вершин (соответствует набору из 5000 точек для кластеризации) от количества доступных процессоров, а также ожидаемое время работы на этом же количестве процессоров.&lt;br /&gt;
&lt;br /&gt;
Замеры производились только для построения минимального остовного дерева для полного графа (вершины — объекты, рёбра с весами — отношения), так как это является вычислительным ядром алгоритма.&lt;br /&gt;
&lt;br /&gt;
[[File:boost_mst_time.png|frame|center|alt=Время работы|Время работы алгоритма построения MST на полном графе из 5000 вершин, синем показана полученное время работы в секундах, зелёным — ожидаемое относительно увеличения количества процессором]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Библиотека шаблонов для С++ Boost предлагает прямую параллельную реализацию алгоритма Борувки в компоненте graph/distributed. Кроме этого, в ней имеются реализации других алгоритмов построения MST, а также много других параллельных алгоритмов на графах. Библиотека основана на технологии MPI. IBM предоставил патч для Boost 1.47.0 для её использования с компиляторами IBM CL C/C++.&lt;br /&gt;
&lt;br /&gt;
Кроме этой библиотеки не существует распространённой библиотеки, реализующей параллельный алгоритм построения минимального остовного дерева. Но имеется достаточно большое количество библиотек, реализующих однопоточное построение MST:&lt;br /&gt;
* JGraphT для Java&lt;br /&gt;
* SciPy MST&lt;br /&gt;
* множество любительских реализаций, который можно легко найти в интернете по запросу &amp;quot;mst algorithm&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации, http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf&lt;br /&gt;
# Minimum spanning tree, https://en.wikipedia.org/wiki/Minimum_spanning_tree&lt;br /&gt;
# Алгоритм Борувки, https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm&lt;br /&gt;
# Euclidean minimum spanning tree, https://en.wikipedia.org/wiki/Euclidean_minimum_spanning_tree&lt;br /&gt;
# Система непересекающихся множеств, http://e-maxx.ru/algo/dsu&lt;br /&gt;
# Boost Library, http://www.boost.org/&lt;br /&gt;
# Параллельное построение MST в библиотеке Boost, http://www.boost.org/doc/libs/1_55_0/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#id14&lt;br /&gt;
# Патч Boost 1.47.0 для IBM CL C/C++, http://www-01.ibm.com/support/docview.wss?uid=swg27027469&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=20162</id>
		<title>Участник:Sarseev/Алгоритм кластеризации, основанный на минимальном покрывающем дереве</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=20162"/>
		<updated>2016-12-06T12:29:09Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Арсеев С. П., группа 620&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Данный алгоритм нацелен на решение задачи кластеризации, то есть, разделения множества объектов на группы, называемые кластерами, так, чтобы внутри одного кластера оказались наиболее похожие (по некоторой метрике) друг на друга объекты, а объекты из разных кластеров как можно сильнее отличались друг от друга.&lt;br /&gt;
Сам алгоритм состоит из следующих шагов:&lt;br /&gt;
# Сведение набора объектов к графу. Объекты становятся вершинами графа, и каждые две вершины соединяются ребром, длина которого равняется расстоянию между этими объектами по некоторой метрике. Выбор метрики зависит от предметной области и желаемых свойств алгоритма. Например, метрикой может служить евклидово расстояние.&lt;br /&gt;
# Построение минимального покрывающего (остовного) дерева в этом графе. Более подробно этот шаг описан в [[Построение_минимального_остовного_дерева_(MST)|соответствующей статье]].&lt;br /&gt;
# Кластеризация путём удаления некоторого количества рёбер в этом дереве. В случае, если заранее известно количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, достаточно удалить &amp;lt;math&amp;gt;k-1&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;n&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, которые нужно кластеризовать. Каждый такой объект можно рассматривать как вектор или элемент некоторого метрического пространства. На пространстве, которому принадлежат &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, задана метрика &amp;lt;math&amp;gt;d_{ij}=d(a_i,a_j)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Первый шаг алгоритма строит граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа &amp;lt;math&amp;gt;(|V|=n)&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество его рёбер &amp;lt;math&amp;gt;(|E| = \frac{n*(n-1)}{2})&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
На втором этапе строится минимальное покрывающее дерево &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt;. После удаления рёбер дерево распадается на поддеревья &amp;lt;math&amp;gt;MST_i&amp;lt;/math&amp;gt;. Вершины, входящие в &amp;lt;math&amp;gt;i&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;
# Преобразование исходных данных. Сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск минимального покрывающего дерева. Последовательная сложность всех трёх основных алгоритмов ([[Алгоритм_Борувки|Борувки]], [[Алгоритм_Крускала|Крускала]] и [[Алгоритм_Прима|Прима]]) &amp;lt;math&amp;gt;O(|E|*log(|E|)) = O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Разбиение на кластеры. Вычислительная сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для простых стратегий разбиения.&lt;br /&gt;
Таким образом, вычислительным ядром алгоритма является алгоритм построения минимального покрывающего дерева, поскольку он имеет наибольшую сложность. Стратегии распараллеливания алгоритма основаны на распараллеливании алгоритма построения минимального покрывающего дерева.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В состав алгоритма входит алгоритм [[Построение_минимального_остовного_дерева_(MST)|построения минимального покрывающего дерева]], являющийся его вычислительным ядром. Также в отдельную часть можно выделить алгоритм разбиения получившегося покрывающего дерева на кластеры.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Описание алгоритма на псевдокоде:&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
MST_Cluster(object[n] objects){&lt;br /&gt;
    g = Pairs(objects, metric);&lt;br /&gt;
    mst = Create_MST(graph);&lt;br /&gt;
    parts = Split_MST(mst);&lt;br /&gt;
    for i in range(parts){&lt;br /&gt;
         clusters[i] = parts[i].vertices;&lt;br /&gt;
    }&lt;br /&gt;
    return clusters&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма определяется самой сложной его частью (см. пункт 1.3) и равна &amp;lt;math&amp;gt;O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:MST_cluster.png|thumb|center|1043px|Информационный граф алгоритма. Показана последовательная реализация (большой прямоугольник) и схема распараллеливания алгоритма построения минимального покрывающего дерева.]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Ресурс параллелизма алгоритма основан на свойстве ассоциативности рёбер: &amp;lt;br&amp;gt;&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес (множество покрывающих деревьев) графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&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;
Сложность параллельного алгоритма зависит от соотношения объёма входных данных и числа процессов. Существуют распределённые алгоритмы построения минимального покрывающего дерева, имеющие асимптотическую сложность &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;(o_1, o_2, ..., o_n)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Выходом алгоритма является множество непересекающихся кластеров: &amp;lt;math&amp;gt;C=(C_1, C_2, ..., C_k); C_i = (o_{i,1}, o_{i,2}, ..., o_{i, n_i}); o(i, l) \ne o(j, m); i \ne j; \forall l, m; \sum{n_i} = n &amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Важным свойством алгоритма является то, что при его распараллеливании по описанной выше стратегии важно правильно выбирать количество процессов. При увеличении количества процессов возрастают затраты на фазу слияния деревьев, полученных каждым процессом: растёт число этапов, в которые производится слияние, а следовательно, возрастают затраты на коммуникацию между процессами, и распределение нагрузки становится более неравномерным из-за того, что часть процессов должна проделать значительно большее количество работы по слиянию деревьев. &amp;lt;br&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма имеет порядок &amp;lt;math&amp;gt;O(n*log(n^2))&amp;lt;/math&amp;gt;. &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;
[[Файл:MST_cluster_time.png|thumb|center|600px|Время работы алгоритма в зависимости от числа процессов и объёма входных данных.]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:Mst_scale2.PNG|thumb|center|600px|Время работы алгоритма при меньшем числе процессов и объёме входных данных.]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:Mst_cluster_efficiency.PNG|thumb|center|600px|Эффективность распараллеливания алгоритма.]] &amp;lt;br&amp;gt;&lt;br /&gt;
На первом и втором графике на логарифмической шкале показана зависимость времени работы алгоритма от числа процессов. Приведены измерения для девфти наборов данных, различающихся количеством входных векторов: от 512 до 131072 элементов. Использовалась собственная реализация алгоритма, основанная на [https://github.com/gpiskas/MinSpanTree_Kruskal_MPI_OpenMP сторонней реализации] алгоритма построения минимального покрывающего дерева (без OpenMP-части для более точного измерения масштабируемости описанной выше архитектуры). Изменения относительно приведённой реализации в основном коснулись генерации входных данных для алгоритма построения остовного дерева: в качестве функции расстояния между элементами при генерации графа бралось случайное число от 0 до 1. Изменения времени работы алгоритма при разных запусках (и, соответственно, с другим набором случайных чисел) незначительны. &amp;lt;br&amp;gt;&lt;br /&gt;
Полный текст изменённой части программы расположен по адресу [http://pastebin.com/5US84UgR]. &amp;lt;br&amp;gt;&lt;br /&gt;
Запуск проводился на аппаратной платформе [http://hpc.cmc.msu.ru/bgp IBM BlueGene/P]. Для каждого размера входных данных были проведены четыре теста: на 128, 256, 512 и 1024 вычислительных узлах соответственно. При дальнейшем увеличении объёма данных время выполнения программы превысило доступный предел (15 минут для 128 узлов). &amp;lt;br&amp;gt;&lt;br /&gt;
На третьем графике показана зависимость эффективности распараллеливания алгоритма в зависимости от числа вычислительных уздов и объёма входных данных. Видно, что эффективность быстро падает для малого объёма входных данных (из-за возрастающих затрат на обмен информацией) и остаётся высокой на графе из 2048 вершин. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Основным вычислительным ядром алгоритма является алгоритм построения минимального покрывающего (остовного) дерева, применяющийся для многих задач, и многие библиотеки ограничиваются его реализацией, без реализации основанного на нём алгоритма кластеризации. Примеры реализаций алгоритмов построения MST: &lt;br /&gt;
* С++:  [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/ Boost graph library] (алгоритмы Борувки и Крускала)&lt;br /&gt;
* C++, MPI: [http://www.boost.org/doc/libs/1_62_0/libs/graph_parallel/doc/html/index.html Parallel boost graph library] (алгоритм Борувки)&lt;br /&gt;
* Java: [http://jgrapht.org/ JGraphT] (алгоритмы Крускала и Прима)&lt;br /&gt;
* Python: [http://docs.scipy.org/doc/scipy-0.15.1/reference/index.html SciPy]&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации &amp;lt;br&amp;gt;&lt;br /&gt;
# C. Kingsford. Minimum Spanning Trees &amp;amp; Clustering ([https://www.cs.umd.edu/class/fall2009/cmsc451/lectures/Lec07a-mst.pdf])&lt;br /&gt;
# Y. Zhou et al. Clustering with Minimum Spanning Trees&lt;/div&gt;</summary>
		<author><name>Zhum</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:Tereshinvs&amp;diff=20161</id>
		<title>Обсуждение участника:Tereshinvs</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:Tereshinvs&amp;diff=20161"/>
		<updated>2016-12-06T12:28:32Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет пояснений к графику - непонятны его свойства.&lt;br /&gt;
* три точки - это очень мало, по ним строить предположения о свойствах алгоритма или программы невозможно.&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=20160</id>
		<title>Участник:Sarseev/Алгоритм кластеризации, основанный на минимальном покрывающем дереве</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=20160"/>
		<updated>2016-12-06T12:25:50Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Zhum}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Арсеев С. П., группа 620&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Данный алгоритм нацелен на решение задачи кластеризации, то есть, разделения множества объектов на группы, называемые кластерами, так, чтобы внутри одного кластера оказались наиболее похожие (по некоторой метрике) друг на друга объекты, а объекты из разных кластеров как можно сильнее отличались друг от друга.&lt;br /&gt;
Сам алгоритм состоит из следующих шагов:&lt;br /&gt;
# Сведение набора объектов к графу. Объекты становятся вершинами графа, и каждые две вершины соединяются ребром, длина которого равняется расстоянию между этими объектами по некоторой метрике. Выбор метрики зависит от предметной области и желаемых свойств алгоритма. Например, метрикой может служить евклидово расстояние.&lt;br /&gt;
# Построение минимального покрывающего (остовного) дерева в этом графе. Более подробно этот шаг описан в [[Построение_минимального_остовного_дерева_(MST)|соответствующей статье]].&lt;br /&gt;
# Кластеризация путём удаления некоторого количества рёбер в этом дереве. В случае, если заранее известно количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, достаточно удалить &amp;lt;math&amp;gt;k-1&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;n&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, которые нужно кластеризовать. Каждый такой объект можно рассматривать как вектор или элемент некоторого метрического пространства. На пространстве, которому принадлежат &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, задана метрика &amp;lt;math&amp;gt;d_{ij}=d(a_i,a_j)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Первый шаг алгоритма строит граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа &amp;lt;math&amp;gt;(|V|=n)&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество его рёбер &amp;lt;math&amp;gt;(|E| = \frac{n*(n-1)}{2})&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
На втором этапе строится минимальное покрывающее дерево &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt;. После удаления рёбер дерево распадается на поддеревья &amp;lt;math&amp;gt;MST_i&amp;lt;/math&amp;gt;. Вершины, входящие в &amp;lt;math&amp;gt;i&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;
# Преобразование исходных данных. Сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск минимального покрывающего дерева. Последовательная сложность всех трёх основных алгоритмов ([[Алгоритм_Борувки|Борувки]], [[Алгоритм_Крускала|Крускала]] и [[Алгоритм_Прима|Прима]]) &amp;lt;math&amp;gt;O(|E|*log(|E|)) = O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Разбиение на кластеры. Вычислительная сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для простых стратегий разбиения.&lt;br /&gt;
Таким образом, вычислительным ядром алгоритма является алгоритм построения минимального покрывающего дерева, поскольку он имеет наибольшую сложность. Стратегии распараллеливания алгоритма основаны на распараллеливании алгоритма построения минимального покрывающего дерева.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В состав алгоритма входит алгоритм [[Построение_минимального_остовного_дерева_(MST)|построения минимального покрывающего дерева]], являющийся его вычислительным ядром. Также в отдельную часть можно выделить алгоритм разбиения получившегося покрывающего дерева на кластеры.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Описание алгоритма на псевдокоде:&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
MST_Cluster(object[n] objects){&lt;br /&gt;
    g = Pairs(objects, metric);&lt;br /&gt;
    mst = Create_MST(graph);&lt;br /&gt;
    parts = Split_MST(mst);&lt;br /&gt;
    for i in range(parts){&lt;br /&gt;
         clusters[i] = parts[i].vertices;&lt;br /&gt;
    }&lt;br /&gt;
    return clusters&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма определяется самой сложной его частью (см. пункт 1.3) и равна &amp;lt;math&amp;gt;O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:MST_cluster.png|thumb|center|1043px|Информационный граф алгоритма. Показана последовательная реализация (большой прямоугольник) и схема распараллеливания алгоритма построения минимального покрывающего дерева.]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Ресурс параллелизма алгоритма основан на свойстве ассоциативности рёбер: &amp;lt;br&amp;gt;&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес (множество покрывающих деревьев) графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&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;
Сложность параллельного алгоритма зависит от соотношения объёма входных данных и числа процессов. Существуют распределённые алгоритмы построения минимального покрывающего дерева, имеющие асимптотическую сложность &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;(o_1, o_2, ..., o_n)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Выходом алгоритма является множество непересекающихся кластеров: &amp;lt;math&amp;gt;C=(C_1, C_2, ..., C_k); C_i = (o_{i,1}, o_{i,2}, ..., o_{i, n_i}); o(i, l) \ne o(j, m); i \ne j; \forall l, m; \sum{n_i} = n &amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Важным свойством алгоритма является то, что при его распараллеливании по описанной выше стратегии важно правильно выбирать количество процессов. При увеличении количества процессов возрастают затраты на фазу слияния деревьев, полученных каждым процессом: растёт число этапов, в которые производится слияние, а следовательно, возрастают затраты на коммуникацию между процессами, и распределение нагрузки становится более неравномерным из-за того, что часть процессов должна проделать значительно большее количество работы по слиянию деревьев. &amp;lt;br&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма имеет порядок &amp;lt;math&amp;gt;O(n*log(n^2))&amp;lt;/math&amp;gt;. &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;
[[Файл:MST_cluster_time.png|thumb|center|600px|Время работы алгоритма в зависимости от числа процессов и объёма входных данных.]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:Mst_scale2.PNG|thumb|center|600px|Время работы алгоритма при меньшем числе процессов и объёме входных данных.]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:Mst_cluster_efficiency.PNG|thumb|center|600px|Эффективность распараллеливания алгоритма.]] &amp;lt;br&amp;gt;&lt;br /&gt;
На первом и втором графике на логарифмической шкале показана зависимость времени работы алгоритма от числа процессов. Приведены измерения для девфти наборов данных, различающихся количеством входных векторов: от 512 до 131072 элементов. Использовалась собственная реализация алгоритма, основанная на [https://github.com/gpiskas/MinSpanTree_Kruskal_MPI_OpenMP сторонней реализации] алгоритма построения минимального покрывающего дерева (без OpenMP-части для более точного измерения масштабируемости описанной выше архитектуры). Изменения относительно приведённой реализации в основном коснулись генерации входных данных для алгоритма построения остовного дерева: в качестве функции расстояния между элементами при генерации графа бралось случайное число от 0 до 1. Изменения времени работы алгоритма при разных запусках (и, соответственно, с другим набором случайных чисел) незначительны. &amp;lt;br&amp;gt;&lt;br /&gt;
Полный текст изменённой части программы расположен по адресу [http://pastebin.com/5US84UgR]. &amp;lt;br&amp;gt;&lt;br /&gt;
Запуск проводился на аппаратной платформе [http://hpc.cmc.msu.ru/bgp IBM BlueGene/P]. Для каждого размера входных данных были проведены четыре теста: на 128, 256, 512 и 1024 вычислительных узлах соответственно. При дальнейшем увеличении объёма данных время выполнения программы превысило доступный предел (15 минут для 128 узлов). &amp;lt;br&amp;gt;&lt;br /&gt;
На третьем графике показана зависимость эффективности распараллеливания алгоритма в зависимости от числа вычислительных уздов и объёма входных данных. Видно, что эффективность быстро падает для малого объёма входных данных (из-за возрастающих затрат на обмен информацией) и остаётся высокой на графе из 2048 вершин. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Основным вычислительным ядром алгоритма является алгоритм построения минимального покрывающего (остовного) дерева, применяющийся для многих задач, и многие библиотеки ограничиваются его реализацией, без реализации основанного на нём алгоритма кластеризации. Примеры реализаций алгоритмов построения MST: &lt;br /&gt;
* С++:  [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/ Boost graph library] (алгоритмы Борувки и Крускала)&lt;br /&gt;
* C++, MPI: [http://www.boost.org/doc/libs/1_62_0/libs/graph_parallel/doc/html/index.html Parallel boost graph library] (алгоритм Борувки)&lt;br /&gt;
* Java: [http://jgrapht.org/ JGraphT] (алгоритмы Крускала и Прима)&lt;br /&gt;
* Python: [http://docs.scipy.org/doc/scipy-0.15.1/reference/index.html SciPy]&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации &amp;lt;br&amp;gt;&lt;br /&gt;
# C. Kingsford. Minimum Spanning Trees &amp;amp; Clustering ([https://www.cs.umd.edu/class/fall2009/cmsc451/lectures/Lec07a-mst.pdf])&lt;br /&gt;
# Y. Zhou et al. Clustering with Minimum Spanning Trees&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev&amp;diff=20159</id>
		<title>Обсуждение участника:Sarseev</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:Sarseev&amp;diff=20159"/>
		<updated>2016-12-06T12:25:20Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п 2.4&lt;br /&gt;
&lt;br /&gt;
Примечание: задержка с последней правкой вызвана трёхдневным перебоем в работе системы BlueGene, из-за чего было невозможно доделать недостающие графики. --[[Участник:Sarseev|Сергей Арсеев]] ([[Обсуждение участника:Sarseev|обсуждение]]) 22:24, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* нет пояснений к первым двум графикам - нужно нечто аналогичное пояснению третьего.&lt;/div&gt;</summary>
		<author><name>Zhum</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19816</id>
		<title>Участник:Parkhomenko/Алгоритм k средних</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19816"/>
		<updated>2016-12-01T10:38:41Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Assignment|Zhum}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd&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(t * \log kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Parkhomenko|П.А.Пархоменко]] ([[#Общее описание алгоритма|1.1]], [[#Вычислительное ядро алгоритма|1.3]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Входные и выходные данные алгоритма|1.9]], [[#Свойства алгоритма|1.10]], [[#Масштабируемость алгоритма и его реализации|2.4]]), [[Участник:Ivan.mashonskiy|И.Д.Машонский]] ([[#Математическое описание алгоритма|1.2]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм k средних (англ. k-means) - один из алгоритмов машинного обучения, решающий задачу кластеризации. Он был изобретен в середине 1950-х математиком Гуго Штейнгаузом&amp;lt;ref&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/ref&amp;gt; и Стюартом Ллойдом&amp;lt;ref&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/ref&amp;gt;. Алгоритм стал особо популярным после публикации Маккуина&amp;lt;ref&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/ref&amp;gt;, в которой впервые был использован термин “k-means”.&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации заключается в разбиении N объектов (d-мерных векторов) на K групп (кластеров). Каждый вектор может принадлежать только одному кластеру. Количество кластеров K фиксировано, оно задается в качестве параметра.&lt;br /&gt;
&lt;br /&gt;
K-means является итерационным алгоритмом, разновидностью EM-алгоритма. Основная идея работы алгоритма заключается в проведении некоторого количества итераций, на каждой из которых происходит пересчет центра масс для каждого кластера, полученного на предыдущей итерации. После этого векторы перераспределяются по кластерам: вектор относится к тому кластеру, расстояние до центра масс которого минимально. Алгоритм завершает работу в том случае, если на очередной итерации центры масс кластеров не изменились.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Исходные данные: множество d-мерных векторов (наблюдений) &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;&amp;amp;nbsp;=&amp;amp;nbsp;&amp;lt;math&amp;gt;\{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&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;k&amp;lt;/math&amp;gt; непересекающихся множеств (кластеров) &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, таких что &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt; и значение &amp;lt;math&amp;gt;\sum_{i=1}^{k} \sum_{\mathbf x \in S_i} \left\| \mathbf x - \mu_i \right\|^2 &amp;lt;/math&amp;gt; — минимально для всех возможных наборов &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
Под &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt; здесь подразумевается центр масс векторов из множества &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Первоначальные значения центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt; определяются в начале работы алгоритма случайным образом среди векторов множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм заключается в попеременном применении двух шагов &amp;lt;ref&amp;gt;MacKay, David (2003). &amp;quot;Chapter 20. An Example Inference Task: Clustering&amp;quot; (PDF). Information Theory, Inference and Learning Algorithms. Cambridge University Press. pp. 284–292.&amp;lt;/ref&amp;gt;: &#039;&#039;&#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;S_i^{(t)}&amp;lt;/math&amp;gt;: &lt;br /&gt;
::&amp;lt;math&amp;gt;S_i^{(t)} = \big \{ x_p : \big \| x_p - \mu^{(t)}_i \big \|^2 \le \big \| x_p - \mu^{(t)}_j \big \|^2 \ \forall j, 1 \le j \le k \big\},&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс&#039;&#039;&#039;: на данном шаге вычисляются новые центры масс для кластеров, полученных на предыдущем шаге.&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм завершается, когда на очередном шаге не происходит изменения центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательного алгоритма k-means состоит из двух операций, повторяющихся на каждой итерации алгоритма:&lt;br /&gt;
* пересчета центров масс кластеров;&lt;br /&gt;
* соотнесения каждого вектора к одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
Пересчет центра масс кластера заключается в нахождении d-мерного вектора, являющегося средним арифметическим d-мерных векторов, принадлежащих кластеру. Эту операцию необходимо выполнять для каждого из K кластеров.&lt;br /&gt;
&lt;br /&gt;
Для соотнесения вектора к одному из кластеров, необходимо для каждого из N вектора посчитать расстояние до центров масс всех K кластеров. Для нахождения расстояния между двумя d-мерными векторами p и q используется Евклидова метрика: &amp;lt;math&amp;gt;\sqrt{\sum_{k=1}^d (p_k-q_k)^2}&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;&lt;br /&gt;
&lt;br /&gt;
В начале алгоритма осуществляется инициализация центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;, которая может быть реализована двумя возможными способами &amp;lt;ref&amp;gt;Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot; (PDF). Proceedings of the eleventh international conference on Information and knowledge management (CIKM).&amp;lt;/ref&amp;gt;: &lt;br /&gt;
* случайный выбор центров масс среди исходных наблюдений &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* все наблюдения случайным образом распределяются по кластерам &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, а затем для каждого кластера выполняется операция обновления центра масс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге распределения векторов по кластерам производится операция нахождения квадратов расстояний между векторами кластера и центром масс кластера: &lt;br /&gt;
::&amp;lt;math&amp;gt;\rho = \big \| x_p - \mu^{(t)}_i \big \|^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Обновление центров масс кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге обновления центров масс производится операция суммирования векторов кластера:&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &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;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;t := 1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого вектора &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;m = \underset{j \in \{1, 2, ..., k\}} {\operatorname{arg\,min}} \big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt;, &amp;lt;br/&amp;gt;вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; распределяется в кластер &amp;lt;math&amp;gt;S^{(t)}_m&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого &amp;lt;math&amp;gt;j = 1, 2, ..., k&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
#  Если &amp;lt;math&amp;gt;\mu^{(t)}_j = \mu^{(t+1)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt;, завершить выполнение алгоритма, иначе &amp;lt;math&amp;gt;t := t + 1&amp;lt;/math&amp;gt;, переход на шаг 3.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для вычисления центра массы кластера, содержащего &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; d-мерных векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n_i * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения центров масс всех k кластеров требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;k * d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения расстояния между двумя d-мерными векторами, требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;d - 1&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Для определения кластера для n d-мерного векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность одной итерации:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций умножения/деления&lt;br /&gt;
&lt;br /&gt;
Сложность всего алгоритма, состоящего из &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; итераций: &amp;lt;math&amp;gt;O(t * n * k * d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе описывается информационный граф алгоритма k-средних при &amp;lt;math&amp;gt;n = 3, k = 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 показана общая структура алгоритма: в качестве входных данных выступает матрица, строками которой являются входные векторы &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt;, на выходе получается массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; чисел, где элемент с номером &amp;lt;math&amp;gt;i = 1, 2, 3&amp;lt;/math&amp;gt; обозначает номер кластера, которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. На шаге &amp;lt;math&amp;gt;init&amp;lt;/math&amp;gt; происходит инициализация значений центров масс кластеров, после этого осуществляется итеративное повторение этапов распределения векторов по кластерам (&amp;lt;math&amp;gt;as&amp;lt;/math&amp;gt;) и обновления значений центров масс кластеров (&amp;lt;math&amp;gt;up&amp;lt;/math&amp;gt;). Когда на очередном шаге обновления центров масс кластеров значения центров масс не изменяются, алгоритм завершает работу, и в качестве выходных данных выступают номера кластеров для каждого из входных векторов.&lt;br /&gt;
[[file:k_means_main.png|thumb|center|400px|Рисунок 1. Общая схема работы алгоритма k-средних]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Распределение векторов по кластерам.&#039;&#039;&#039; На рисунке 2 изображен информационный граф для этапа распределения векторов по кластерам. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; и текущие значения центров масс кластеров &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt; (вычисленные на этапе инициализации или предшествующего этапа обновления центров масс кластеров). В качестве выходных данных выступают числа &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_i, i = 1, 2, 3&amp;lt;/math&amp;gt; - номер кластера, содержащего вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; обозначены операции векторной разности, вершинами &amp;lt;math&amp;gt;sn&amp;lt;/math&amp;gt; - операция вычисления квадрата нормы вектора, вершинами &amp;lt;math&amp;gt;am&amp;lt;/math&amp;gt; - операция &amp;lt;math&amp;gt;\underset{i \in \{1, 2\}} {\operatorname{arg\,min}}  p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k_means_info_graph_as.png|thumb|center|400px|Рисунок 2. Граф алгоритма для этапа распределения векторов по кластерам]]&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс кластеров.&#039;&#039;&#039; На рисунке 3 представлен информационный граф для этапа обновления центров масс кластеров. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; с соответствующими номерами кластеров &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, содержащих эти векторы. В качестве выходных данных здесь выступают обновленные значения центров масс кластеров: &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;s_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции суммирования векторов, принадлежащих кластеру &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;cs_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции вычисления выражения &amp;lt;math&amp;gt;1 / |S_i|&amp;lt;/math&amp;gt;, где за &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt; обозначен кластер с номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; обозначена операция произведения.&lt;br /&gt;
[[file:k_means_info_graph_up.png|thumb|center|400px|Рисунок 3. Граф алгоритма для этапа обновления центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм кластеризации k средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (распределения векторов по кластерам и обновления центров масс кластеров) лежат циклы, итерации которых являются информационно независимыми.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
В силу особенностей алгоритма k средних порядок выполнения основных этапов существенен, что не позволяет выполнять их параллельно. Таким образом, итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;T = t * (T_{as} + T_{up})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{as}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;T_{up}&amp;lt;/math&amp;gt; - параллельная сложность этапа обновления центров масс кластеров, &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - общее число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа распределения векторов по кластерам с учетом доступности неограниченного числа процессоров вычисляется следующим образом: так как вычисление значений &amp;lt;math&amp;gt;\big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt; для каждой пары векторов &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^{(t)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt; можно выполнять независимо и для вычисления этого выражения требуется &amp;lt;math&amp;gt;O(\log d)&amp;lt;/math&amp;gt; операций в соответствии с [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения сумм элементов массива сдваиванием]]. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log k)&amp;lt;/math&amp;gt;. Таким образом, сложность этапа распределения векторов по кластерам &amp;lt;math&amp;gt;O(\log kd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа обновления центров масс кластеров вычисляется следующим образом: так как обновление значений каждого из центров масс кластеров можно выполнять независимо, параллельная сложность определяется сложностью обновления центра масс самого большого кластера. В худшем случае для нахождения значения выражения &amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
С учетом вышесказанного, итоговая параллельная сложность алгоритма k средних определяется выражением &amp;lt;math&amp;gt;O(t * \log kdn)&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;k&amp;lt;/math&amp;gt; - количество кластеров;&lt;br /&gt;
* матрица координат векторов &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размерностью &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных&#039;&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; вещественных чисел (если координаты вещественные числа), &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
&#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;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности алгоритма равно &amp;lt;math&amp;gt;\frac{O(tnkd)}{O(t * \log kdn)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Число операций алгоритма k средних оценивается как &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;, суммарный объем входных и выходных данных равен &amp;lt;math&amp;gt;n(d+1)&amp;lt;/math&amp;gt;. Следовательно, вычислительная мощность алгоритма линейна по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и линейна по &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить следующие ключевые особенности алгоритма k средних:&lt;br /&gt;
* Алгоритм прост в реализации и имеет высокую скорость работы по сравнению с другими алгоритмами кластеризации (при удачном выборе начальных значений центров масс кластеров)&lt;br /&gt;
* В качестве метрики используется евклидово расстояние, а в качестве меры разброса кластера используется дисперсия&lt;br /&gt;
* Количество кластеров k является входным параметром алгоритма, поэтому неправильный выбор этого параметра может негативно повлиять на качество кластеризации&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;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование масштабируемости алгоритма k-means в зависимости от количества используемых процессов было проведено в статье Кумара&amp;lt;ref&amp;gt;Kumar, J., Mills, R. T., Hoffman, F. M., &amp;amp; Hargrove, W. W. (2011). Parallel k-means clustering for quantitative ecoregion delineation using large data sets. Procedia Computer Science, 4, 1602-1611.&amp;lt;/ref&amp;gt;. Исследование происходило на суперкомпьютере Jaguar - Cray XT5&amp;lt;ref&amp;gt;https://www.top500.org/system/176029&amp;lt;/ref&amp;gt;. На момент экспериментов данный суперкомпьютер имел следующую конфигурацию: 18,688 вычислительных узлов с двумя шестнадцатиядерными процессорами AMD Opteron 2435 (Istanbul) 2.6 GHz, 16 GB of DDR2-800 оперативной памяти, и SeaStar 2+ роутер. Всего он состоял из 224,256 вычислительных ядер, 300 TB памяти, и пиковой производительностью 2.3 petaflops.&lt;br /&gt;
&lt;br /&gt;
Реализация алгоритма была выполнена на языке программирования C с использованием MPI.&lt;br /&gt;
&lt;br /&gt;
Объем данных составлял 84 гигабайта, количество объектов (d-мерных векторов) n равнялось 1,024,767,667, размерность векторов d равнялась 22, количество кластеров k равнялось 1000. &lt;br /&gt;
&lt;br /&gt;
На рисунке 4 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно отметить, что время, затраченное на чтение данных и запись результатов кластеризации, практически не изменяется с увеличением количества задействованных процессоров. Время же работы самого алгоритма кластеризации уменьшается с увеличением количества процессоров.&lt;br /&gt;
[[Файл:k-means-proc-scalability.png|thumb|center|700px|Рисунок 4. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Также было произведено самостоятельное исследование масштабируемости алгоритма. Исследование производилось на суперкомпьютере &amp;quot;Blue Gene/P&amp;quot;&amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых параметров запуска реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* число процессоров [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];&lt;br /&gt;
* количество объектов [5000, 10000, 25000, 50000].&lt;br /&gt;
&lt;br /&gt;
Был использован набор данных &#039;&#039;Dataset for Sensorless Drive Diagnosis Data Set&#039;&#039;&amp;lt;ref&amp;gt;PASCHKE, Fabian ; BAYER, Christian ; BATOR, Martyna ; MÃ–NKS, Uwe ; DICKS, Alexander ; ENGE-ROSENBLATT, Olaf ; LOHWEG, Volker: Sensorlose ZustandsÃ¼berwachung an Synchronmotoren, Bd. 46. In: HOFFMANN, Frank; HÃœLLERMEIER, Eyke (Hrsg.): Proceedings 23. Workshop Computational Intelligence. Karlsruhe : KIT Scientific Publishing, 2013 (Schriftenreihe des Instituts fÃ¼r Angewandte Informatik - Automatisierungstechnik am Karlsruher Institut fÃ¼r Technologie, 46), S. 211-225&amp;lt;/ref&amp;gt; из репозитория &#039;&#039;Machine learning repository&#039;&#039;&amp;lt;ref&amp;gt;https://archive.ics.uci.edu/ml/datasets/Dataset+for+Sensorless+Drive+Diagnosis&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исследуемый набор данных содержит векторы, размерность которых равна 49. Компоненты векторов являются вещественными числами. Количество кластеров равно 11. Пропущенные значения отсутствуют.&lt;br /&gt;
&lt;br /&gt;
Для исследования масштабируемости алгоритма была использована реализация на языке C с использованием MPI&amp;lt;ref&amp;gt;http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html&amp;lt;/ref&amp;gt;. Код можно найти здесь: https://github.com/serban/kmeans.&lt;br /&gt;
&lt;br /&gt;
На рисунке 5 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно видеть, что при увеличении количества процессоров в N раз, время работы алгоритма уменьшается примерно в N раз (при условии, что количество объектов остается неизменным). Также можно отметить, что при фиксированном количестве процессоров, зависимость времени работы алгоритма от количества объектов также близка к линейной.&lt;br /&gt;
[[Файл:Heatmap_kmeans.png|thumb|center|900px|Рисунок 5. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (при фиксированном количестве объектов, равном 50000). При визуализации была использована логарифмическая шкала. Можно отметить, что при заданном наборе данных можно наблюдать линейную зависимость времени работы алгоритма от количества используемых процессоров, что согласуется с теоретической оценкой сложности последовательной версии алгоритма.&lt;br /&gt;
[[Файл:50000kmeans.png|thumb|center|900px|Рисунок 6. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (количество объектов равно 50000).]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие реализации алгоритма k-средних:&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++, содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [https://github.com/JuliaStats/Clustering.jl Julia k-means] реализация алгоритма &#039;&#039;k&#039;&#039;-средних на языке программирования Julia.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций &#039;&#039;k&#039;&#039;-средних на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация &#039;&#039;k&#039;&#039;-средних на языке R.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:IanaV/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_means&amp;diff=19443</id>
		<title>Участник:IanaV/Алгоритм k means</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:IanaV/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_means&amp;diff=19443"/>
		<updated>2016-11-29T08:28:25Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{level-a}}&lt;br /&gt;
Авторы страницы: [[Участник:IanaV|Валуйская Я.А.]], [[Участник:GlotovES|Глотов Е.С.]]&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;p&amp;gt;Алгоритм &amp;lt;i&amp;gt;k-means&amp;lt;/i&amp;gt; (&amp;lt;i&amp;gt;k средних&amp;lt;/i&amp;gt;) - один из наиболее популярных алгоритмов кластеризации. Наиболее простой, но в то же время достаточно неточный метод кластеризации в классической реализации.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм был разработан в 1956 году математиком Гуго Штейнгаузом &amp;lt;ref&amp;gt;[http://www.laurent-duval.eu/Documents/Steinhaus_H_1956_j-bull-acad-polon-sci_division_cmp-k-means.pdf Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.]&amp;lt;/ref&amp;gt;, и почти одновременно его изобрел Стюарт Ллойд &amp;lt;ref&amp;gt;[http://www-evasion.imag.fr/people/Franck.Hetroy/Teaching/ProjetsImage/2007/Bib/lloyd-1982.pdf Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.]&amp;lt;/ref&amp;gt;. Особую популярность алгоритм снискал после работы Джеймса Маккуина &amp;lt;ref&amp;gt;[https://pdfs.semanticscholar.org/a718/b85520bea702533ca9a5954c33576fd162b0.pdf MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.]&amp;lt;/ref&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм кластеризации &amp;lt;i&amp;gt;k-means&amp;lt;/i&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;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм k-means является итерационным алгоритмом кластеризации. На каждой итерации происходит пересчет центра масс для каждого кластера, полученного на предыдущем шаге, затем исходные векторы заново распределяются по кластерам в соответствии с тем, какой из новых центров оказался ближе по выбранной метрике. Алгоритм завершается, когда на какой-то итерации не происходит изменения кластеров.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Исходные данные:&#039;&#039; &lt;br /&gt;
* множество наблюдений &amp;lt;math&amp;gt;X = \{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, где каждое наблюдение &amp;lt;math&amp;gt;x_i \in R^d, i = 1, ..., n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* количество кластеров &amp;lt;math&amp;gt;k \in N, k \leq n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Обозначения:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;S = \{S_1, S_2, ..., S_k \} &amp;lt;/math&amp;gt; - множество кластеров, которые удовлетворяют следующим условиям:&lt;br /&gt;
** &amp;lt;math&amp;gt;S_i \bigcap S_j = \emptyset, i \neq j&amp;lt;/math&amp;gt;; &lt;br /&gt;
** &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;\mu_i \in R^d, i = 1, ..., k&amp;lt;/math&amp;gt; - центр масс кластера &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;d(u, v)&amp;lt;/math&amp;gt; - расстояние между векторами &amp;lt;math&amp;gt;u \in R^m, v \in R^m&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;L = (l_1, l_2, ..., l_n)&amp;lt;/math&amp;gt; - набор меток, где метка &amp;lt;math&amp;gt;l_i \in N_{[1, k]}&amp;lt;/math&amp;gt; - является порядковым номером кластера, к которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt; x_i \in S_{l_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;Цель алгоритма k-means&#039;&#039; - распределить наблюдения из входного множества &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;S = \{S_1, S_2, ..., S_k \} &amp;lt;/math&amp;gt; таким образом, чтобы сумма квадратов расстояний от каждой точки кластера до его центра масс по всем кластерам была минимальной: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\arg\min_{S} \sum_{i=1}^{k} \sum_{x \in S_i} (d(x, \mu_i))^2 &amp;lt;/math&amp;gt;,&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Алгоритм состоит из следующих шагов:&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;i&amp;gt;Инициализация центров масс&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге задаются начальные значения центров масс &amp;lt;math&amp;gt; \mu_1^1, ..., \mu_k^1&amp;lt;/math&amp;gt;. Существует несколько способов их выбора. Они будут рассмотрены ниже.&lt;br /&gt;
# &amp;lt;i&amp;gt;t-ый шаг итерации:&amp;lt;/i&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге каждый вектор &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; распределяется в свой кластер &amp;lt;math&amp;gt;S_j^t&amp;lt;/math&amp;gt; так, что: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;l_i^t = \arg \min_{j} (d(x_i, \mu_j^t))^2 , j = 1, ..., k; i = 1, ..., n&amp;lt;/math&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге происходит пересчет центров масс кластеров, полученных на предыдущем этапе: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;Критерий останова&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^t = \mu_i^{t+1},&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Способы инициализации начальных данных:&amp;lt;/i&amp;gt;&lt;br /&gt;
# Метод Forgy &amp;lt;br&amp;gt; Случайным образом выбираются &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества наблюдений &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;. Эти вектора используются в качестве центров масс кластеров &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;&lt;br /&gt;
# Метод случайного разбиения (Random Partition) &amp;lt;ref&amp;gt;[http://people.csail.mit.edu/tieu/notebook/kmeans/15_p600-hamerly.pdf Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot;. Proceedings of the eleventh international conference on Information and knowledge management (CIKM).]&amp;lt;/ref&amp;gt; &amp;lt;br&amp;gt; Каждый вектор &amp;lt;math&amp;gt;x_i, i = 1, .., n&amp;lt;/math&amp;gt; случайным образом распределяется в кластер &amp;lt;math&amp;gt;S_j, j = 1, .., k&amp;lt;/math&amp;gt;, затем для каждого кластера вычисляется его центр масс &amp;lt;math&amp;gt;\mu_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительном ядром алгоритма является шаг 2, состоящий из следующих этапов:&lt;br /&gt;
# распределение векторов по кластерам;&lt;br /&gt;
# пересчет центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; заключается в следующем: для каждого вектора &amp;lt;math&amp;gt;x_i \in X, i = 1, ..., n&amp;lt;/math&amp;gt; необходимо посчитать расстояние между этим вектором и центром масс кластера &amp;lt;math&amp;gt;\mu_j^t, j = 1, ..., k&amp;lt;/math&amp;gt;. Следовательно, на каждой итерации необходимо выполнить &amp;lt;math&amp;gt;n * k&amp;lt;/math&amp;gt; операций вычисления расстояния между векторами. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; заключается в следующем: для каждого кластера &amp;lt;math&amp;gt;S_i^t \in S, i = 1, ..., k&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;
&lt;br /&gt;
# Макрооперация &amp;lt;i&amp;gt;&amp;quot;Расстояние между векторами&amp;quot;&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; В данном алгоритме используется Евклидова метрика:  &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;v \in R^m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;u \in R^m: d(v, u) = \sqrt{\sum_{j=1}^{m} (v_j - u_j)^2} &amp;lt;/math&amp;gt;;&lt;br /&gt;
# Макрооперация &amp;lt;i&amp;gt;&amp;quot;Пересчет центра масс кластера&amp;quot;&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Таким образом, алгоритм в терминах макроопераций выглядит следующим образом:&amp;lt;/p&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;Инициализация центров масс&amp;lt;/i&amp;gt; &amp;lt;p&amp;gt;Метод Forgy: центры масс &amp;lt;math&amp;gt;\mu_j^1, j = 1, ..., k&amp;lt;/math&amp;gt; выбираются случайным образом.&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;Метод случайного разбиения: вектора &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; случайным образом распределяются по кластерам. Для каждого кластера выполняется макрооперация &amp;quot;Пересчет центра масс кластера&amp;quot; для нахождения &amp;lt;math&amp;gt;\mu_j^1&amp;lt;/math&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;t-ый шаг итерации:&amp;lt;/i&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге для каждого вектора &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; k раз выполняется макрооперация &amp;quot;Расстояние между векторами&amp;quot; для нахождения расстояния между &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu_j^t, j = 1, ..., k&amp;lt;/math&amp;gt;. Затем для каждого вектора &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; находится минимальное расстояние.&lt;br /&gt;
#* &amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге для каждого кластера выполняется макрооперация &amp;quot;Пересчет центра масс&amp;quot; для нахождения &amp;lt;math&amp;gt;\mu_j^{t+1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;i&amp;gt;Критерий останова&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^t = \mu_i^{t+1},&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Псевдокод алгоритма&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;b&amp;gt;Входные данные&amp;lt;/b&amp;gt;  : Множество векторов &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;lt;b&amp;gt;Выходные данные&amp;lt;/b&amp;gt; : Набор меток &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; принадлежности к кластеру&lt;br /&gt;
 1  Инициализация центров масс &amp;lt;math&amp;gt;\mu_i^1, i = 1, ..., k&amp;lt;/math&amp;gt;;&lt;br /&gt;
 2  t := 1;&lt;br /&gt;
 3  Для каждого вектора &amp;lt;math&amp;gt;x_i \in X, i = 1, ..., n:&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;l_i^t = \arg \min_{j} (d(x_i,\mu_j^t))^2 , j = 1, ..., k&amp;lt;/math&amp;gt;;&lt;br /&gt;
 4  Для каждого кластера &amp;lt;math&amp;gt;S_i^t, i = 1, ..., k&amp;lt;/math&amp;gt; выполняется макрооперация &amp;lt;i&amp;gt;&amp;quot;Пересчет центра масс&amp;quot;&amp;lt;/i&amp;gt;;&lt;br /&gt;
 5  &amp;lt;b&amp;gt;if&amp;lt;/b&amp;gt; (&amp;lt;math&amp;gt;\exists i = 1, ..., k: \mu_i^t \neq \mu_i^{t+1}&amp;lt;/math&amp;gt;) {&lt;br /&gt;
 6      t := t + 1;&lt;br /&gt;
 7      &amp;lt;b&amp;gt;goto&amp;lt;/b&amp;gt; 3;&lt;br /&gt;
 8  } &amp;lt;b&amp;gt;else&amp;lt;/b&amp;gt; {&lt;br /&gt;
 9      &amp;lt;b&amp;gt;stop&amp;lt;/b&amp;gt;;&lt;br /&gt;
 10 };&lt;br /&gt;
&lt;br /&gt;
Существующие способы оптимизации алгоритма &amp;lt;ref&amp;gt;[http://link.springer.com/chapter/10.1007/3-540-45643-0_13  Phillips S. J. Acceleration of k-means and related clustering algorithms //Workshop on Algorithm Engineering and Experimentation. – Springer Berlin Heidelberg, 2002. – С. 166-177.]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.187.3017&amp;amp;rep=rep1&amp;amp;type=pdf  Hamerly G. Making k-means Even Faster //SDM. – 2010. – С. 130-140.]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;[http://cseweb.ucsd.edu/~elkan/kmeansicml03.pdf  Elkan C. Using the triangle inequality to accelerate k-means //ICML. – 2003. – Т. 3. – С. 147-153.]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Подсчитаем количество операций для каждого шага алгоритма.&lt;br /&gt;
&lt;br /&gt;
# Инициализация:&lt;br /&gt;
#* Метод Forgy: &amp;lt;math&amp;gt;N_{init} = 0&amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метод случайного разбиения: &lt;br /&gt;
#** Количество операций деления: &amp;lt;math&amp;gt;N^{/}_{init} = k * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#** Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{init} = d * (n - k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Распределение &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов &amp;lt;math&amp;gt;x_i \in R^d&amp;lt;/math&amp;gt; по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам на одной итерации:&lt;br /&gt;
#* Количество операций умножения: &amp;lt;math&amp;gt;N^{*}_{distr} = k * n * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{distr} = k * n * (d - 1)&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций вычитания: &amp;lt;math&amp;gt;N^{-}_{distr} = k * n * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций взятия минимума: &amp;lt;math&amp;gt;N^{min}_{distr} = n * (k - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Пересчет центра масс &amp;lt;math&amp;gt;\mu_i \in R^d&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров на одной итерации:&lt;br /&gt;
#* Количество операций деления: &amp;lt;math&amp;gt;N^{/}_{update} = k * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{update} = d * (n - k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Следовательно, в случае если алгоритм сошелся за &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, получаем:&lt;br /&gt;
* Операций сложения/вычитания: &amp;lt;math&amp;gt;N^{+,-}_{k-means} = N^{+}_{init} + i*(N^{+}_{distr} + N^{-}_{distr} + N^{+}_{update}) = d * (n - k) + i*(k*n*(d-1)+k*n*d+d*(n-k)) \thicksim O(i*k*n*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Операций умножения/деления: &amp;lt;math&amp;gt;N^{*,/}_{k-means} = N^{/}_{init} + i*(N^{*}_{distr} + N^{/}_{update}) = k * d + i*(k*n*d+k*d) \thicksim O(i*k*n*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, [[глоссарий#Последовательная сложность|&#039;&#039;последовательная сложность&#039;&#039;]] алгоритма k-means для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерных векторов и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров за &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, требуемых для сходимости алгоритма:  &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;O(i * n * k * d)&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф алгоритма k-means.&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 1&amp;lt;/i&amp;gt; представлена общая схема алгоритма k-means, согласно разделу [[#Схема реализации последовательного алгоритма|&amp;quot;Схема реализации последовательного алгоритма&amp;quot;]]. &lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Init&amp;lt;/i&amp;gt; - этап инициализации.&lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Distribute&amp;lt;/i&amp;gt; - этап распределения векторов по кластерам.&lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Update&amp;lt;/i&amp;gt; - этап пересчета центров масс кластеров.&lt;br /&gt;
[[file:k-means-all.PNG|800px|thumb|center|Рисунок 1. Общая схема алгоритма k-means]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 2&amp;lt;/i&amp;gt; представлен информационный граф этапа распределения векторов по кластерам. На вход данному этапу подают множество векторов &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt; из входных данных и множество центров масс кластеров &amp;lt;math&amp;gt;\mu^t_1, ..., \mu^t_k&amp;lt;/math&amp;gt;, полученных с шага инициализации или с предыдущей итерации. Для каждой пары векторов &amp;lt;math&amp;gt;x_i, \mu^t_j, i = 1, ..., n; j = 1,..., k&amp;lt;/math&amp;gt; считается расстояние между ними (вершина &amp;lt;i&amp;gt;dist&amp;lt;/i&amp;gt;). Информационный граф вычисления расстояния между двумя векторами рассмотрен ниже (&amp;lt;i&amp;gt;рисунок 3&amp;lt;/i&amp;gt;). Затем выполняется операция нахождения минимума (вершина &amp;lt;i&amp;gt;min&amp;lt;/i&amp;gt;), в результате которой каждому вектору &amp;lt;math&amp;gt;x_i, i = 1, ..., n&amp;lt;/math&amp;gt; приписывается метка &amp;lt;math&amp;gt;l^t_i&amp;lt;/math&amp;gt;, равная порядковому номеру кластера &amp;lt;math&amp;gt;S^t_j, j = 1 ..., k&amp;lt;/math&amp;gt;, которому принадлежит данный вектор: &amp;lt;math&amp;gt;x_i \in S^t_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k-means-distribute.PNG|800px|thumb|center|Рисунок 2. Схема этапа распределения векторов по кластерам]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 3&amp;lt;/i&amp;gt; представлен информационный граф вычисления расстояния между двумя векторами &amp;lt;math&amp;gt;x_i = (x_{i1}, ..., x_{id}), i = 1, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^t_j = (\mu^t_{j1}, ..., \mu^t_{jd}) , j = 1, ..., k&amp;lt;/math&amp;gt;. Выполняется операция поэлементного вычитания двух векторов (вершины &amp;lt;i&amp;gt;-&amp;lt;/i&amp;gt;), затем возведение в квадрат (вершины &amp;lt;i&amp;gt;sqr&amp;lt;/i&amp;gt;), затем суммирование полученных результатов (вершина &amp;lt;i&amp;gt;+&amp;lt;/i&amp;gt;). На выходе получаем квадрат расстояния между векторами.&lt;br /&gt;
[[file:K-means-distance.PNG|800px|thumb|center|Рисунок 3. Схема вычисления расстояния между двумя векторами]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 4&amp;lt;/i&amp;gt; представлен информационный граф этапа пересчета центров масс кластеров &amp;lt;math&amp;gt;\mu^t_1, \mu^t_2,..., \mu^t_k&amp;lt;/math&amp;gt;. Для каждого кластера &amp;lt;math&amp;gt;S^t_j, j = 1, ..., k&amp;lt;/math&amp;gt; выполняется операция суммирования (вершина &amp;lt;math&amp;gt;sum_j&amp;lt;/math&amp;gt;) векторов, которые принадлежат данному кластеру. Кроме того, для каждого кластера выполняется операция определения количества элементов в нем (вершина &amp;lt;math&amp;gt;size_j&amp;lt;/math&amp;gt;). Затем выполняется операция деления (вершина &amp;lt;i&amp;gt;/&amp;lt;/i&amp;gt;). На выходе получаем новые центры масс кластеров &amp;lt;math&amp;gt;\mu^{t+1}_1, \mu^{t+1}_2,..., \mu^{t+1}_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k-means-update.PNG|800px|thumb|center|Рисунок 4. Схема этапа пересчета центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма имеет большие возможности для параллелизма:&lt;br /&gt;
* Распределение векторов по кластерам: вектора &amp;lt;math&amp;gt;x_i, x_j, i \neq j; i, j = 1, ..., n&amp;lt;/math&amp;gt; могут быть распределены по кластерам независимо друг от друга;&lt;br /&gt;
* Пересчет центров масс кластеров: центры масс кластеров &amp;lt;math&amp;gt;\mu_i, \mu_j, i \neq j; i, j = 1, ..., k&amp;lt;/math&amp;gt; могут быть пересчитаны независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
Однако, этап пересчета центров масс кластеров зависит от этапа распределения векторов по кластерам, так что эти этапы должны выполняться строго последовательно. Таким образом, параллельная сложность алгоритма может быть вычислена следующим образом: &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;N_{k-means} = i * (N_{distr} + N_{update})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - количество итераций, необходимых для сходимости алгоритма, &amp;lt;math&amp;gt;N_{distr}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;N_{update}&amp;lt;/math&amp;gt; - параллельная сложность этапа пересчета центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассчитаем параллельную сложность &amp;lt;math&amp;gt;N_{distr}&amp;lt;/math&amp;gt; этапа &amp;lt;i&amp;gt;распределения векторов по кластерам&amp;lt;/i&amp;gt; в предположении наличия неограниченного числа процессоров для одной итерации. Для каждой пары векторов &amp;lt;math&amp;gt;(x_i, \mu_j), i = 1,...,n; j = 1, ..., k&amp;lt;/math&amp;gt; операция вычисления расстояния выполняется независимо.&lt;br /&gt;
* При выполнении операция вычисления расстояния операции вычитания и умножения выполняются независимо друг от друга, поэтому требуется только одно вычитание и одно умножение. Число операций сложения: &amp;lt;math&amp;gt;N^{+}_{distr} = O(log(d))&amp;lt;/math&amp;gt; согласно [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритму нахождения частичной суммы элементов массива путем сдваивания]]; &lt;br /&gt;
* Для нахождения минимума среди &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов требуется &amp;lt;math&amp;gt;O(log(k))&amp;lt;/math&amp;gt; операций сравнения.&lt;br /&gt;
Таким образом, получаем, что &amp;lt;math&amp;gt;N_{distr} = O(log(k*d))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассчитаем параллельную сложность &amp;lt;math&amp;gt;N_{update}&amp;lt;/math&amp;gt; этапа &amp;lt;i&amp;gt;пересчета центров масс кластеров&amp;lt;/i&amp;gt; в предположении наличия неограниченного числа процессоров для одной итерации. Нахождение центра масс &amp;lt;math&amp;gt;\mu_j, j = 1, ..., k&amp;lt;/math&amp;gt; кластера &amp;lt;math&amp;gt;S_j&amp;lt;/math&amp;gt; может выполняться независимо от нахождения центров масс других кластеров. Таким образом, получаем, что &amp;lt;math&amp;gt;N_{update} \leq O(log(n))&amp;lt;/math&amp;gt; в худшем случае, когда почти все векторы находятся в одном кластере. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итоговая параллельная сложность алгоритма: &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;N_{k-means} \leq i * (log(k*d) + log(n)) \thicksim O(i * log(k*d*n)); i&amp;lt;/math&amp;gt; - количество итераций, необходимых для сходимости.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Входные данные&amp;lt;/i&amp;gt;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов &amp;lt;math&amp;gt;x_i \in R^d, i = 1, ..., n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* число кластеров &amp;lt;math&amp;gt;k \in N_{[1,n]}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Объем входных данных&amp;lt;/i&amp;gt;:&lt;br /&gt;
&amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; действительных чисел, 1 целое положительное число&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Выходные данные&amp;lt;/i&amp;gt;:&lt;br /&gt;
* вектор меток &amp;lt;math&amp;gt;L \in N^n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Объем выходных данных&amp;lt;/i&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;
# Число кластеров является входным аргументом алгоритма. Таким образом, некорректный выбор данного параметра может привести к плохим результатам работы алгоритма, поэтому зачастую проводят несколько прогонов алгоритма с разными значениями &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, чтобы подобрать оптимальный;&lt;br /&gt;
# Алгоритм может сойтись к локальному минимуму (достижение глобального минимума не гарантируется);&lt;br /&gt;
# Результат сильно зависит от выбора начальных значений центров масс кластеров. Существует улучшенная версия алгоритма - &amp;lt;b&amp;gt;k-means++ &amp;lt;/b&amp;gt;&amp;lt;ref&amp;gt;[http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf Arthur, D.; Vassilvitskii, S. (2007). &amp;quot;k-means++: the advantages of careful seeding&amp;quot;. Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms. Society for Industrial and Applied Mathematics Philadelphia, PA, USA. pp. 1027–1035.]&amp;lt;/ref&amp;gt;, которая предлагает свой способ нахождения начальных оптимальных значений центров масс кластеров;&lt;br /&gt;
# Чувствительность к выбросам и шумам, так как они учитываются при вычислении центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
;[[глоссарий#Устойчивость|&#039;&#039;Устойчивость&#039;&#039;]]: Алгоритм является устойчивым к погрешностям во входных данных, так как при вычислении центров кластеров расстояния между объектами усредняются, что приводит к уменьшению ошибки. Алгоритм также является устойчивым к погрешностям, допускаемых при вычислениях, так как операция сложения на каждой новой итерации выполняется над входными данными. &lt;br /&gt;
&lt;br /&gt;
;Сбалансированность: &amp;lt;p&amp;gt;Множество входных векторов можно равномерно разделить между параллельными вычислителями. Центры всех кластеров можно распространить на все вычислители (так как их заведомо меньше, чем число входных векторов). В таком случае на каждой итерации определение для каждого объекта ближайшего кластера представляет собой вычисление расстояния от каждого входного вектора, находящегося на вычислителе, до каждого кластера, и является сбалансированной операцией. После данного этапа происходит пересчёт центров кластеров. Данная операция является несбалансированной в случаях, когда число  объектов в одном из кластеров значительно больше числа объектов в других кластерах, из-за чего один кластер занимает большое число вычислителей, а остальные занимают по одному или несколько вычислителей. В таком случае для данного кластера потребуется вычислить центр [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритмом нахождения частичной суммы элементов массива путем сдваивания]]. Данная операция представляет несбалансированность порядка &amp;lt;math&amp;gt;O(log (k*n))&amp;lt;/math&amp;gt;. После данного шага происходит сбалансированная операция рассылки центров кластеров на все вычислители.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;В идеальном случае параллелизации алгоритм не является сбалансированным, однако в реальных задачах, когда &amp;lt;math&amp;gt;n &amp;gt;&amp;gt; p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - количество вычислителей, эта несбалансированность сглаживается наличием объектов из каждого кластера на каждом вычислителе&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Соотношение последовательной и параллельной сложности: &amp;lt;math&amp;gt;{O(n*d*k*i) \over O(log(n*d*k)*i)} \thicksim O \bigg( {n*d*k \over log(n*d*k)} \bigg)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]: &amp;lt;p&amp;gt;Последовательный алгоритм: &amp;lt;math&amp;gt;{O(n*d*k*i) \over n*d+n+1} \thicksim {O(d*k*i) \over d+1} \thicksim O(k*i)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число входных векторов, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; - размерность векторов, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число кластеров, &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - число итераций, требуемое для сходимости&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Параллельный алгоритм: &amp;lt;math&amp;gt;O(log(n*d*k)*i) \over n*d+n+1&amp;lt;/math&amp;gt;&amp;lt;/p&amp;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;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского Государственного Университета имени М.В. Ломоносова]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Набор данных для тестирования состоял из 946000 векторов размерности 2 (координаты на сфере)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Набор и границы значений изменяемых параметров запуска реализации алгоритма:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* число процессов (виртуальных ядер) [8 : 512];&lt;br /&gt;
* число кластеров [128 : 384].&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон эффективности реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* минимальная эффективность реализации &amp;lt;math&amp;gt;2,47%&amp;lt;/math&amp;gt; достигается при делении исходных данных на 128 кластеров с использованием 512 процессов;&lt;br /&gt;
* максимальная эффективность реализации &amp;lt;math&amp;gt;7,13%&amp;lt;/math&amp;gt; достигается при делении исходных данных на 352 кластера с использованием 8 процессов.&lt;br /&gt;
На рисунках 5 и 6 соответственно представлены графики зависимости производительности и эффективности параллельной реализации k means от числа кластеров  и числа процессов.&lt;br /&gt;
[[file:kmeans_performance.jpg|thumb|center|720px|Рисунок 5. График зависимости производительности параллельной реализации алгоритма от числа кластеров и числа процессов. ]]&lt;br /&gt;
По рис. 5 можно отметить практически полное отсутствие роста производительности с увеличением числа процессов от 256 до 512 при минимальном размере задачи. Это связано с быстрым ростом накладных расходов по отношению к крайне низкому объёму вычислений. При росте размера задачи данный эффект пропадает, и при одновременном пропорциональном увеличении числа кластеров и числа процессов рост производительности становится близким к линейному. &lt;br /&gt;
[[file:kmeans_efficiency.jpg|thumb|center|720px|Рисунок 6. График зависимости эффективности параллельной реализации алгоритма от числа кластеров и числа процессов. ]]&lt;br /&gt;
Исследовалась [https://github.com/like-a-bauss/kmeans параллельная реализация алгоритма k means].&lt;br /&gt;
&amp;lt;p&amp;gt;Были получены следующие оценки масштабируемости реализации алгоритма k means:&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;i&amp;gt;По числу процессов:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;-0.02209&amp;lt;/math&amp;gt;. Следовательно, с ростом числа процессов эффективность уменьшается. На рис. 6 можно наблюдать плавное и равномерное снижение производительности по мере увеличения числа процессов при неизменном числе кластеров, что свидетельствует об относительно невысоком росте накладных расходов на передачу данных между процессами и преобладании объёма вычислений над объёмом пересылок данных по сети.&lt;br /&gt;
*&amp;lt;i&amp;gt;По размеру задачи:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;0.01252&amp;lt;/math&amp;gt;. Следовательно, с ростом размера задачи (числа кластеров) эффективность увеличивается. При этом объём пересылок данных по сети пропорционален &amp;lt;math&amp;gt;(n + k) * p&amp;lt;/math&amp;gt; (где &amp;lt;math&amp;gt;k&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;math&amp;gt;k &amp;lt;&amp;lt; n&amp;lt;/math&amp;gt;, рост накладных расходов с ростом числа кластеров при неизменном числе процессов и входных векторов представляет собой незначительную величину.&lt;br /&gt;
*&amp;lt;i&amp;gt;Общая оценка:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;-0.00081&amp;lt;/math&amp;gt;. Таким образом, с ростом и размера задачи, и числа процессов эффективность уменьшается. Это связано с тем, что отношение объёма вычислений к объёму передаваемых данных изменяется пропорционально &amp;lt;math&amp;gt;{n * k \over (n + k) * p} \thicksim {k \over p}&amp;lt;/math&amp;gt;, что преставляет собой невысокий коэффициент, но при этом позволяет параллельной реализации не деградировать до нулевой эффективности при значительном увеличении числа процессов.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие Open Source реализации алгоритма:&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de/ ELKI] - содержит реализацию алгоритма k-means на языке Java (в том числе реализацию улучшенного алгоритма k-means++)&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka/ Weka] - содержит реализацию k-means на языке Java&lt;br /&gt;
* [http://mahout.apache.org/ Apache Mahout] - содержит реализацию k-means в парадигме MapReduce&lt;br /&gt;
* [https://spark.apache.org/ Spark Mllib] - содержит распределенную реализацию k-means&lt;br /&gt;
* [http://accord-framework.net/ Accord.NET] - содержит реализацию k-means на C# (в том числе реализацию улучшенного алгоритма k-means++)&lt;br /&gt;
* [https://github.com/mlpack/mlpack MLPACK] - содержит реализацию k-means на языке C++&lt;br /&gt;
* [http://opencv.org/ OpenCV] - содержит реализацию k-means на C++. А также есть обертки для языков Python и Java&lt;br /&gt;
* [https://www.scipy.org/ SciPy] - содержит реализацию k-means на языке Python&lt;br /&gt;
* [http://scikit-learn.org/ Scikit-learn] - содержит реализацию k-means на языке Python&lt;br /&gt;
* [http://julialang.org/ Julia] - содержит реализацию алгоритма k-means на языке Julia&lt;br /&gt;
* [https://www.gnu.org/software/octave/ Octave] - содержит реализацию k-means на языке Octave&lt;br /&gt;
* [https://www.r-project.org/ R] - содержит реализацию k-means на языке R&lt;br /&gt;
* [http://torch.ch/ Torch] - содержит реализацию k-means на языке Lua&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19442</id>
		<title>Участник:Sarseev/Алгоритм кластеризации, основанный на минимальном покрывающем дереве</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19442"/>
		<updated>2016-11-29T08:23:50Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Арсеев С. П., группа 620&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Данный алгоритм нацелен на решение задачи кластеризации, то есть, разделения множества объектов на группы, называемые кластерами, так, чтобы внутри одного кластера оказались наиболее похожие (по некоторой метрике) друг на друга объекты, а объекты из разных кластеров как можно сильнее отличались друг от друга.&lt;br /&gt;
Сам алгоритм состоит из следующих шагов:&lt;br /&gt;
# Сведение набора объектов к графу. Объекты становятся вершинами графа, и каждые две вершины соединяются ребром, длина которого равняется расстоянию между этими объектами по некоторой метрике. Выбор метрики зависит от предметной области и желаемых свойств алгоритма. Например, метрикой может служить евклидово расстояние.&lt;br /&gt;
# Построение минимального покрывающего (остовного) дерева в этом графе. Более подробно этот шаг описан в [[Построение_минимального_остовного_дерева_(MST)|соответствующей статье]].&lt;br /&gt;
# Кластеризация путём удаления некоторого количества рёбер в этом дереве. В случае, если заранее известно количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, достаточно удалить &amp;lt;math&amp;gt;k-1&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;n&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, которые нужно кластеризовать. Каждый такой объект можно рассматривать как вектор или элемент некоторого метрического пространства. На пространстве, которому принадлежат &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, задана метрика &amp;lt;math&amp;gt;d_{ij}=d(a_i,a_j)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Первый шаг алгоритма строит граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа &amp;lt;math&amp;gt;(|V|=n)&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество его рёбер &amp;lt;math&amp;gt;(|E| = \frac{n*(n-1)}{2})&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
На втором этапе строится минимальное покрывающее дерево &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt;. После удаления рёбер дерево распадается на поддеревья &amp;lt;math&amp;gt;MST_i&amp;lt;/math&amp;gt;. Вершины, входящие в &amp;lt;math&amp;gt;i&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;
# Преобразование исходных данных. Сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск минимального покрывающего дерева. Последовательная сложность всех трёх основных алгоритмов ([[Алгоритм_Борувки|Борувки]], [[Алгоритм_Крускала|Крускала]] и [[Алгоритм_Прима|Прима]]) &amp;lt;math&amp;gt;O(|E|*log(|E|)) = O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Разбиение на кластеры. Вычислительная сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для простых стратегий разбиения.&lt;br /&gt;
Таким образом, вычислительным ядром алгоритма является алгоритм построения минимального покрывающего дерева, поскольку он имеет наибольшую сложность. Стратегии распараллеливания алгоритма основаны на распараллеливании алгоритма построения минимального покрывающего дерева.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В состав алгоритма входит алгоритм [[Построение_минимального_остовного_дерева_(MST)|построения минимального покрывающего дерева]], являющийся его вычислительным ядром. Также в отдельную часть можно выделить алгоритм разбиения получившегося покрывающего дерева на кластеры.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Описание алгоритма на псевдокоде:&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
MST_Cluster(object[n] objects){&lt;br /&gt;
    g = Pairs(objects, metric);&lt;br /&gt;
    mst = Create_MST(graph);&lt;br /&gt;
    parts = Split_MST(mst);&lt;br /&gt;
    for i in range(parts){&lt;br /&gt;
         clusters[i] = parts[i].vertices;&lt;br /&gt;
    }&lt;br /&gt;
    return clusters&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма определяется самой сложной его частью (см. пункт 1.3) и равна &amp;lt;math&amp;gt;O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:MST_cluster.png|thumb|center|1043px|Информационный граф алгоритма. Показана последовательная реализация (большой прямоугольник) и схема распараллеливания алгоритма построения минимального покрывающего дерева.]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Ресурс параллелизма алгоритма основан на свойстве ассоциативности рёбер: &amp;lt;br&amp;gt;&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес (множество покрывающих деревьев) графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&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;
Сложность параллельного алгоритма зависит от соотношения объёма входных данных и числа процессов. Существуют распределённые алгоритмы построения минимального покрывающего дерева, имеющие асимптотическую сложность &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;(o_1, o_2, ..., o_n)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Выходом алгоритма является множество непересекающихся кластеров: &amp;lt;math&amp;gt;C=(C_1, C_2, ..., C_k); C_i = (o_{i,1}, o_{i,2}, ..., o_{i, n_i}); o(i, l) \ne o(j, m); i \ne j; \forall l, m; \sum{n_i} = n &amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Важным свойством алгоритма является то, что при его распараллеливании по описанной выше стратегии важно правильно выбирать количество процессов. При увеличении количества процессов возрастают затраты на фазу слияния деревьев, полученных каждым процессом: растёт число этапов, в которые производится слияние, а следовательно, возрастают затраты на коммуникацию между процессами, и распределение нагрузки становится более неравномерным из-за того, что часть процессов должна проделать значительно большее количество работы по слиянию деревьев. &amp;lt;br&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма имеет порядок &amp;lt;math&amp;gt;O(n*log(n^2))&amp;lt;/math&amp;gt;. &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;
[[Файл:MST_cluster_time.png|thumb|center|600px|Время работы алгоритма в зависимости от числа процессов и объёма входных данных.]]&lt;br /&gt;
На графике на логарифмической шкале показана зависимость времени работы алгоритма от числа процессов. Приведены измерения для восьми наборов данных, различающихся количеством входных векторов: от 1024 до 131072 элементов. Использовалась собственная реализация алгоритма, основанная на [https://github.com/gpiskas/MinSpanTree_Kruskal_MPI_OpenMP сторонней реализации] алгоритма построения минимального покрывающего дерева (без OpenMP-части для более точного измерения масштабируемости описанной выше архитектуры). Изменения относительно приведённой реализации в основном коснулись генерации входных данных для алгоритма построения остовного дерева: в качестве функции расстояния между элементами при генерации графа бралось случайное число от 0 до 1. Изменения времени работы алгоритма при разных запусках (и, соответственно, с другим набором случайных чисел) незначительны. &amp;lt;br&amp;gt;&lt;br /&gt;
Запуск проводился на аппаратной платформе [http://hpc.cmc.msu.ru/bgp IBM BlueGene/P]. Для каждого размера входных данных были проведены четыре теста: на 128, 256, 512 и 1024 вычислительных узлах соответственно. При дальнейшем увеличении объёма данных время выполнения программы превысило доступный предел (15 минут для 128 узлов). &amp;lt;br&amp;gt;&lt;br /&gt;
Построение отдельного графика эффективности распараллеливания затруднено, т.к. на больших объёмах данных последовательная реализация будет вычисляться слишком долго. Как видно из графика, эффективность распараллеливания падает с увеличением числа вычислительных узлов. Это объясняется возрастающими затратами на коммуникацию и неравномерной загрузкой процессоров на фазе слияния. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Основным вычислительным ядром алгоритма является алгоритм построения минимального покрывающего (остовного) дерева, применяющийся для многих задач, и многие библиотеки ограничиваются его реализацией, без реализации основанного на нём алгоритма кластеризации. Примеры реализаций алгоритмов построения MST: &lt;br /&gt;
* С++:  [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/ Boost graph library] (алгоритмы Борувки и Крускала)&lt;br /&gt;
* C++, MPI: [http://www.boost.org/doc/libs/1_62_0/libs/graph_parallel/doc/html/index.html Parallel boost graph library] (алгоритм Борувки)&lt;br /&gt;
* Java: [http://jgrapht.org/ JGraphT] (алгоритмы Крускала и Прима)&lt;br /&gt;
* Python: [http://docs.scipy.org/doc/scipy-0.15.1/reference/index.html SciPy]&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации &amp;lt;br&amp;gt;&lt;br /&gt;
# C. Kingsford. Minimum Spanning Trees &amp;amp; Clustering ([https://www.cs.umd.edu/class/fall2009/cmsc451/lectures/Lec07a-mst.pdf])&lt;br /&gt;
# Y. Zhou et al. Clustering with Minimum Spanning Trees&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev&amp;diff=19441</id>
		<title>Обсуждение участника:Sarseev</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:Sarseev&amp;diff=19441"/>
		<updated>2016-11-29T08:23:37Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п 2.4&lt;br /&gt;
&lt;br /&gt;
* нет окончательного текста программы&lt;br /&gt;
* &amp;quot;Построение отдельного графика эффективности распараллеливания затруднено, т.к. на больших объёмах данных последовательная реализация будет вычисляться слишком долго.&amp;quot; Разве это не важное свойство алгоритма?? Можно построить графики эффективности только параллельной части и всей программы целиком - прекрасная иллюстрация свойств алгоритма.&lt;br /&gt;
&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 11:23, 29 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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:Guryanovak/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19440</id>
		<title>Участник:Guryanovak/Алгоритм кластеризации, основанный на минимальном остовном дереве</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:Guryanovak/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19440"/>
		<updated>2016-11-29T08:19:29Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
Авторы описания: [[Участник:guryanovak|Гурьянов Алексей Константинович]], [[Участник:Валерия Кибитова|Кибитова Валерия Николаевна]]&lt;br /&gt;
&lt;br /&gt;
Оба автора внесли в данную статью равноценный вклад. Сбор информации, построение алгоритмов, а также составление данной статьи происходило совместно обоими авторами, четкого распределения ответственности произвести невозможно.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на минимальном остовном дереве&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N^2 \log_2 N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O(Nm) &amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &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;
&lt;br /&gt;
В отличии от классических алгоритмов кластеризации, данный алгоритм не предполагает, что кластеры являются сферической формы, и способен выделять кластеры произвольной формы. Данный алгоритм применяется в кластеризации цветов в различных изображениях, например, таких как: фотографии, карты, коммерческой рекламе, а также в различных биологических изображениях.&lt;br /&gt;
&lt;br /&gt;
Проблема нахождения минимального остовного дерева, а также ее решение было предложено Борувка в 1926 году. Идея использовать минимальное остовное дерево для кластеризации впервые была предложена Zahn в 1971&amp;lt;ref&amp;gt;Zahn C. T. Graph-theoretical methods for detecting and describing gestalt clusters //IEEE Transactions on computers. – 1971. – Т. 100. – №. 1. – С. 68-86.&amp;lt;/ref&amp;gt;, в последующие годы данная идея развивалась такими учеными как: Asano, Bhattacharya, Keil, и Yao&amp;lt;ref&amp;gt;Asano T. et al. Clustering algorithms based on minimum and maximum spanning trees //Proceedings of the fourth annual symposium on Computational geometry. – ACM, 1988. – С. 252-257.&amp;lt;/ref&amp;gt;, Avis&amp;lt;ref&amp;gt;Avis D. Diameter partitioning //Discrete &amp;amp; Computational Geometry. – 1986. – Т. 1. – №. 3. – С. 265-276.&amp;lt;/ref&amp;gt;, Eldershaw и Hegland&amp;lt;ref&amp;gt;Eldershaw C., Hegland M. Cluster analysis using triangulation //Computational Techniques and Applications. – 1997. – С. 201-208.&amp;lt;/ref&amp;gt;, Paivinen и другими&amp;lt;ref&amp;gt;Grygorash O., Zhou Y., Jorgensen Z. Minimum spanning tree based clustering algorithms //2006 18th IEEE International Conference on Tools with Artificial Intelligence (ICTAI&#039;06). – IEEE, 2006. – С. 73-81.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основная идея алгоритма состоит из следующих шагов:&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;
В минимальном остовном дереве, выбирается &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; ребро (где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; – это заранее определенное число кластеров), имеющие наибольшой вес. Выбранные ребра удаляются. Оставшиеся k компонент связности считаются кластерами.&lt;br /&gt;
&lt;br /&gt;
В данной статье алгоритм будет описываться с использованием алгоритма Борувки. Подробное описание которого можно найти пройдя по ссылке [[https://algowiki-project.org/ru/Алгоритм_Борувки Алгоритм Борувки]]&lt;br /&gt;
&lt;br /&gt;
Общая идея его состоит в следующем:&lt;br /&gt;
#Изначально каждая вершина графа — тривиальное дерево, а ребра не принадлежат никакому дереву.&lt;br /&gt;
#Для каждого дерева найдем минимальное по весу инцидентное ему ребро. Добавим все такие ребра.&lt;br /&gt;
#Повторяем шаг 2 пока в графе не останется только одно дерево.&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;m&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;(a_{i,1}, a_{i,2}, ..., a_{i,m})&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,m], i \in [1..N] &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;N&amp;lt;/math&amp;gt; чисел от &amp;lt;math&amp;gt;1&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;
&lt;br /&gt;
* &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин.&lt;br /&gt;
* &amp;lt;math&amp;gt;G = (V,E) &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;
* &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt; (Minimum Spanning Tree) - минимальное остовное дерево графа &amp;lt;math&amp;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;
* &amp;lt;math&amp;gt; | x , y | = \sqrt{\sum_i^m (x_i - y_i)^2} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Работу алгоритма можно разделить на три блока:&lt;br /&gt;
*1: Вычисление расстояний между вершинами и заполнение данных о компонентах связности&lt;br /&gt;
*2: Построение минимального основного дерева&lt;br /&gt;
*3: Разделение на K кластеров&lt;br /&gt;
&lt;br /&gt;
Из них основное время работы алгоритма занимают блоки 1 и 2.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
В алгоритме используются следующие структуры данных:&lt;br /&gt;
* Node – структура данных, описывающая вершину пространства. Включает в себя значения координат точки.&lt;br /&gt;
* Edge – структура данных, описывающая узел между двумя вершинами. Включает в себя индексы двух вершин и расстояние между ними&lt;br /&gt;
* Components – класс, описывающий состояние компонент связности в промежуточных состояниях графа по мере добавления в него ребер. Включает в себя информацию о том, какой компоненте связности принадлежит каждая точка и о расстоянии между каждой парой компонент связности, которая равна минимальному ребру между двумя вершинами в этих двух компонентах.&lt;br /&gt;
&lt;br /&gt;
Для описания алгоритма вводятся следующие макрооперации:&lt;br /&gt;
*1: getDistance – находит расстояние между двумя вершинами графа как расстояние между соответствующими им точками пространства. (Сложность – &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*2: initializeComponents – инициализирует состояние класса Components информацией о вершинах и ребрах. В каждую исходную компоненту связности входит одна вершина. (Сложность – &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*3: findMinimalOutgoingEdge – находит ребро c наименьшей длиной, исходящее из заданной компоненты связности. (Cложность – &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*4: findComp  – возвращает индекс компоненты связности в которой находится вершина (Cложность – &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*5: connectComponentsWithEdge – соединяет две компоненты связности, связанные поданным на вход ребром. Объединение компонент включает в себя замену ребер инцидентности новой компоненты на минимальные ребра из пары объединяемых компонент. (Сложность – &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Макрооперации 2-5 относятся к последовательной реализации [[Алгоритм Борувки|алгоритма Борувки]], который был выбран как основа вычислительного ядра алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Схема работы алгоритма была создана с учетом последующей параллельной реализации,&amp;lt;ref&amp;gt;Chung S., Condon A. Parallel implementation of Bouvka&#039;s minimum spanning tree algorithm //Parallel Processing Symposium, 1996., Proceedings of IPPS&#039;96, The 10th International. – IEEE, 1996. – С. 302-308.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
int N;        // Размер входных данных&lt;br /&gt;
int number_of_clusters;        // Число кластеров&lt;br /&gt;
int size = N*N;        // Размер матрицы инцидентности&lt;br /&gt;
Node nodes[N];        // Вершины(точки), заданные начальными условиями&lt;br /&gt;
Edge edges[size];        // Все расстояния между вершинами &lt;br /&gt;
vector&amp;lt;Edge&amp;gt; tree;        // Рёбра минимального остовного дерева&lt;br /&gt;
Components comps;         // Структура компонент связности&lt;br /&gt;
read_data(&amp;amp;nodes, file_name);    // Считывание начальных данных&lt;br /&gt;
&lt;br /&gt;
//----------------Вычисление расстояний между вершинами----------------//&lt;br /&gt;
for(int i = 0; i &amp;lt; N-1; i++) {&lt;br /&gt;
    for(int j = i+1; j &amp;lt; N; j++) {&lt;br /&gt;
        double distance = getDistance(node[i], node[j]);        // Вычисление расстояний между вершинами&lt;br /&gt;
	edges[i*N + j] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
        edges[j*N + i] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//----------------Заполнение данных о компонентах связности--------------//&lt;br /&gt;
initializeComponents(&amp;amp;comps, nodes, edges); &lt;br /&gt;
&lt;br /&gt;
//----------------Построение минимального основного дерева--------------//&lt;br /&gt;
while(comps.size &amp;gt; 1) {&lt;br /&gt;
    vector&amp;lt;edges&amp;gt; mstedges;&lt;br /&gt;
    for component in comps {&lt;br /&gt;
        edge e = findMinimalOutgoingEdge(component, comps, edges) // Нахождение минимального ребра инцидентного данной компоненте&lt;br /&gt;
        mstedges.add(e)&lt;br /&gt;
    }&lt;br /&gt;
    for mstedge in mstedges {&lt;br /&gt;
        if (findComp(comps, mstedge.first) != findComp(comps, mstedge.second)) { &lt;br /&gt;
             connectComponentsWithEdge(comps, mstedge) // Объединение двух компонент&lt;br /&gt;
             tree.add(mstedge) // Добавление ребра в минимальное основное дерево&lt;br /&gt;
        }&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//-----------------------Разделение на K кластеров--------------------------//&lt;br /&gt;
sort(tree)&lt;br /&gt;
for(int i = tree.size() - 1; i &amp;gt; tree.size() - 1 - number_of_clusters; i--) {&lt;br /&gt;
    remove_edge(tree, i);        // Удаление самых длинных рёбер из MST - формирование кластеров&lt;br /&gt;
}&lt;br /&gt;
write_data(tree);        // Запись выходных данных&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Среди выделенных в описании частей алгоритма, самую большую сложность для оценивания временной сложности вызывает часть 3, являющаяся реализацией алгоритма Борувки. Однако если заметить, что после выполнения каждого тела внешнего цикла количество компонент связности будет уменьшаться как минимум вдвое (так как одно ребро в списке ребер минимального основного дерева может повториться максимум двое), то можно сделать вывод, что сложность в худшем случае этого блока алгоритма является &amp;lt;math&amp;gt; O (N^2 \log_2 N) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Асимптотическую сложность алгоритма можно выразить как сумму временных сложностей четырех выделенных в описании частей:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; TC(N, m) = O(N^2m) + O(N^2) + O(N^2 \log_2 N) + O(N \log_2 N) = O(N^2 (m + \log_2 N)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если посчитать размерность пространства входных точек за константу при расчете сложности, то временная сложность выразится как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; TC(N, m) = O(N^2 \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;
[[Файл:InformationGraph-2-8.png|frame|center|alt=Алгоритм| Информационный граф параллельного алгоритма кластеризации на основе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
В то время как узлы каждого слоя не зависят информационно от каждого узла предыдущего слоя, из-за того что структура информационного графа определяется динамически по ходу действия программы (на основе выбора ребер, по которым будет проводится слияние компонент связности), составить информационный граф, использующий более разреженную структуру связей между слоями, не представляется возможным.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Данная задача хорошо параллелизуется благодаря тому, что наиболее требующие вычислительных ресурсов блоки алгоритмического ядра алгоритма можно эффективно разделить по процессам. Процесс считывания входных данных и процесс расчета расстояния между точками, которые представляет собой большое количество попарных взятий евклидова расстояния, можно выполнить за &amp;lt;math&amp;gt;O(1)&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(1)&amp;lt;/math&amp;gt; времени при наличии как минимум &amp;lt;math&amp;gt;N^3&amp;lt;/math&amp;gt; процессоров. Процесс обновления информации о компонентах связности, который заключается в обновлении индексов кластеров вершин и в обновлении расстояний между кластерами, можно представить в виде независимой последовательности операций взятия минимума и операций присваивания в количестве  &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; штук (количество компонент связности), что позволит выполнить ярус, ответственный за обновление структуры компонент связности за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; при наличие как минимум &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; процессоров.  Это значит, что каждый ярус будет выполняться за &amp;lt;math&amp;gt; O(1) &amp;lt;/math&amp;gt; времени, и аналогично последовательному алгоритму Борувки, после выполнения 2 ярусов количество компонент связности уменьшится минимум вдвое, что гарантирует то, что за время выполнения алгоритма повторится не более чем &amp;lt;math&amp;gt; 4 * \log_2 N &amp;lt;/math&amp;gt; ярусов. Учитывая ярусы, посвященные считыванию ввода, вычислению расстояний между точками пространства, удалению &amp;lt;math&amp;gt;K-1&amp;lt;/math&amp;gt; ребер из минимального опорного дерева и выводу ответа, итоговую параллельная сложность алгоритма можно оценить как &amp;lt;math&amp;gt; 2 * \log_2 N + 5&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; векторов размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;(a_{i,1}, a_{i,2}, ..., a_{i,m})&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,m], i \in [1..N] &amp;lt;/math&amp;gt; и количество кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;, на которые необходимо разбить множество точек. Общий объем входных данных можно оценить как &amp;lt;math&amp;gt;O(Nm)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если оценивать размерность пространства входных точек константой, то объем входных данных можно оценить как &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;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;1&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; \frac{N^2 \log_2 N} {\log_2 N} = N^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вычислительную мощность алгоритма можно оценить как &amp;lt;math&amp;gt; \frac {N^2 \log_2 N} {N}   = N \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;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Авторами данной статьи было проведено исследование масштабируемости параллельной реализации алгоритма кластеризации, основанном на минимальном остовном дереве. Исследование алгоритма проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster суперкомпьютерного комплекса Московского университета]. Параллельная реализация была написана с помощью OpenMP. Распараллеливание проводилось по числу объектов кластеризации, поэтому исследование масштабируемости проводилось для числа объектов кластеризации.  Алгоритм исследовался на наборе данных, который был сгенерирован при помощи функции [http://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_blobs.html make_blobs (sklearn)] (количество признаков – 2, количество кластеров – 2).&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 16] с шагом 1;&lt;br /&gt;
*	число входных объектов [100 : 1000] с шагом 100.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] алгоритма кластеризации, основанного на минимальном остовном дереве, в зависимости от изменяемых параметров запуска.&lt;br /&gt;
[[File:Execution_GK_MST.png|800px|thumb|center|Рис.1. Изменение производительности в зависимости от числа процессов и числа входных объектов]]&lt;br /&gt;
[[File:Efficiency_MST_GK.png|800px|thumb|center|Рис.2. Изменение эффективности распараллеливания в зависимости от числа процессов и числа входных объектов]]&lt;br /&gt;
&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;&#039;&#039; - При увеличении числа процессов происходит снижение эффективности на всей области рассматриваемых значений параметров, при этом скорость уменьшения эффективности (производная функции эффективности от количества процессов) убывает на всей области рассматриваемых значений. Это объясняется тем соображением, что на основании формулы [[https://ru.wikipedia.org/wiki/Закон_Амдала закона Амдала]] с &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; последовательной долей времени исполнения и с &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; процессами эффективность распараллеливания можно выразить как &amp;lt;math&amp;gt;E = S / p = \frac{1}{p\alpha + 1 - \alpha}&amp;lt;/math&amp;gt;, что есть гипербола от количества процессов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;При одновременном увеличении количества процессов и размерности системы&#039;&#039;&#039; происходит снижение эффективности, при этом скорость убывания эффективности довольно невысокая. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://bitbucket.org/GuryanovKibitova/mstclusterization/src/ccaffad7780e3aee9645845539e6e1a311295208?at=master Параллельная реализация на языке C++ (+OpenMP)]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Существующих реализаций алгоритма кластеризации в настоящий момент немного. Ниже приводится список некоторых из них:&lt;br /&gt;
*[https://github.com/jakevdp/mst_clustering MST Clustering Python]&lt;br /&gt;
Однако существует множество реализаций алгоритма поиска минимального остовного дерева, на основе которых можно реализовать алгоритм кластеризации.&lt;br /&gt;
*[https://github.com/umarali/boruvka-mst-parallel  Parallel Implementation of Borůvka&#039;s MST Algorithm ]&lt;br /&gt;
*[http://www.mlpack.org/docs/mlpack-2.0.2/doxygen.php?doc=namespacemlpack_1_1emst.html  С++ mlpack MST Algorithm ]&lt;br /&gt;
*[https://github.com/nastra/algorithms-java Java Graph Algorithms]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Kruskal Java MST Algorithm ]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Prim Java MST Algorithm ]&lt;br /&gt;
*[https://github.com/dineshappavoo/GraphMinimumSpanningTree Java Graph Minimum Spanning Tree ]&lt;br /&gt;
*[https://pypi.python.org/pypi/PyAlgDat Python PyAlgDat ]&lt;br /&gt;
*[https://networkx.github.io/documentation/networkx-1.10/reference/algorithms.mst.html Python NetworkX Minimum Spanning Tree]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_53_0/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html C++ Boost Minimum Spanning Tree]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19152</id>
		<title>Участник:Parkhomenko/Алгоритм k средних</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19152"/>
		<updated>2016-11-25T13:27:59Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd&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(t * \log kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Parkhomenko|П.А.Пархоменко]] ([[#Общее описание алгоритма|1.1]], [[#Вычислительное ядро алгоритма|1.3]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Входные и выходные данные алгоритма|1.9]], [[#Свойства алгоритма|1.10]], [[#Масштабируемость алгоритма и его реализации|2.4]]), [[Участник:Ivan.mashonskiy|И.Д.Машонский]] ([[#Математическое описание алгоритма|1.2]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм k средних (англ. k-means) - один из алгоритмов машинного обучения, решающий задачу кластеризации. Он был изобретен в середине 1950-х математиком Гуго Штейнгаузом&amp;lt;ref&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/ref&amp;gt; и Стюартом Ллойдом&amp;lt;ref&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/ref&amp;gt;. Алгоритм стал особо популярным после публикации Маккуина&amp;lt;ref&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/ref&amp;gt;, в которой впервые был использован термин “k-means”.&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации заключается в разбиении N объектов (d-мерных векторов) на K групп (кластеров). Каждый вектор может принадлежать только одному кластеру. Количество кластеров K фиксировано, оно задается в качестве параметра.&lt;br /&gt;
&lt;br /&gt;
K-means является итерационным алгоритмом, разновидностью EM-алгоритма. Основная идея работы алгоритма заключается в проведении некоторого количества итераций, на каждой из которых происходит пересчет центра масс для каждого кластера, полученного на предыдущей итерации. После этого векторы перераспределяются по кластерам: вектор относится к тому кластеру, расстояние до центра масс которого минимально. Алгоритм завершает работу в том случае, если на очередной итерации центры масс кластеров не изменились.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Исходные данные: множество d-мерных векторов (наблюдений) &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;&amp;amp;nbsp;=&amp;amp;nbsp;&amp;lt;math&amp;gt;\{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&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;k&amp;lt;/math&amp;gt; непересекающихся множеств (кластеров) &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, таких что &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt; и значение &amp;lt;math&amp;gt;\sum_{i=1}^{k} \sum_{\mathbf x \in S_i} \left\| \mathbf x - \mu_i \right\|^2 &amp;lt;/math&amp;gt; — минимально для всех возможных наборов &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
Под &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt; здесь подразумевается центр масс векторов из множества &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Первоначальные значения центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt; определяются в начале работы алгоритма случайным образом среди векторов множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм заключается в попеременном применении двух шагов &amp;lt;ref&amp;gt;MacKay, David (2003). &amp;quot;Chapter 20. An Example Inference Task: Clustering&amp;quot; (PDF). Information Theory, Inference and Learning Algorithms. Cambridge University Press. pp. 284–292.&amp;lt;/ref&amp;gt;: &#039;&#039;&#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;S_i^{(t)}&amp;lt;/math&amp;gt;: &lt;br /&gt;
::&amp;lt;math&amp;gt;S_i^{(t)} = \big \{ x_p : \big \| x_p - \mu^{(t)}_i \big \|^2 \le \big \| x_p - \mu^{(t)}_j \big \|^2 \ \forall j, 1 \le j \le k \big\},&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс&#039;&#039;&#039;: на данном шаге вычисляются новые центры масс для кластеров, полученных на предыдущем шаге.&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм завершается, когда на очередном шаге не происходит изменения центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательного алгоритма k-means состоит из двух операций, повторяющихся на каждой итерации алгоритма:&lt;br /&gt;
* пересчета центров масс кластеров;&lt;br /&gt;
* соотнесения каждого вектора к одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
Пересчет центра масс кластера заключается в нахождении d-мерного вектора, являющегося средним арифметическим d-мерных векторов, принадлежащих кластеру. Эту операцию необходимо выполнять для каждого из K кластеров.&lt;br /&gt;
&lt;br /&gt;
Для соотнесения вектора к одному из кластеров, необходимо для каждого из N вектора посчитать расстояние до центров масс всех K кластеров. Для нахождения расстояния между двумя d-мерными векторами p и q используется Евклидова метрика: &amp;lt;math&amp;gt;\sqrt{\sum_{k=1}^d (p_k-q_k)^2}&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;&lt;br /&gt;
&lt;br /&gt;
В начале алгоритма осуществляется инициализация центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;, которая может быть реализована двумя возможными способами &amp;lt;ref&amp;gt;Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot; (PDF). Proceedings of the eleventh international conference on Information and knowledge management (CIKM).&amp;lt;/ref&amp;gt;: &lt;br /&gt;
* случайный выбор центров масс среди исходных наблюдений &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* все наблюдения случайным образом распределяются по кластерам &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, а затем для каждого кластера выполняется операция обновления центра масс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге распределения векторов по кластерам производится операция нахождения квадратов расстояний между векторами кластера и центром масс кластера: &lt;br /&gt;
::&amp;lt;math&amp;gt;\rho = \big \| x_p - \mu^{(t)}_i \big \|^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Обновление центров масс кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге обновления центров масс производится операция суммирования векторов кластера:&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &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;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;t := 1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого вектора &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;m = \underset{j \in \{1, 2, ..., k\}} {\operatorname{arg\,min}} \big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt;, &amp;lt;br/&amp;gt;вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; распределяется в кластер &amp;lt;math&amp;gt;S^{(t)}_m&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого &amp;lt;math&amp;gt;j = 1, 2, ..., k&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
#  Если &amp;lt;math&amp;gt;\mu^{(t)}_j = \mu^{(t+1)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt;, завершить выполнение алгоритма, иначе &amp;lt;math&amp;gt;t := t + 1&amp;lt;/math&amp;gt;, переход на шаг 3.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для вычисления центра массы кластера, содержащего &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; d-мерных векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n_i * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения центров масс всех k кластеров требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;k * d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения расстояния между двумя d-мерными векторами, требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;d - 1&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Для определения кластера для n d-мерного векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность одной итерации:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций умножения/деления&lt;br /&gt;
&lt;br /&gt;
Сложность всего алгоритма, состоящего из &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; итераций: &amp;lt;math&amp;gt;O(t * n * k * d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе описывается информационный граф алгоритма k-средних при &amp;lt;math&amp;gt;n = 3, k = 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 показана общая структура алгоритма: в качестве входных данных выступает матрица, строками которой являются входные векторы &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt;, на выходе получается массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; чисел, где элемент с номером &amp;lt;math&amp;gt;i = 1, 2, 3&amp;lt;/math&amp;gt; обозначает номер кластера, которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. На шаге &amp;lt;math&amp;gt;init&amp;lt;/math&amp;gt; происходит инициализация значений центров масс кластеров, после этого осуществляется итеративное повторение этапов распределения векторов по кластерам (&amp;lt;math&amp;gt;as&amp;lt;/math&amp;gt;) и обновления значений центров масс кластеров (&amp;lt;math&amp;gt;up&amp;lt;/math&amp;gt;). Когда на очередном шаге обновления центров масс кластеров значения центров масс не изменяются, алгоритм завершает работу, и в качестве выходных данных выступают номера кластеров для каждого из входных векторов.&lt;br /&gt;
[[file:k_means_main.png|thumb|center|400px|Рисунок 1. Общая схема работы алгоритма k-средних]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Распределение векторов по кластерам.&#039;&#039;&#039; На рисунке 2 изображен информационный граф для этапа распределения векторов по кластерам. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; и текущие значения центров масс кластеров &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt; (вычисленные на этапе инициализации или предшествующего этапа обновления центров масс кластеров). В качестве выходных данных выступают числа &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_i, i = 1, 2, 3&amp;lt;/math&amp;gt; - номер кластера, содержащего вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; обозначены операции векторной разности, вершинами &amp;lt;math&amp;gt;sn&amp;lt;/math&amp;gt; - операция вычисления квадрата нормы вектора, вершинами &amp;lt;math&amp;gt;am&amp;lt;/math&amp;gt; - операция &amp;lt;math&amp;gt;\underset{i \in \{1, 2\}} {\operatorname{arg\,min}}  p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k_means_info_graph_as.png|thumb|center|400px|Рисунок 2. Граф алгоритма для этапа распределения векторов по кластерам]]&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс кластеров.&#039;&#039;&#039; На рисунке 3 представлен информационный граф для этапа обновления центров масс кластеров. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; с соответствующими номерами кластеров &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, содержащих эти векторы. В качестве выходных данных здесь выступают обновленные значения центров масс кластеров: &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;s_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции суммирования векторов, принадлежащих кластеру &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;cs_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции вычисления выражения &amp;lt;math&amp;gt;1 / |S_i|&amp;lt;/math&amp;gt;, где за &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt; обозначен кластер с номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; обозначена операция произведения.&lt;br /&gt;
[[file:k_means_info_graph_up.png|thumb|center|400px|Рисунок 3. Граф алгоритма для этапа обновления центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм кластеризации k средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (распределения векторов по кластерам и обновления центров масс кластеров) лежат циклы, итерации которых являются информационно независимыми.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
В силу особенностей алгоритма k средних порядок выполнения основных этапов существенен, что не позволяет выполнять их параллельно. Таким образом, итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;T = t * (T_{as} + T_{up})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{as}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;T_{up}&amp;lt;/math&amp;gt; - параллельная сложность этапа обновления центров масс кластеров, &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - общее число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа распределения векторов по кластерам с учетом доступности неограниченного числа процессоров вычисляется следующим образом: так как вычисление значений &amp;lt;math&amp;gt;\big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt; для каждой пары векторов &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^{(t)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt; можно выполнять независимо и для вычисления этого выражения требуется &amp;lt;math&amp;gt;O(\log d)&amp;lt;/math&amp;gt; операций в соответствии с [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения сумм элементов массива сдваиванием]]. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log k)&amp;lt;/math&amp;gt;. Таким образом, сложность этапа распределения векторов по кластерам &amp;lt;math&amp;gt;O(\log kd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа обновления центров масс кластеров вычисляется следующим образом: так как обновление значений каждого из центров масс кластеров можно выполнять независимо, параллельная сложность определяется сложностью обновления центра масс самого большого кластера. В худшем случае для нахождения значения выражения &amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
С учетом вышесказанного, итоговая параллельная сложность алгоритма k средних определяется выражением &amp;lt;math&amp;gt;O(t * \log kdn)&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;k&amp;lt;/math&amp;gt; - количество кластеров;&lt;br /&gt;
* матрица координат векторов &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размерностью &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных&#039;&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; вещественных чисел (если координаты вещественные числа), &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
&#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;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности алгоритма равно &amp;lt;math&amp;gt;\frac{O(tnkd)}{O(t * \log kdn)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Число операций алгоритма k средних оценивается как &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;, суммарный объем входных и выходных данных равен &amp;lt;math&amp;gt;n(d+1)&amp;lt;/math&amp;gt;. Следовательно, вычислительная мощность алгоритма линейна по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и линейна по &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить следующие ключевые особенности алгоритма k средних:&lt;br /&gt;
* Алгоритм прост в реализации и имеет высокую скорость работы по сравнению с другими алгоритмами кластеризации (при удачном выборе начальных значений центров масс кластеров)&lt;br /&gt;
* В качестве метрики используется евклидово расстояние, а в качестве меры разброса кластера используется дисперсия&lt;br /&gt;
* Количество кластеров k является входным параметром алгоритма, поэтому неправильный выбор этого параметра может негативно повлиять на качество кластеризации&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;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование масштабируемости алгоритма k-means в зависимости от количества используемых процессов было проведено в статье Кумара&amp;lt;ref&amp;gt;Kumar, J., Mills, R. T., Hoffman, F. M., &amp;amp; Hargrove, W. W. (2011). Parallel k-means clustering for quantitative ecoregion delineation using large data sets. Procedia Computer Science, 4, 1602-1611.&amp;lt;/ref&amp;gt;. Исследование происходило на суперкомпьютере Jaguar - Cray XT5&amp;lt;ref&amp;gt;https://www.top500.org/system/176029&amp;lt;/ref&amp;gt;. На момент экспериментов данный суперкомпьютер имел следующую конфигурацию: 18,688 вычислительных узлов с двумя шестнадцатиядерными процессорами AMD Opteron 2435 (Istanbul) 2.6 GHz, 16 GB of DDR2-800 оперативной памяти, и SeaStar 2+ роутер. Всего он состоял из 224,256 вычислительных ядер, 300 TB памяти, и пиковой производительностью 2.3 petaflops.&lt;br /&gt;
&lt;br /&gt;
Реализация алгоритма была выполнена на языке программирования C с использованием MPI.&lt;br /&gt;
&lt;br /&gt;
Объем данных составлял 84 гигабайта, количество объектов (d-мерных векторов) n равнялось 1,024,767,667, размерность векторов d равнялась 22, количество кластеров k равнялось 1000. &lt;br /&gt;
&lt;br /&gt;
На рисунке 4 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно отметить, что время, затраченное на чтение данных и запись результатов кластеризации, практически не изменяется с увеличением количества задействованных процессоров. Время же работы самого алгоритма кластеризации уменьшается с увеличением количества процессоров.&lt;br /&gt;
[[Файл:k-means-proc-scalability.png|thumb|center|700px|Рисунок 4. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Также было произведено самостоятельное исследование масштабируемости алгоритма. Исследование производилось на суперкомпьютере &amp;quot;Blue Gene/P&amp;quot;&amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых параметров запуска реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* число процессоров [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];&lt;br /&gt;
* количество объектов [5000, 10000, 25000, 50000].&lt;br /&gt;
&lt;br /&gt;
Был использован набор данных &#039;&#039;Dataset for Sensorless Drive Diagnosis Data Set&#039;&#039;&amp;lt;ref&amp;gt;PASCHKE, Fabian ; BAYER, Christian ; BATOR, Martyna ; MÃ–NKS, Uwe ; DICKS, Alexander ; ENGE-ROSENBLATT, Olaf ; LOHWEG, Volker: Sensorlose ZustandsÃ¼berwachung an Synchronmotoren, Bd. 46. In: HOFFMANN, Frank; HÃœLLERMEIER, Eyke (Hrsg.): Proceedings 23. Workshop Computational Intelligence. Karlsruhe : KIT Scientific Publishing, 2013 (Schriftenreihe des Instituts fÃ¼r Angewandte Informatik - Automatisierungstechnik am Karlsruher Institut fÃ¼r Technologie, 46), S. 211-225&amp;lt;/ref&amp;gt; из репозитория &#039;&#039;Machine learning repository&#039;&#039;&amp;lt;ref&amp;gt;https://archive.ics.uci.edu/ml/datasets/Dataset+for+Sensorless+Drive+Diagnosis&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исследуемый набор данных содержит векторы, размерность которых равна 49. Компоненты векторов являются вещественными числами. Количество кластеров равно 11. Пропущенные значения отсутствуют.&lt;br /&gt;
&lt;br /&gt;
Для исследования масштабируемости алгоритма была использована реализация на языке C с использованием MPI&amp;lt;ref&amp;gt;http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html&amp;lt;/ref&amp;gt;. Код можно найти здесь: https://github.com/serban/kmeans.&lt;br /&gt;
&lt;br /&gt;
На рисунке 5 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно видеть, что при увеличении количества процессоров в N раз, время работы алгоритма уменьшается примерно в N раз (при условии, что количество объектов остается неизменным). Также можно отметить, что при фиксированном количестве процессоров, зависимость времени работы алгоритма от количества объектов также близка к линейной.&lt;br /&gt;
[[Файл:Heatmap_kmeans.png|thumb|center|900px|Рисунок 5. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (при фиксированном количестве объектов, равном 50000). При визуализации была использована логарифмическая шкала. Можно отметить, что при заданном наборе данных можно наблюдать линейную зависимость времени работы алгоритма от количества используемых процессоров, что согласуется с теоретической оценкой сложности последовательной версии алгоритма.&lt;br /&gt;
[[Файл:50000kmeans.png|thumb|center|900px|Рисунок 6. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (количество объектов равно 50000).]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие реализации алгоритма k-средних:&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++, содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [https://github.com/JuliaStats/Clustering.jl Julia k-means] реализация алгоритма &#039;&#039;k&#039;&#039;-средних на языке программирования Julia.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций &#039;&#039;k&#039;&#039;-средних на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация &#039;&#039;k&#039;&#039;-средних на языке R.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:IanaV&amp;diff=19151</id>
		<title>Обсуждение участника:IanaV</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:IanaV&amp;diff=19151"/>
		<updated>2016-11-25T13:27:48Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;Для вычисления производительности в неё был вписан код на MPI, реализующий подсчёт числа операций с плавающей точкой&amp;quot; - вписан Вами? Тогда где эта реализация? Если нет, зачем это отдельно отмечать?&lt;br /&gt;
* К графикам нет внятных пояснений.&lt;br /&gt;
&lt;br /&gt;
Исправления:&lt;br /&gt;
* Ссылка на окончательный текст программы была вписана еще в прошлый раз. Цитата: &amp;quot;Для вычисления производительности в неё был вписан код на MPI, реализующий подсчёт числа операций с плавающей точкой: &amp;lt;b&amp;gt;[https://github.com/like-a-bauss/kmeans окончательный текст программы]&amp;lt;/b&amp;gt;.&amp;quot;. Выделила жирным ссылку, чтобы было видно.&lt;br /&gt;
* Добавлены пояснения.&lt;br /&gt;
&lt;br /&gt;
Вопросы:&lt;br /&gt;
* Эта программа последний раз изменялась несколько лет назад, судя по информации с сайта. Предложение у Вас сформулировано так, как если бы Вами в эту программу &amp;quot;вписан код на MPI, реализующий...&amp;quot;. Если это так, то укажите версию с Вашими правками. Если нет, зачем писать об этой особенности программы?&lt;br /&gt;
* Пояснения весьма скудные, практически повторяющие подписи. Требуется пояснить изменение поведения программы при изменении условий исходя из этих графиков.&lt;/div&gt;</summary>
		<author><name>Zhum</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:Denemmy/Partitioning_Around_Medoids_(%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC)&amp;diff=19024</id>
		<title>Участник:Denemmy/Partitioning Around Medoids (Алгоритм)</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:Denemmy/Partitioning_Around_Medoids_(%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC)&amp;diff=19024"/>
		<updated>2016-11-23T12:04:38Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Partitioning Around Medoids&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(T*K*N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; N*(N-1)/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;
Авторы: [[Участник:Denemmy|Галеев Д.Ф]], [[Участник:zaputlyaev|Запутляев И.]]&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;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм &#039;&#039;&#039;Partitioning Around Medoids&#039;&#039;&#039; (&#039;&#039;&#039;PAM&#039;&#039;&#039;) был создан Леонардом Кауфманом (Leonard Kaufman) и Питером Руссивом (Peter J. Rousseeuw)&amp;lt;ref&amp;gt;Kaufman, L. and Rousseeuw, P.J., Clustering by means of Medoids. Statistical Data Analysis Based on the L1-Norm and Related Methods. Springer US; 1987, p. 405–416.&amp;lt;/ref&amp;gt;. и он очень похож на алгоритм K-means, в основном потому, что оба являются алгоритмами кластеризации, другими словами, оба разделяют множество объектов на группы (кластеры) и работа обоих основана на попытках минимизировать ошибку, но PAM работает с медоидами - объектами, являющимися частью исходного множества и представляющими группу, в которую они включены, а K-means работает с центроидами - искусственно созданными объектами, представляющими кластер.&lt;br /&gt;
&lt;br /&gt;
Алгоритм PAM разделяет множество из N объектов на K кластеров, где и множество объектов, и число K являются входными данными алгоритма. Алгоритм работает с матрицей расстояний, его цель - минимизировать расстояние между представителями каждого кластера и его членами. Алгоритм использует следующую модель для решения задачи:&amp;lt;ref&amp;gt;Fasulo D. An analysis of recent work on clustering algorithms. – Technical report, 1999. – №. 01-03. – С. 02.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F(x)=minimize \sum_{i=1}^{N}\sum_{j=1}^{N} d(i,j)z_{ij}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt; \sum_{i=1}^N {z_{ij} = 1} , j = 1,2,...,N&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;z_{ij} \le y_i , i, j = 1,2,...,N&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
3. &amp;lt;math&amp;gt;\sum_{i=1}^N {y_i = K} , K - &amp;lt;/math&amp;gt; число кластеров;&lt;br /&gt;
&lt;br /&gt;
4. &amp;lt;math&amp;gt;y_i , z_{ij} \in \{ 0,1 \} , i, j = 1,2,...,N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где функция &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; - целевая минимизируемая функция, &amp;lt;math&amp;gt;d(i,j)&amp;lt;/math&amp;gt; - мера расстояния между объектами &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;z_{ij}&amp;lt;/math&amp;gt; - переменная, которая гарантирует, что расстояние только между объектами из одного кластера будет вычислено в целевой функции. Остальные выражения являются следующими ограничениями:&lt;br /&gt;
&lt;br /&gt;
1. Каждый объект принадлежит одному и только одному кластеру;&lt;br /&gt;
&lt;br /&gt;
2. Каждый объект относится к медоиде, представляющей его кластер;&lt;br /&gt;
&lt;br /&gt;
3. Есть в точности K кластеров;&lt;br /&gt;
&lt;br /&gt;
4. Решающая переменная принимает значения 0 или 1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PAM может работать с двумя типами входных данных:&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;
Фаза &#039;&#039;&#039;Build&#039;&#039;&#039;:&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;
&lt;br /&gt;
Фаза &#039;&#039;&#039;Swap&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
4. Для каждого кластера найти объекты, снижающие среднее расстояние, и если такие объекты есть, выбрать те, которые снижают его сильней всего, в качестве медоид;&lt;br /&gt;
&lt;br /&gt;
5. Если хотя бы одна медоида поменялась, вернуться к шагу 3, иначе завершить алгоритм.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&#039;&#039;Обозначения&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;M = \{ x_{m_1}, x_{m_2}, ... , x_{m_s} \} - &amp;lt;/math&amp;gt; множество медоид;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;L = \{ x_{l_1}, x_{l_2}, ... , x_{m_t} \} - &amp;lt;/math&amp;gt; множество объектов &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, не являющихся медоидами;&lt;br /&gt;
&lt;br /&gt;
3. &amp;lt;math&amp;gt;F_{M,L} = \sum_{j = 1}^{N} \min_{ x_{m_q} \in M}  d(x_{m_q},x_{l_j}) - &amp;lt;/math&amp;gt; целевая функция.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
1. Множество &amp;lt;math&amp;gt;X = \{ x_{1}, x_{2}, \dots, x_{N} \}&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, каждый из который задается &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; вещественными значениями;&lt;br /&gt;
&lt;br /&gt;
2. Симметрическая матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, элементы &amp;lt;math&amp;gt;d_{ij} = d(x_i,x_j)&amp;lt;/math&amp;gt; которой являются расстояниями между объектами &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_j&amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
3. Число кластеров &amp;lt;math&amp;gt;K \le N&amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
4. Метрика &amp;lt;math&amp;gt;d_{ij} = d(x_i,x_j)&amp;lt;/math&amp;gt;, задающая расстояние между объектами &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычислительные формулы метода&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Фаза &#039;&#039;&#039;Build&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Представляет из себя выбор K медоид за K последовательных шагов:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;M_0 = \varnothing, L_0 = X&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;x_{m_i} = \arg \min_{x_{l_q} \in L_0} \sum_{j = 1}^N  d(x_{l_q},x_j), L_1 = L_0 \backslash \{x_{m_i}\}, M_1 = M_1 \cup \{x_{m_i}\}, i=1 \dots N &amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Фаза &#039;&#039;&#039;Swap&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Представляет из себя итерационный процесс.&lt;br /&gt;
&lt;br /&gt;
1. Начальное приближение: &amp;lt;math&amp;gt;M_0 = M_{BUILD}, L_0 = L_{BUILD}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2. Множественные вычисления значений целевой функции для различных вариантов замены текущей медоиды на новую:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = arg \min_{i \in M , j \in L} \sum_{y \in L \backslash \{j\}} {min (d(i,y),d(j,y))} &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
3. Изменение множеств в соответствии с выбранной парой &amp;lt;math&amp;gt;(x_{m_t}, x_{l_q})&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M_i = M_{i-1} \backslash \{x_{m_t}\} \cup \{x_{l_q}\} , L_i = L_{i-1} \cup \{x_{m_t}\} \backslash \{x_{l_q}\} &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
4. Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;M = \{ x_{m_{1}}, x_{m_{2}}, ..., x_{m_{K}} \}&amp;lt;/math&amp;gt; - множество медоид;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;K_{m_i} = \{ x_{l_q} \in L \| x_{m_i} = \arg \min_{x_{m_s} \in M}  d(x_{l_q}, x_{m_s}) \}, i=1 \dots K, K -&amp;lt;/math&amp;gt; множество искомых кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма PAM сосредоточено в стадии &#039;&#039;&#039;SWAP&#039;&#039;&#039; и состоит из вычислений значений целевой функции для различных вариантов замены текущей медоиды на новую:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = arg \min_{i \in M , j \in L} \sum_{y \in L \backslash \{j\}} {min (d(i,y),d(j,y))} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Если на вход алгоритма была подана не матрица расстояний, а матрица объектов с их координатами в пространстве &amp;lt;math&amp;gt;R^P&amp;lt;/math&amp;gt;, то операция вычисления расстояния между объектами &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;P&amp;lt;/math&amp;gt; будет являться макрооперацией. В качестве меры расстояния может быть использована евклидова метрика:&lt;br /&gt;
&amp;lt;math&amp;gt; d(a,b) = \sqrt{\sum_{i=1}^P (a_i-b_i)^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Псевдокод алгоритма&#039;&#039; &amp;lt;ref&amp;gt;Park H. S., Jun C. H. A simple and fast algorithm for K-medoids clustering //Expert Systems with Applications. – 2009. – Т. 36. – №. 2. – С. 3336-3341.:&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 1  функция PAM(D, k, tmax=100):&lt;br /&gt;
 2      &amp;lt;span style=&amp;quot;color:#408080&amp;quot;&amp;gt;# D - матрица расстояний, k - число кластеров, tmax - маскимальное число итераций&amp;lt;/span&amp;gt;&lt;br /&gt;
 3      выполнить фазу BUILD, получить множество метоидов M и множество не-метоидов L&lt;br /&gt;
 4      вычислить значение целевой функции F&lt;br /&gt;
 5      для t = 0..tmax-1:&lt;br /&gt;
 6          выполнить фазу SWAP, вычислить значение целевой функции F&#039;&lt;br /&gt;
 7          delta = F - F&#039;&lt;br /&gt;
 8          если delta &amp;gt; 0:&lt;br /&gt;
 9              обновить множества M и L&lt;br /&gt;
 10             F = F&#039;&lt;br /&gt;
 11         иначе:&lt;br /&gt;
 12             выйти из цикла&lt;br /&gt;
 13     вернуть М&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;N&amp;lt;/math&amp;gt;, число итераций алгоритма как &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На стадии &#039;&#039;&#039;BUILD&#039;&#039;&#039; каждый шаг нахождения очередного метоида имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt; по количеству операций сложения вещественных чисел и по количеству операций сравнения двух вещественных чисел.&lt;br /&gt;
&amp;lt;br&amp;gt;Тогда стадия &#039;&#039;&#039;BUILD&#039;&#039;&#039; имеет сложность &amp;lt;math&amp;gt;O(K*N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На стадии &#039;&#039;&#039;SWAP&#039;&#039;&#039; вычисление целевой функции имеет сложность &amp;lt;math&amp;gt;O(K*N^2)&amp;lt;/math&amp;gt; по количеству операций сложения вещественных чисел и по количеству операций сравнения двух вещественных чисел.&lt;br /&gt;
&lt;br /&gt;
Таким образом алгоритм &amp;lt;b&amp;gt;PAN&amp;lt;/b&amp;gt; имеет сложность &amp;lt;math&amp;gt;O(T*K*N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фаза BUILD&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Общий вид информационного графа для шага t представлен на рисунке 1:&lt;br /&gt;
&lt;br /&gt;
[[Файл:BUILD_2.png|thumb|center|800px|Рисунок 1. Информационный граф шага t фазы BUILD алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Операции&#039;&#039;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_M&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_L&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - операции обновления множества метоидов и не-метоидов соответственно&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;SUM&amp;lt;/b&amp;gt; - вычисление функции ошибки, на вход подается расстояния от очередного объекта до всех остальных, а также минимальные расстояния от выбранных на данном шаге метоидов до всех остальных вершин&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;MIN&amp;lt;math&amp;gt;_s&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - нахождение аргумента, соответствующего минимальному значению, а также само минимальное значение&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;MIN&amp;lt;math&amp;gt;_d&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - нахождение минимальных расстояний от медоидов до остальных вершин, используется в операции SUM&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
Количество шагов t равно &amp;lt;b&amp;gt;K&amp;lt;/b&amp;gt;, где &amp;lt;b&amp;gt;K&amp;lt;/b&amp;gt; - число кластеров&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фаза SWAP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Общий вид информационного графа для итерации t представлен на рисунке 2:&lt;br /&gt;
&lt;br /&gt;
[[Файл:SWAP_2.png|thumb|center|800px|Рисунок 2. Информационный граф итерации t фазы SWAP алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Операции&#039;&#039;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_M&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_L&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - операции обновления множества метоидов и не-метоидов соответственно&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;SUM&amp;lt;/b&amp;gt; - вычисление целевой функции&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;MIN&amp;lt;/b&amp;gt; - нахождение аргумента, соответствующего минимальному значению, а также само минимальное значение&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&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;N&amp;lt;/math&amp;gt;. Тогда параллельная сложность фазы &amp;lt;b&amp;gt;BUILD&amp;lt;/b&amp;gt; имеет &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сложения и &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сравнения двух вещественных чисел. Таким образом параллельная сложность фазы &amp;lt;b&amp;gt;BUILD&amp;lt;/b&amp;gt; равна &amp;lt;math&amp;gt;O(K*N*T)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность фазы &amp;lt;b&amp;gt;SWAP&amp;lt;/b&amp;gt; имеет &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сложения и &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сравнения двух вещественных чисел. Параллельная сложность фазы &amp;lt;b&amp;gt;SWAP&amp;lt;/b&amp;gt; равна &amp;lt;math&amp;gt;O(K*N*T)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 Таким образом параллельная сложность алгоритма равна &amp;lt;math&amp;gt;O(K*N*T)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
 * число &amp;lt;math&amp;gt;K&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;N*(N-1)/2&amp;lt;/math&amp;gt;, из данных чисел однозначно восстанавливается симметрическая матрица расстояний &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;Объём входных данных&#039;&#039;: &amp;lt;math&amp;gt;N*(N-1)/2&amp;lt;/math&amp;gt; вещественное число и &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; целых числа.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&lt;br /&gt;
 * K чисел &amp;lt;math&amp;gt;m_1, m_2, ..., m_K&amp;lt;/math&amp;gt; - индексы объектов соответствующие метоидам;&lt;br /&gt;
 * N-K чисел &amp;lt;math&amp;gt;k_1, k_2, ..., k_{N-K}&amp;lt;/math&amp;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;
=== Свойства алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Суммарный объем входных данных равен &amp;lt;math&amp;gt; O(N^2) &amp;lt;/math&amp;gt; , число операций алгоритма &#039;&#039;&#039;PAM&#039;&#039;&#039; равно &amp;lt;math&amp;gt; O(K*N^2*\Tau) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; &amp;amp;ndash; число кластеров, &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; &amp;amp;ndash; число итераций алгоритма, &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt; - число точек. Тогда вычислительная мощность алгоритма &#039;&#039;&#039;PAM&#039;&#039;&#039; равна &amp;lt;math&amp;gt;O(K*\Tau)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Детерминированность |&#039;&#039;Детерминированность&#039;&#039;]] и [[глоссарий#Устойчивость |&#039;&#039;Устойчивость&#039;&#039;]] &amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Алгоритм &amp;lt;i&amp;gt;PAM&amp;lt;/i&amp;gt; является итерационным, количество итераций может быть ограничено сверху, однако в общем случае не фиксируется. Из-за недетермирированности выбора элементов, на которых достигается минимум целевой функции, алгоритм не является &amp;lt;b&amp;gt;детермирированным&amp;lt;/b&amp;gt;. Однако, данный алгоритм является &amp;lt;b&amp;gt;устойчивым&amp;lt;/b&amp;gt;, поскольку не накапливает ошибки в процессе своей работы. &amp;lt;ref&amp;gt;Нейский И. М. Классификация и сравнение методов кластеризации //ББК 32.813 И 76 Составитель: ЮН Филиппович. – 2006. – С. 130.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Сильные стороны алгоритма&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;ul&amp;gt;  &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Меньшая чувствительность к выбросам, чем k-means&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Несложность реализации&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Возможность распараллеливания&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Однако равномерная загрузка процессоров не всегда возможна.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Недостатки алгоритма&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Квадратичная сложность алгоритма&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Количество кластеров является параметром алгоритма&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Во многих задачах число кластеров может быть неизвестным.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Возможность сходимости к локальному оптимуму&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Оптимальное решение не гарантировано.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&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;
Исследование проводилось на суперкомпьютере &#039;&#039;&#039;&amp;quot;Ломоносов&amp;quot;&#039;&#039;&#039; &amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt;. [http://parallel.ru/cluster/lomonosov.html Аппаратные характеристики суперкомпьютера]. Алгоритм реализован с использованием технологии &#039;&#039;&#039;MPI&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Данные были сгенерированы в &#039;&#039;&#039;Matlab&#039;е&#039;&#039;&#039; случайным образом. Количество кластеров равно &#039;&#039;&#039;16&#039;&#039;&#039;, каждый кластер был промоделирован нормальным распределением с некоторыми случайными значениями среднего и среднеквадратического отклонения. Размерность данных равна &#039;&#039;&#039;2&#039;&#039;&#039;, а число точек на каждый кластер было достаточно большим для проведения экспериментов с различными значениями размерности матрицы расстояний. Пример сгенерированных данных показан на рисунке 3.&lt;br /&gt;
&lt;br /&gt;
[[Файл:16_clusters.jpg|thumb|center|512px|Рисунок 3. Пример сгенерированных данных.]]&lt;br /&gt;
&lt;br /&gt;
Измерялось &#039;&#039;&#039;время выполнения&#039;&#039;&#039; стадий &#039;&#039;&#039;BUILD&#039;&#039;&#039; и &#039;&#039;&#039;SWAP&#039;&#039;&#039;, без учета времени, потраченного на построение матрицы расстояний, а также измерялось &#039;&#039;&#039;число итераций&#039;&#039;&#039; алгоритма.&lt;br /&gt;
Были проведены эксперименты для следующих значений размерности матрицы расстояний и числа процессоров:&lt;br /&gt;
&lt;br /&gt;
 Значения размерности: [2500, 2000, 1500, 1000, 500, 200, 150, 100, 50]&lt;br /&gt;
 Значения числа процессоров: [500, 400, 256, 128, 64, 32, 16, 8, 4, 1]&lt;br /&gt;
&lt;br /&gt;
Полученные графики производительности и эффективности представлены на рисунках 4 и 5 соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GFLOPS.png|thumb|center|512px|Рисунок 4. Зависимость производительности реализации параллельного алгоритма от размера матрицы и числа процессоров]]&lt;br /&gt;
[[Файл:EFF.png|thumb|center|512px|Рисунок 5. Зависимость эффективности реализации параллельного алгоритма от размера матрицы и числа процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, из полученных графиков можно сделать вывод о том, что эффективность задачи при фиксированном размере входной матрицы понижается с увеличением числа процессоров, это связано с увеличением передачи данных между процессорами. При фиксированном значениии числа процессоров эффективность увеличивается при увеличении размера входной матрицы, это в свою очередь связано с тем, что доля времени, потраченного на передачу данных между процессорами, от общего затраченного на вычисления времени уменьшается.&lt;br /&gt;
&lt;br /&gt;
В экспериментах была использована сторонняя реализация алгоритма, с небольшими изменениями она выложена в репозитории[https://denemmy@bitbucket.org/denemmy/pam.git]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/ELKI ELKI] реализует несколько вариантов алгоритма кластеризации, включая алгоритм PAM. Написан на Java&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://java-ml.sourceforge.net/ Java-ML]. Включает реализацию k-metoid. Написан на Java&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php?title=Julia_language&amp;amp;action=edit&amp;amp;redlink=1 Julia] содержит реализацию k-metoid в пакете для кластеризации JuliaStats&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/R_(programming_language) R] включает различные варианты k-means в пакете flexclust. Алгоритм PAM реализован в пакете cluster&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/MATLAB MATLAB]. Реализованы PAM, CLARA и другие алгоритмы кластеризации&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/Python_(programming_language) Python]. Алгоритм PAM реализован как k-medoids в пакете pyclust, содержащем также различные варианты k-means&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Avasilenko/Partitioning_Around_Medoids_(PAM)&amp;diff=19023</id>
		<title>Участник:Avasilenko/Partitioning Around Medoids (PAM)</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:Avasilenko/Partitioning_Around_Medoids_(PAM)&amp;diff=19023"/>
		<updated>2016-11-23T12:01:02Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Partitioning Around Medoids&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(T*kn^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n*(n-1)+2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;k+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(T*kn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(k(n-k))&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Авторы&#039;&#039;&#039;:&lt;br /&gt;
* [[Участник:Avasilenko|А.Э.Василенко]] (отвечает за программистскую составляющую);&lt;br /&gt;
* [[Участник:Anastasia|А.В.Тузикова]] (отвечает за математическую составляющую).&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;Rousseeuw P. J., Kaufman L. Finding Groups in Data. – Wiley Online Library, 1990.&amp;lt;/ref&amp;gt;. Более того - это важная часть процесса познания мира, в котором мы живем. Мы учим детей различать кошек и собак, воду и песок, мужчин и женщин путем постоянного развития и улучшения подсознательных схем классификации. Примеры есть и во взрослой жизни – в химии необходимо классифицировать соединения, в археологии – объединять находки по периодам и т.д.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Задача кластеризации&#039;&#039;&#039; - разделение множества объектов на заданное число множеств с минимизацией некоторого функционала - является популярной задачей, относится к разделу машинного обучения и находит применение в таких областях, как анализ текста, биоинформатика, интеллектуальные транспортные системы и др. &amp;lt;ref&amp;gt; Речкалов Т.В. Параллельный алгоритм кластеризации для многоядерного сопроцессора Intel Xeon Phi // Суперкомпьютерные дни в России: Труды vеждународной конференции. – М.: Изд-во МГУ, 2015, c. 532.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует несколько видов кластеризации&amp;lt;ref&amp;gt;Маннинг К.Д., Введение в информационный поиск: пер. с англ. / К.Д. Маннинг, П. Рагхаван, Х.Шютце - М. : Вильямс, 2011. - 528.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;плоская кластеризация&#039;&#039; (&#039;&#039;flat clustering&#039;&#039;) - порождает совокупность кластеров, не имеющих явных взаимосвязей;&lt;br /&gt;
* &#039;&#039;иерархическая кластеризация&#039;&#039; (&#039;&#039;hierarchical clustering&#039;&#039;) - создаёт иерархию кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PAM&#039;&#039;&#039; (&#039;&#039;&#039;Partitioning Around Medoids&#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;
&lt;br /&gt;
&lt;br /&gt;
Модель &amp;lt;ref&amp;gt;Kaufman, L. and Rousseeuw, P.J., Clustering by means of Medoids. In: Y. Dodge and North-Holland, editor. Statistical Data Analysis Based on the L1-Norm and Related Methods. Springer US; 1987, p. 405–416.&amp;lt;/ref&amp;gt;, лежащая в основе алгоритма PAM, основана на выборе &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; объектов, являющихся характерными точками соответствующего кластера. Таким образом, кластерам сопоставляются принадлежащие им объекты, называемые &#039;&#039;&#039;медоидами&#039;&#039;&#039;, на основании которых распределяются остальные объекты по принципу наибольшего сходства. Cама модель называется моделью &#039;&#039;&#039;k-medoids&#039;&#039;&#039; (не путать с моделью &#039;&#039;k-median&#039;&#039;).&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
PAM состоит из двух фаз: &#039;&#039;&#039;BUILD&#039;&#039;&#039; и &#039;&#039;&#039;SWAP&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
*   на фазе BUILD выполняется первичная кластеризация, в ходе которой последовательно выбираются &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; объектов в качестве медоидов;&lt;br /&gt;
*   фаза SWAP представляет собой итерационный процесс, в ходе которого производятся попытки улучшить множество медоидов. Алгоритм выполняет поиск пары объектов (медоид, не-медоид), минимизирующих целевую функцию при замене, после чего обновляет множество медоидов.&lt;br /&gt;
&lt;br /&gt;
На каждой итерации алгоритма выбирается пара (медоид, не-медоид) такая, что замена медоида &amp;lt;math&amp;gt;x_{m_l}&amp;lt;/math&amp;gt; на не-медоид &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt; дает лучшую кластеризацию из возможных. Оценка кластеризации выполняется с помощью целевой функции, вычисляемой как сумма расстояний от каждого объекта до ближайшего медоида:&lt;br /&gt;
 &lt;br /&gt;
:&amp;lt;math&amp;gt;E = \sum_{j = 1}^n \min_{1 \leq l \leq k} \rho ( x_{m_l},x_{o_j} )&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;:&lt;br /&gt;
 &lt;br /&gt;
*   множество кластеризуемых объектов &amp;lt;math&amp;gt;X = \{ x_1, x_2, ... , x_n \} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; - кортеж, состоящий из &amp;lt;math&amp;gt; w &amp;lt;/math&amp;gt; вещественных чисел;&lt;br /&gt;
*   симметрическая матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, элементы матрицы &amp;lt;math&amp;gt;d_{ij}=d(i,j)&amp;lt;/math&amp;gt; - вычисленные расстояния между объектами &amp;lt;math&amp;gt;x_i, x_j&amp;lt;/math&amp;gt;;&lt;br /&gt;
*   количество кластеров &amp;lt;math&amp;gt;k \leq n.&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Обозначения&#039;&#039;:&lt;br /&gt;
*   &amp;lt;math&amp;gt;M = \{ x_{m_1}, x_{m_2}, ... , x_{m_k} \} &amp;lt;/math&amp;gt; - множество медоидов, &amp;lt;math&amp;gt; M \subseteq X ;&amp;lt;/math&amp;gt;&lt;br /&gt;
*   &amp;lt;math&amp;gt;O = \{ x_{o_1}, x_{o_2}, ... , x_{o_r} \} &amp;lt;/math&amp;gt; - множество не-медоидов, &amp;lt;math&amp;gt; O \subseteq X, &amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;r=n-k;&amp;lt;/math&amp;gt;&lt;br /&gt;
*   &amp;lt;math&amp;gt;d_{ij}=\rho (x_i, x_j )&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \rho  : X \times X \rightarrow R &amp;lt;/math&amp;gt; — метрика расстояния;&lt;br /&gt;
*   &amp;lt;math&amp;gt;F_{M,O} = \sum_{j = 1}^{n} \min_{ x_{m_l} \in M} \rho ( x_{m_l},x_{o_j} )&amp;lt;/math&amp;gt; - значение целевой функции для заданных множеств &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
*   &amp;lt;math&amp;gt;T^i&amp;lt;/math&amp;gt; - изменение целевой функции на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге фазы SWAP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;M = \{ x_{m_1}, x_{m_2}, ... , x_{m_k} \}&amp;lt;/math&amp;gt; - множество медоидов;&lt;br /&gt;
* &amp;lt;math&amp;gt;K_{m_i} = \{ x_{o_h} \in O \| x_{m_i} = \arg \min_{x_{m_s} \in M} \rho (x_{o_h}, x_{m_s}) \}, 1 \leq i \leq k&amp;lt;/math&amp;gt; - искомые кластеры.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычислительные формулы метода&#039;&#039;:&lt;br /&gt;
 &lt;br /&gt;
*   &#039;&#039;фаза BUILD&#039;&#039; (состоит из &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;
**   &amp;lt;math&amp;gt;O_0 = X, M_0 = \varnothing ;&amp;lt;/math&amp;gt;&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_1} = \arg \min_{x_{o_h} \in O_0} \sum_{j = 1}^n \rho \left ( x_{o_h},x_j \right ), O_1 = O_0 \backslash \{x_{m_1}\}, M_1 = M_1 \cup \{x_{m_1}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_2} = \arg \min_{x_{o_h} \in O_1} \sum_{j = 1}^{n} \min ( \rho ( x_{m_1},x_j ), \rho ( x_{o_h},x_j ) ), O_2 = O_1 \backslash \{x_{m_2}\}, M_2 = M_1 \cup \{x_{m_2}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_3} = \arg \min_{x_{o_h} \in O_2} \sum_{j = 1}^{n} \min_{1 \leq l \leq 2} ( \rho ( x_{m_l},x_j ) , \rho ( x_{o_h},x_j ) ) , O_3 = O_2 \backslash \{x_{m_3}\}, M_3 = M_3 \cup \{x_{m_3}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
** ...&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_k} = \arg \min_{x_{o_h} \in O_{k-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{k-1}} ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j ) ) , O_k = O_{k-1} \backslash \{x_{m_k}\}, M_k = M_{k-1} \cup \{x_{m_k}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;фаза SWAP&#039;&#039; (итерационный процесс):&lt;br /&gt;
** начальное приближение: &amp;lt;math&amp;gt;O_0 = O_k^{BUILD}, M_0 = M_k^{BUILD};&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й шаг итерации:&lt;br /&gt;
*** вычисление целевой функции при оптимальной замене (замене, при которой достигается лучшая кластеризация из возможных на текущей итерации): &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;F^i = \min_{x_{m_s} \in M_{i-1} , x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j) ); &amp;lt;/math&amp;gt;&lt;br /&gt;
*** определение пары (медоид, не-медоид), на которой достигается оптимальная замена: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt; (x_{m_s}, x_{o_h}) = \arg F^i; &amp;lt;/math&amp;gt;&lt;br /&gt;
*** проверка критерия останова;&lt;br /&gt;
*** изменение множеств в соответствии с выбранной парой &amp;lt;math&amp;gt; (x_{m_s}, x_{o_h}):&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;M_i = M_{i-1} \backslash \{x_{m_s}\} \cup \{x_{o_h}\} , O_i = O_{i-1} \cup \{x_{m_s}\} \backslash \{x_{o_h}\} ;&amp;lt;/math&amp;gt;&lt;br /&gt;
** критерий останова:&lt;br /&gt;
*** &amp;lt;math&amp;gt;T^i = F^{i-1} - F^{i} \leq 0 .&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычислительное ядро алгоритма PAM на этапе BUILD&#039;&#039;&#039; состоит из многочисленных вычислений значений целевой функции при различных вариантах выбора дополнительного медоида (шаг алгоритма &amp;lt;math&amp;gt;1 \leq i \leq k&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{i-1}} ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j ) ) = \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \overline{MIN}_{i, x_{o_h}}^{(j)},&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где:&lt;br /&gt;
* &amp;lt;math&amp;gt;\overline{MIN}_{1, x_{o_h} \in O_0} = \{ \rho (x_{o_h}, x_j ) \| 1 \leq j \leq n \};&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\overline{MIN}_{i, x_{o_h} \in O_{i-1}} = \{ \min ( \overline{MIN}_{i-1, x_{m_{i-1}}}^{(j)}, \rho (x_{o_h}, x_j) ) \| 1 \leq j \leq n \}, \forall i: 1 &amp;lt; i \leq k.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каждой итерацией количество вариантов выбора дополнительных медоидов уменьшается на единицу, таким образом, на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге целевую функцию нужно вычислить лишь &amp;lt;math&amp;gt;|O_{i-1}| = n-i+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;
* таким образом, на каждой последующей итерации можно использовать величину, вычисленную на предыдущем шаге, что позволяет сократить число вычислений минимумов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычислительное ядро алгоритма PAM на этапе SWAP&#039;&#039;&#039; состоит из многочисленных вычислений значений целевой функции при различных вариантах замены некоторого существующего медоида на элемент из числа не-медоидов. &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-я итерация фазы SWAP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{m_s} \in M_{i-1} , x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j) ) =&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;= \min_{x_{m_s} \in M_{i-1} } \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min ( \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )) , \rho ( x_{o_h},x_j) )&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;x_{o_h}&amp;lt;/math&amp;gt;, для которых вычисляется &amp;lt;math&amp;gt;\sum_{j = 1}^{n} \min (a, b)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt; \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )),&amp;lt;/math&amp;gt; и является инвариантом по отношению к &amp;lt;math&amp;gt;\min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min ( \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )) , \rho ( x_{o_h},x_j) ),&amp;lt;/math&amp;gt; что позволяет предвычислить элемент &amp;lt;math&amp;gt;a.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Опишем макрооперации алгоритма&#039;&#039;:&lt;br /&gt;
* в случае если алгоритму PAM на вход подаётся набор точек с их координатами в пространстве &amp;lt;math&amp;gt;R^w,&amp;lt;/math&amp;gt; то необходимо вычислить матрицу расстояний. Для этого может быть использована, например, евклидова метрика:&lt;br /&gt;
*: &amp;lt;math&amp;gt;\rho (p,q)=\sqrt{(p_1-q_1)^2+(p_2-q_2)^2+\dots+(p_w-q_w)^2} = \sqrt{\sum_{j=1}^w (p_j-q_j)^2} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;p, q \in R^w;&amp;lt;/math&amp;gt;&lt;br /&gt;
* нахождение минимального среди значений, принимаемых некоторой функцией &amp;lt;math&amp;gt;value,&amp;lt;/math&amp;gt; определённой на дискретном множестве &amp;lt;math&amp;gt;Y=\{y_i\}, \, i = \overline{(1,r)}:&amp;lt;/math&amp;gt;&lt;br /&gt;
*: &amp;lt;math&amp;gt;minimum_{y \in Y } (value (y)) = \min \Bigg(value(y_1), \min \bigg(value(y_2), \min \Big(\dots , \min(value(y_{r-1}), value(y_{r})) ... \Big) \bigg)\Bigg);&amp;lt;/math&amp;gt;&lt;br /&gt;
* вычисление вектора инвариантов, используемых при оптимизации фазы SWAP (см. вычислительное ядро алгоритма):&lt;br /&gt;
*: &amp;lt;math&amp;gt;\overline{a} = \{ minimum_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l}, x_j ) ) \| 1 \leq j \leq n \},&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;i - &amp;lt;/math&amp;gt;номер итерации;&lt;br /&gt;
* вычисление вектора расстояний от каждой точки заданного множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; до ближайшего медоида из множества &amp;lt;math&amp;gt;M_{i-1} \backslash \{x_{m_s}\},&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;i - &amp;lt;/math&amp;gt;номер итерации фазы SWAP: &lt;br /&gt;
*: &amp;lt;math&amp;gt;\overline{b} = \{ \min (\overline{a}^{(j)}, \rho ( x_{o_p}, x_j )) \| 1 \leq j \leq n \};&amp;lt;/math&amp;gt;&lt;br /&gt;
* сложение компонент вектора: &amp;lt;math&amp;gt;S = \sum_{j = 1}^{n} \overline{f}^{(j)}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Макроструктура вычислительного ядра алгоритма PAM&#039;&#039; может быть выражена через макрооперации следующим образом:&lt;br /&gt;
* для ядра фазы BUILD:&lt;br /&gt;
*::: &amp;lt;math&amp;gt;F^i = minimum_{ x_{o_h} \in O_{i-1} } ( S \, ( \overline{f} (x_{o_h}) )),&amp;lt;/math&amp;gt;&lt;br /&gt;
*::: &amp;lt;math&amp;gt;\overline{f} = \{ \min \,( \overline{MIN}_{i, x_{o_h}}^{(j)}, \rho (x_{o_h}, x_j)) \| 1 \leq j \leq n \}, &amp;lt;/math&amp;gt; где&lt;br /&gt;
*# &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;n-i+1&amp;lt;/math&amp;gt; раз;&lt;br /&gt;
*# &amp;lt;math&amp;gt;minimum&amp;lt;/math&amp;gt; находится &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз при вычислении наилучшего значения целевой функции на текущей итерации.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* для ядра фазы SWAP:&lt;br /&gt;
*::: &amp;lt;math&amp;gt;F^i = minimum_{ x_{m_s} \in M_{i-1} , x_{o_h} \in O_{i-1} } ( S \, ( \overline{b} \, ( x_{o_h}, \overline{a} \, (x_{m_s}) ) )),&amp;lt;/math&amp;gt; где:&lt;br /&gt;
*# &amp;lt;math&amp;gt;minimum&amp;lt;/math&amp;gt; находится &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз  в процессе вычисления макрооперации &amp;lt;math&amp;gt;\overline {a};&amp;lt;/math&amp;gt;&lt;br /&gt;
*# &amp;lt;math&amp;gt;\overline {a}&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз и использует вычисленные на шаге 1 минимальные значения;&lt;br /&gt;
*# &amp;lt;math&amp;gt;\overline {b}&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; раз и использует вычисленные на шаге 2 значения &amp;lt;math&amp;gt;\overline {a};&amp;lt;/math&amp;gt;&lt;br /&gt;
*# &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; раз и использует вычисленные на шаге 3 значения &amp;lt;math&amp;gt;\overline {b};&amp;lt;/math&amp;gt;&lt;br /&gt;
*# &amp;lt;math&amp;gt;minimum&amp;lt;/math&amp;gt; находится &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
 &#039;&#039;&#039;Вход&#039;&#039;&#039;  : Множество объектов &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; или матрица расстояний &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
 &#039;&#039;&#039;Выход&#039;&#039;&#039; : Множество медоидов &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;&lt;br /&gt;
 0  Если на вход было подано множество объектов, необходимо предварительно вычислить матрицу расстояний;&lt;br /&gt;
 1  Инициализация множеств &amp;lt;math&amp;gt;M := \varnothing, O := X;&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;amp;nbsp;&lt;br /&gt;
    // фаза BUILD&lt;br /&gt;
 2  &#039;&#039;&#039;for (int i := 1; i &amp;lt;= k; i++)&#039;&#039;&#039; {&lt;br /&gt;
        // Найти &amp;lt;math&amp;gt;x_{m_i}:&amp;lt;/math&amp;gt;&lt;br /&gt;
        {&lt;br /&gt;
 3          Для всех &amp;lt;math&amp;gt;x_{o_h} \in O&amp;lt;/math&amp;gt; вычислить &amp;lt;math&amp;gt;F_{M \cup \{x_{o_h}\}, O \backslash \{x_{o_h}\}};&amp;lt;/math&amp;gt;&lt;br /&gt;
 4          Положить &amp;lt;math&amp;gt;x_{m_i}&amp;lt;/math&amp;gt; равным &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt;, соответствующему минимальному из вычисленных &amp;lt;math&amp;gt;F;&amp;lt;/math&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
 5      Обновить множества &amp;lt;math&amp;gt;M := M \cup \{x_{m_i}\}, O := O \backslash \{x_{m_i}\};&amp;lt;/math&amp;gt;&lt;br /&gt;
    }&lt;br /&gt;
    // фаза SWAP&lt;br /&gt;
 6  &#039;&#039;&#039;while (1)&#039;&#039;&#039; {&lt;br /&gt;
        // Найти пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h}),&amp;lt;/math&amp;gt; на которой достигается оптимальная замена:&lt;br /&gt;
        {&lt;br /&gt;
 7          Для всех &amp;lt;math&amp;gt;x_{m_i} \in M, x_{o_j} \in O&amp;lt;/math&amp;gt; вычислить &amp;lt;math&amp;gt;F^i;&amp;lt;/math&amp;gt; // вычисление проводить с учётом приведённой ранее оптимизации (см. 1.3 [[#Вычислительное ядро алгоритма|Вычислительное ядро алгоритма]]);&lt;br /&gt;
 8          Положить пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; равной паре &amp;lt;math&amp;gt;(x_{m_i}, x_{o_j})&amp;lt;/math&amp;gt;, соответствующей минимальному из вычисленных &amp;lt;math&amp;gt;F;&amp;lt;/math&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
 9      Вычислить &amp;lt;math&amp;gt;T^i;&amp;lt;/math&amp;gt; // критерий останова (см. 1.2 [[#Математическое описание алгоритма|Математическое описание алгоритма]])&lt;br /&gt;
 10     &#039;&#039;&#039;if&#039;&#039;&#039; &amp;lt;math&amp;gt;(T^i &amp;gt; 0)&amp;lt;/math&amp;gt; {&lt;br /&gt;
 11         Обновить множества &amp;lt;math&amp;gt;M := M \backslash \{x_{m_s}\} \cup \{x_{o_h}\}, O := O \cup \{x_{m_s}\} \backslash \{x_{o_h}\};&amp;lt;/math&amp;gt;&lt;br /&gt;
 12     } &#039;&#039;&#039;else&#039;&#039;&#039; {&lt;br /&gt;
 13         &#039;&#039;&#039;break&#039;&#039;&#039;;&lt;br /&gt;
        }&lt;br /&gt;
    };&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;
* &#039;&#039;&#039;Минимизация кеш-промахов&#039;&#039;&#039;. Матрица расстояний, к которой постоянно выполняется доступ на чтение для вычисления минимумов и их суммирования, должна храниться в памяти как матрица размера &amp;lt;math&amp;gt;n*n&amp;lt;/math&amp;gt;, несмотря на то, что она симметрическая и достаточно хранить лишь половину (элементы над или под главной диагональю). Данная оптимизация потребует использования в 2 раза большего количества памяти для хранения расстояний (а это является главным расходом памяти программы, так как все остальные используемые данные являются временными (локальными для каждой операции) и представляют из себя несколько векторов длинной &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов). Однако так как в данном алгоритме доступ к матрице осуществляется построчно (в  [[#Вычислительное ядро алгоритма|вычислительном ядре алгоритма]] поэлементно ищутся  минимумы 2-х векторов, после чего все они складываются), то в этом случае из матрицы можно будет читать данные по строкам подряд, что снижает количество кеш-промахов. Использование только половины матрицы приводит к частым промахам в кеше в связи с необходимостью загрузки части векторов по строкам, а части - по столбцам.&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;\frac{n*(n-1)}{2}&amp;lt;/math&amp;gt; операций вычисления расстояний;&lt;br /&gt;
* сложность каждой операции вычисления расстояния (для случая использования Евклидовой метрики) равна &amp;lt;math&amp;gt;2*w-1&amp;lt;/math&amp;gt; операций сложения и вычитания, &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; операций возведения в квадрат (умножения) и &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; операции извлечения квадратного корня.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На основании формулы из описания &#039;&#039;&#039;вычислительного ядра фазы BUILD&#039;&#039;&#039; определим сложность выполнения всего этапа.&lt;br /&gt;
&lt;br /&gt;
Обозначим:&lt;br /&gt;
*&amp;lt;math&amp;gt;summs&amp;lt;/math&amp;gt; - операция сложения двух вещественных чисел;&lt;br /&gt;
*&amp;lt;math&amp;gt;mins&amp;lt;/math&amp;gt; - операция нахождения минимума для двух вещественных чисел.&lt;br /&gt;
&lt;br /&gt;
Тогда, количество операций равно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = ( ( (n-1) summs ) * (n-1+1) + (n-1+1-1)mins) + \sum_{i = 2}^{k} ( ( (1 mins)*(n-1) + (n-1) summs ) * (n-i+1) + (n-i+1-1)mins) \Rightarrow&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\Rightarrow S =&lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    S(mins) = ( ( 0 ) * (n-1+1) + (n-1+1-1)mins) + \sum_{i = 2}^{k} ( ( (1 mins)*(n-1)) * (n-i+1) + (n-i+1-1)mins ) &amp;amp; \quad\\&lt;br /&gt;
    S(summs) = \sum_{i = 1}^{k} ( ( (n-1) summs ) * (n-i+1) ) &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
=&lt;br /&gt;
&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;&lt;br /&gt;
=&lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    S(mins) = n^2(k-1) - \frac{1}{2}(nk^2 - nk) + n - k &amp;amp; \quad\\&lt;br /&gt;
    S(summs) = n^2k - \frac{1}{2}(nk^2 + nk - k^2 + k ) &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, сложность фазы BUILD есть &amp;lt;math&amp;gt;O(kn^2)&amp;lt;/math&amp;gt; как по количеству найденных минимумов, так и по количеству операций сложения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На основании формулы из описания &#039;&#039;&#039;вычислительного ядра фазы SWAP&#039;&#039;&#039; определим сложность вычисления целевой функции &amp;lt;math&amp;gt;F^i&amp;lt;/math&amp;gt; с использованием оптимизации процесса нахождения минимума (см. раздел [[#Вычислительное ядро алгоритма|Вычислительное ядро алгоритма]]). Количество операций равно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = ( ( (1 mins)*(n-1) + (n-1) summs )*(n-k) + n*((k-1-1) mins) )*k \Rightarrow&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\Rightarrow S =&lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    S(mins) = ( ((1 mins)*(n-1) )*(n-k) + n*((k-1-1) mins) )*k = kn^2 - 3nk + k^2 &amp;amp; \quad\\&lt;br /&gt;
    S(summs) = ( ( (n-1) summs )*(n-k) )*k = (n-1)(n-k)k &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, сложность одной итерации фазы SWAP есть &amp;lt;math&amp;gt;O(kn^2)&amp;lt;/math&amp;gt; как по количеству найденных минимумов, так и по количеству операция сложения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Полученные оценки позволяют говорить о том, что PAM относится к алгоритмам &#039;&#039;&#039;с квадратичной сложностью&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Основная вычислительная сложность алгоритма PAM приходится на фазу SWAP, т.к. сложность одной её итерации сопоставима со сложностью всей фазы BUILD, однако SWAP может содержать в себе огромное число итераций.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
==== Информационный граф фазы BUILD ====&lt;br /&gt;
 &lt;br /&gt;
Опишем граф фазы BUILD как аналитически, так и в виде рисунка.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Граф состоит из шести групп вершин&#039;&#039;:&lt;br /&gt;
 &lt;br /&gt;
# Операция M, соответствующая &#039;&#039;&#039;первой&#039;&#039;&#039; группе вершин, осуществляет изменение множества медоидов:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M_{It} = M_{It-1} \cup \overline{x}_{m_{It}}, &amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;\overline{x}_{m_{It}} = \overline{x}_{o_{It}}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; (номер итерации), соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{x}_{o_{It}}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из пятой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M_{It-1}&amp;lt;/math&amp;gt; - множество медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - входное данное алгоритма;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It \leq k &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из первой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; k &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = k &amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы BUILD.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция O, соответствующая &#039;&#039;&#039;второй&#039;&#039;&#039; группе вершин, осуществляет изменение множества не-медоидов:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;O_{It} = O_{It-1} \backslash \overline{x}_{o_{It}}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt;, соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{x}_{o_{It}}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из пятой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;O_{It-1}&amp;lt;/math&amp;gt; - множество не-медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - входное данное алгоритма;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It \leq k &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; k &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = k &amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы BUILD.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция, соответствующая &#039;&#039;&#039;третьей&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{c}=\overline{c}_{x_{o_p} } =  \begin{cases} \{ \rho (x_{o_p}, x_j ) \| 1 \leq j \leq n \}, It=1 &amp;amp; \quad\\ \{ \min ( \overline{MIN}^{(j)}, \rho (x_{o_p}, x_j) ) \| 1 \leq j \leq n \}, 1&amp;lt;It \leq k &amp;amp; \quad\\  \end{cases} .&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-It+1],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = 1:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:** &amp;lt;math&amp;gt;\overline{x}_{o_p}&amp;lt;/math&amp;gt; - входное данное алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;1 &amp;lt; It \leq k:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:**&amp;lt;math&amp;gt;\overline{x}_{o_p}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1;&amp;lt;/math&amp;gt;&lt;br /&gt;
#:**&amp;lt;math&amp;gt;\overline{MIN}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из шестой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция S, соответствующая &#039;&#039;&#039;четвертой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;S = \sum_{j = 1}^{n} \overline {c}^{(j)}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-It+1],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{c}_{ x_{o_p} }&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из третьей группы с координатами &amp;lt;math&amp;gt; It, p.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция M1, соответствующая &#039;&#039;&#039;пятой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M1 = (F^{It}; x_{o_{It}}),&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt; \begin{cases}&lt;br /&gt;
F^{It} = \min_{x_{o_p} \in O_{It-1}} S &amp;amp; \quad\\&lt;br /&gt;
x_{o_{It}} = \arg F^{It} &amp;amp; \quad\\&lt;br /&gt;
\end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; - результаты срабатывания операций, соответствующих вершинам из четвертой группы с первой координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;n-It+1&amp;lt;/math&amp;gt; величина.&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; k &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = k &amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы BUILD.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция MIN, соответствующая &#039;&#039;&#039;шестой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{MIN}=\overline{MIN}_{It,x_{m_{It}} } =  \begin{cases}&lt;br /&gt;
\{ \rho (x_{m_{It}            }, x_j ) \| 1 \leq j \leq n \}, It=1 &amp;amp; \quad\\&lt;br /&gt;
\{ \min ( \overline{MIN}^{(j)}, \rho (x_{m_{It}}, x_j) ) \| 1 \leq j \leq n \}, 1&amp;lt;It \leq k &amp;amp; \quad\\&lt;br /&gt;
 \end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k-1],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{MIN}_{It-1,x_{m_{It-1}}} &amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из шестой группы с координатой &amp;lt;math&amp;gt;It-1;&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;x_{m_{It}} =  x_{o_{It}} &amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из пятой группы с координатой &amp;lt;math&amp;gt;It.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный граф в общем случае представлен на Рис.1, отображен шаг с номером &amp;lt;math&amp;gt;It, 1&amp;lt;It&amp;lt;k&amp;lt;/math&amp;gt; фазы BUILD.&lt;br /&gt;
Обозначения:&lt;br /&gt;
# вершины первой группы обозначены фиолетовым цветом и подписаны как &amp;lt;math&amp;gt;M;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины  второй группы обозначены синим цветом и подписаны как &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины третьей группы обозначены жёлтым цветом и подписаны как &amp;lt;math&amp;gt;\overline{c}_{x_{o_p} };&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины четвертой группы обозначены серым цветом и подписаны как &amp;lt;math&amp;gt;S;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины пятой группы обозначены зеленым цветом и подписаны как &amp;lt;math&amp;gt;M1;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины шестой группы обозначены красным цветом и подписаны как &amp;lt;math&amp;gt;MIN;&amp;lt;/math&amp;gt;&lt;br /&gt;
# синим дугам сопоставлены метки, отражающие данные, используемые в соответствующих жёлтых вершинах.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:Pam build 1.png|thumb|center|800px|Рисунок 1. Граф фазы BUILD алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На Рис.2 представлен граф фазы BUILD для случая &amp;lt;math&amp;gt;n=6, k=3.&amp;lt;/math&amp;gt; Вершины, соответствующие операциям, получающим входные данные и производящим выходные данные, увеличены и выделены толстыми границами.&lt;br /&gt;
&lt;br /&gt;
[[file:Pam build 2.png|thumb|center|1100px|Рисунок 2. Граф фазы BUILD алгоритма для n = 6, k = 3. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Информационный граф фазы SWAP ====&lt;br /&gt;
 &lt;br /&gt;
Опишем граф фазы SWAP как аналитически, так и в виде рисунка.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Граф состоит из семи групп вершин&#039;&#039;:&lt;br /&gt;
 &lt;br /&gt;
# Операция M, соответствующая &#039;&#039;&#039;первой&#039;&#039;&#039; группе вершин, осуществляет изменение множества медоидов.&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; (номер итерации), соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; T),&amp;lt;/math&amp;gt; принимая все целочисленные значения, где:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;T\in \N&amp;lt;/math&amp;gt; - количество выполнений фазы SWAP во время работы алгоритма PAM.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* результат срабатывания операции, соответствующей вершине из седьмой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M_{It-1}&amp;lt;/math&amp;gt; - множество медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It &amp;lt; T &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из первой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; T -1 &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = T - 1&amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы SWAP.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция O, соответствующая &#039;&#039;&#039;второй&#039;&#039;&#039; группе вершин, осуществляет изменение множества не-медоидов.&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt;, соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; T),&amp;lt;/math&amp;gt; принимая все целочисленные значения, где:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;T\in \N&amp;lt;/math&amp;gt; - количество выполнений фазы SWAP во время работы алгоритма PAM.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* результат срабатывания операции, соответствующей вершине из седьмой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;O_{It-1}&amp;lt;/math&amp;gt; - множество не-медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It &amp;lt; T &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция, соответствующая &#039;&#039;&#039;третьей&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{a}=\overline{a}_{x_{m_i} } = \{ \min_{ x_{m_l} \in M  \backslash \{x_{m_i}\} }  \rho ( x_{m_l},x_j ) \| 1 \leq j \leq n \}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументом операции является &amp;lt;math&amp;gt;\overline{x}_{m_i}:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = 1&amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из первой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция, соответствующая &#039;&#039;&#039;четвертой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{b}=\overline{b}_{x_{m_i} x_{o_p} } =  \{ \min (\overline{a}_{ x_{m_i}}^{(j)}, \rho ( x_{o_p},x_j )) \| 1 \leq j \leq n \}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{x}_{o_p}:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1&amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{a}_{x_{m_i}}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из третьей группы с координатами &amp;lt;math&amp;gt;It, i.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция S, соответствующая &#039;&#039;&#039;пятой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;S = \sum_{j = 1}^{n} \overline {b}^{(j)}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{b}_{x_{m_i} x_{o_p} }&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из четвертой группы с координатами &amp;lt;math&amp;gt;It, i, p.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция M1, соответствующая &#039;&#039;&#039;шестой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M1 = (F_{x_{o_p}}^{It}; x_{o_p}),&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt; \begin{cases}&lt;br /&gt;
F_{x_{o_p}}^{It} = \min_{x_{o_p} \in O} S &amp;amp; \quad\\&lt;br /&gt;
x_{o_p} = \arg F_{x_{o_p}}^{It} &amp;amp; \quad\\&lt;br /&gt;
\end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; - результаты срабатывания операций, соответствующих вершинам из пятой группы с первыми двумя координатами &amp;lt;math&amp;gt;It, i,&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;n-k&amp;lt;/math&amp;gt; величин.&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция M2, соответствующая &#039;&#039;&#039;седьмой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M2 = (F_{x_{o_p}, x_{m_i}}^{It}; x_{m_i}; x_{o_p}),&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;\begin{cases}&lt;br /&gt;
F_{x_{o_p}, x_{m_i}}^{It} = \min (\min_{x_{m_i} \in M} (F_{x_{o_p}}^{It}), M2_{It-1}) &amp;amp; \quad\\&lt;br /&gt;
x_{m_i} = \arg \min_{x_{m_i} \in M} M1 &amp;amp; \quad\\&lt;br /&gt;
\end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M1 = (F_{x_{o_p}}^{It}; x_{o_p})&amp;lt;/math&amp;gt; - результаты срабатывания операций, соответствующих вершинам из шестой группы с координатами &amp;lt;math&amp;gt;It, i,&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; величин;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M2_{It-1}:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1&amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt; - результаты срабатывания операции, соответствующей вершине из седьмой группы с координатой &amp;lt;math&amp;gt;It-1;&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; T&amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = T&amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы SWAP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный граф в общем случае представлен на Рис.3, отображена одна итерация фазы SWAP.&lt;br /&gt;
Обозначения:&lt;br /&gt;
# вершины первой группы обозначены фиолетовым цветом и подписаны как &amp;lt;math&amp;gt;M;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины второй группы обозначены синим цветом и подписаны как &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины третьей группы обозначены желтым цветом и подписаны как &amp;lt;math&amp;gt;\overline{a}_{x_{m_i} };&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины четвертой группы обозначены оранжевым цветом и подписаны как &amp;lt;math&amp;gt;\overline{b}_{x_{m_i o_p} };&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины пятой группы обозначены серым цветом и подписаны как &amp;lt;math&amp;gt;S;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины шестой группы обозначены зеленым цветом и подписаны как &amp;lt;math&amp;gt;M1;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины седьмой группы обозначены красным цветом и подписаны как &amp;lt;math&amp;gt;M2;&amp;lt;/math&amp;gt;&lt;br /&gt;
# синим дугам сопоставлены метки, отражающие данные, используемые в соответствующих оранжевых вершинах;&lt;br /&gt;
# фиолетовым дугам сопоставлены метки, отражающие данные, используемые в соответствующих жёлтых вершинах.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:Pam swap 1.png|thumb|center|1100px|Рисунок 3. Граф фазы SWAP алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На Рис.4 представлен граф фазы SWAP для случая &amp;lt;math&amp;gt;n=6, k=2, T=3.&amp;lt;/math&amp;gt; Вершины, соответствующие операциям, получающим входные данные и производящим выходные данные, увеличены и выделены толстыми границами.&lt;br /&gt;
&lt;br /&gt;
[[file:Pam swap 2.png|thumb|center|1100px|Рисунок 4. Граф фазы SWAP алгоритма для n = 6, k = 2, T = 3.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При параллельной реализации алгоритма PAM будем рассматривать параллельные варианты фазы BUILD и SWAP ([[#Возможная параллельная реализация алгоритма PAM|возможная параллельная реализация]]). Покажем, что сложность фазы BUILD сопоставима со сложностью одной итерации фазы SWAP.&lt;br /&gt;
&lt;br /&gt;
Оценки последовательной сложности фаз BUILD и SWAP приведены в параграфе &amp;quot;[[#Последовательная сложность алгоритма|Последовательная сложность алгоритма]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Ресурс параллелизма фазы BUILD ====&lt;br /&gt;
&lt;br /&gt;
Для решения задачи кластеризации методом PAM в параллельном варианте фазы BUILD в предположении доступности неограниченного числа процессов для одной итерации необходимо выполнить:&lt;br /&gt;
*  1 ярус с нахождением &amp;lt;math&amp;gt;\overline{c}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt;;&lt;br /&gt;
* 1 ярус с нахождением суммы &amp;lt;math&amp;gt;S:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; операцию сложения;&lt;br /&gt;
* 1 ярус с нахождением значений элемента &amp;lt;math&amp;gt;M1:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n-It&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением минимального элемента &amp;lt;math&amp;gt;\overline{MIN}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It&amp;gt;1;&amp;lt;/math&amp;gt;&lt;br /&gt;
* 1 ярус с изменением множеств медоидов и не-медоидов (не выполняются операции рассматриваемых типов).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таким образом, параллельная сложность одного шага фазы BUILD равна: &lt;br /&gt;
* &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; операция сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;n-It&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It=1;&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;3n-It&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It&amp;gt;1.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность фазы BUILD при числе кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; равна: &lt;br /&gt;
* &amp;lt;math&amp;gt;kn-k&amp;lt;/math&amp;gt; операция сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;3kn - 2n - \frac{1}{2}k^2 - \frac{1}{2}k&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В параллельном варианте, по сравнению с последовательным, при &amp;lt;math&amp;gt;k, n \to \infty&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;O(kn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(kn^2).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Соотношение количества сложений и взятий минимума для фазы BUILD является постоянным и стремится к &amp;lt;math&amp;gt;3,&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;k, n \to \infty.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Максимальная ширина яруса достигается при вычислении &amp;lt;math&amp;gt;\overline {c}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; и равна &amp;lt;math&amp;gt;n-It+1&amp;lt;/math&amp;gt; для шага &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; фазы BUILD. Максимальная ширина яруса при рассмотрении всей фазы есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; и достигается на первом шаге.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Ресурс параллелизма фазы SWAP ====&lt;br /&gt;
&lt;br /&gt;
Для решения задачи кластеризации методом PAM в параллельном варианте фазы SWAP в предположении доступности неограниченного числа процессов для одной итерации необходимо выполнить:&lt;br /&gt;
* 1 ярус с нахождением &amp;lt;math&amp;gt;\overline {a}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n(k-1)&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением &amp;lt;math&amp;gt;\overline {b}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением суммы &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;:&lt;br /&gt;
** &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; операцию сложения;&lt;br /&gt;
* 1 ярус с нахождением минимального элемента &#039;первого&#039; вида &amp;lt;math&amp;gt;(M1 = \min_{x_{o_p} \in O} S):&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n-k-1&amp;lt;/math&amp;gt; операцию нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением минимального элемента &#039;второго&#039; вида &amp;lt;math&amp;gt;(M2 = \min_{x_{m_i} \in M} M1):&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; операцию нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с изменением множеств медоидов и не-медоидов (не выполняются операции рассматриваемых типов).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таким образом, параллельная сложность одной итерации фазы SWAP равна:&lt;br /&gt;
* &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; - по количеству операций сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;kn + n - 2&amp;lt;/math&amp;gt; - по количеству операций нахождений минимума для двух чисел.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В параллельном варианте, по сравнению с последовательным, при &amp;lt;math&amp;gt;k, n \to \infty&amp;lt;/math&amp;gt; количество операций на один порядок меньше по параметру &amp;lt;math&amp;gt;n:&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(kn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(kn^2).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Соотношение количества сложений и взятий минимума для одной итерации фазы SWAP является постоянным и равным &amp;lt;math&amp;gt;k,&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;n \to \infty&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Максимальная ширина яруса достигается при вычислении &amp;lt;math&amp;gt;\overline {b}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; и равна &amp;lt;math&amp;gt;k(n-k).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При использовании выбранной модели, в предположении доступности неограниченного числа вычислителей, фаза BUILD при &amp;lt;math&amp;gt;n \to \infty&amp;lt;/math&amp;gt; требует большего количества операций суммирования на порядок &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, по сравнению с одной итерацией фазы SWAP. В свою очередь, количество операций подсчёта минимума ассимптотически эквивалентны.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
* число &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек упорядоченного множества &amp;lt;math&amp;gt;X;&amp;lt;/math&amp;gt;&lt;br /&gt;
* число &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество требуемых кластеров;&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, состоящих из &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; вещественных чисел, - попарных расстояний между элементами из множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; за исключением расстояний вида &amp;lt;math&amp;gt;\rho (x, x)=0, x \in X;&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;Объём входных данных&#039;&#039;: &amp;lt;math&amp;gt;n(n-1)&amp;lt;/math&amp;gt; вещественное число и &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; целых числа.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Структура хранения данных&#039;&#039;:&lt;br /&gt;
* сразу после считывания входных данных составляется квадратная симметрическая матрица &amp;lt;math&amp;gt;D^{n * n}&amp;lt;/math&amp;gt;, у которой каждый элемент &amp;lt;math&amp;gt;d_{ij}&amp;lt;/math&amp;gt; является вещественным числом - расстоянием &amp;lt;math&amp;gt;\rho (x_i, x_j);&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; целых чисел - индексов принадлежащих ему элементов из множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;, аналогичный массив &amp;lt;math&amp;gt;n-k&amp;lt;/math&amp;gt; целых чисел создаётся для множества не-медоидов &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
* для пересылки данных, как правило, используется тройка &amp;lt;math&amp;gt;(F_{x_{m_s}, x_{o_h}}^{i_j}; x_{m_s}; x_{o_h})&amp;lt;/math&amp;gt; или пара &amp;lt;math&amp;gt;(F_{x_{o_h}}^{i_j}; x_{o_h}),&amp;lt;/math&amp;gt; где:&lt;br /&gt;
** &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; - целое число, обозначающее порядковый номер элемента множества &amp;lt;math&amp;gt;X;&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt; - целое число, обозначающее порядковый номер элемента множества &amp;lt;math&amp;gt;X;&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;F_{x_{m_s}, x_{o_h}}^{i_j}&amp;lt;/math&amp;gt; - вещественное число, равное минимальному значению целевой функции, вычисленному в текущем контексте.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&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;1&amp;lt;/math&amp;gt; вещественное число, являющееся значением целевой функции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Объём выходных данных&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;  целых чисел и &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычислительная мощность&#039;&#039; последовательного алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, равна &amp;lt;math&amp;gt;O(T*k).&amp;lt;/math&amp;gt;&lt;br /&gt;
Вычислительная мощность параллельного алгоритма равна &amp;lt;math&amp;gt;O(T*\frac{k}{n}).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм PAM является &#039;&#039;менее чувствительным&#039;&#039; к выбросам данных, чем алгоритм k-средних. Это связано с использованием медоидов, менее подверженных влиянию таких данных.&amp;lt;ref&amp;gt; Егоров А. В., Куприянова Н. И. Особенности методов кластеризации данных // Известия Южного федерального университета. Технические науки. –2011. T. 124, № 11. –С. 174-177.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Фиксированный способ выбора начальных приближений позволяет говорить о воспроизводимости полученных результатов для последовательной реализации. Для параллельной работы алгоритма подобная воспроизводимость отсутствует, так как из-за &#039;&#039;недетерминированности&#039;&#039; выбора элементов, на которых достигается минимальное значение целевой функции для каждой итерации, итоговое множество медоидов и, следовательно, значение целевой функции может меняться.&lt;br /&gt;
&lt;br /&gt;
Алгоритм PAM &#039;&#039;конечен&#039;&#039;, так как:&lt;br /&gt;
* количество операций фазы BUILD конечно;&lt;br /&gt;
* значение целевой фукнции неотрицательно и на каждой итерации фазы SWAP строго уменьшается.&lt;br /&gt;
&lt;br /&gt;
Одно из преимуществ алгоритма PAM заключается в том, что он может работать с любой матрицей различий. В данном случае принципиальное отличие от матрицы расстояний заключается в том, что для матрицы различий может не выполняться неравенство треугольника, так как в основе её построения не обязательно используется метрика.&lt;br /&gt;
&lt;br /&gt;
Основные операция алгоритма PAM: сложение и нахождение минимума. Операция взятия минимума не подвержена процессу накопления ошибки. На каждой итерации алгоритма сложение производится над значениями, взятыми непосредственно из исходных данных. Таким образом, алгоритм PAM не накапливает ошибок в процессе своей работы и является &#039;&#039;устойчивым&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;math&amp;gt;k(n-k) \, \bmod \, p \neq 0&amp;lt;/math&amp;gt; отсутствует возможность равномерно распределить задачи рассчёта &amp;lt;math&amp;gt;\overline{b}&amp;lt;/math&amp;gt; между процессами. Кроме того, если &amp;lt;math&amp;gt;k \, \bmod \, p \neq 0&amp;lt;/math&amp;gt;, то после их распределения для некоторых процессов окажется необходимым вычислить несколько соседних значений &amp;lt;math&amp;gt;\overline{a}.&amp;lt;/math&amp;gt; Таким образом, &#039;&#039;равномерная&#039;&#039; загрузка процессов &#039;&#039;не всегда возможна&#039;&#039;. Помимо этого, агрегирование данных с целью вычисления глобального минимума приводит к простою незадействованных процессов.&lt;br /&gt;
&lt;br /&gt;
Как и большинство алгоритмов кластеризации, PAM может сходиться к некоторому &#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;
==== Возможная параллельная реализация алгоритма PAM ====&lt;br /&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;M, O&amp;lt;/math&amp;gt; - упорядоченны.&lt;br /&gt;
&lt;br /&gt;
Приведённые далее возможные схемы для параллельных реализаций фазы BUILD и фазы SWAP основаны на максимально равномерном распределении задач между процессами.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Возможная параллельная реализация фазы BUILD =====&lt;br /&gt;
&lt;br /&gt;
Главная задача заключается в распараллеливании многочисленных вычислений целевой функции на каждой итерации фазы BUILD:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{o_h} \in O_{i-1}} F_{x_{o_h}}^i,&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;F_{x_{o_h}}^i = \sum_{j = 1}^{n} \overline{MIN}_{i, x_{o_h}}^{(j)},&amp;lt;/math&amp;gt; где&lt;br /&gt;
: &amp;lt;math&amp;gt;\overline{MIN}_{1, x_{o_h} \in O_0} = \{ \rho (x_{o_h}, x_j ) \| 1 \leq j \leq n \};&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;\overline{MIN}_{i, x_{o_h} \in O_{i-1}} = \{ \min ( \overline{MIN}_{i-1, x_{m_{i-1}}}^{(j)}, \rho (x_{o_h}, x_j) ) \| 1 \leq j \leq n \}, \forall i: 1 &amp;lt; i \leq k.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Схема реализации&#039;&#039;: &amp;lt;br&amp;gt;(далее &amp;lt;math&amp;gt;0 &amp;lt; i \leq k &amp;lt;/math&amp;gt; - номер текущей итерации)&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## положим:&lt;br /&gt;
##: &amp;lt;math&amp;gt;pos = p-(n-i+1) \, \bmod \, p;&amp;lt;/math&amp;gt;&lt;br /&gt;
## на каждой итерации процессу необходимо вычислить &amp;lt;math&amp;gt;r_j&amp;lt;/math&amp;gt; значений целевой функции для соответствующих ему элементов &amp;lt;math&amp;gt;x_{o_h}, ..., x_{o_{h+(r_j-1)}}:&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt; r_j = \begin{cases}&lt;br /&gt;
    \lfloor \frac{n-i+1}{p} \rfloor , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
    \lceil \frac{n-i+1}{p} \rceil , pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt;h = \begin{cases}&lt;br /&gt;
    r_j*j , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
    (r_j-1)*pos + (j-pos)*r_j , pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&amp;lt;/math&amp;gt; - порядковый номер &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# каждому процессу с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## найти минимум среди значений целевой функции &amp;lt;math&amp;gt;F_{x_{o_h}}^i&amp;lt;/math&amp;gt; для каждого из значений параметра &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt; &lt;br /&gt;
##* в данном случае может быть использована оптимизация, описанная в [[#Вычислительное ядро алгоритма|вычислительном ядре алгоритма]];&lt;br /&gt;
## для процессов с номером &amp;lt;math&amp;gt;j \neq 0&amp;lt;/math&amp;gt; переслать процессу с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; пару &amp;lt;math&amp;gt;(x_{o_h}, F_{x_{o_h}}^i)&amp;lt;/math&amp;gt; для всех обработанных &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# для процесса с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;:&lt;br /&gt;
## принять пары от процессов &amp;lt;math&amp;gt;(0, p-1],&amp;lt;/math&amp;gt; в ответ на шаг 2.2;&lt;br /&gt;
## найти &amp;lt;math&amp;gt;x_{o_h},&amp;lt;/math&amp;gt; для которого целевая функция принимает минимальное значение, и положить &amp;lt;math&amp;gt;x_{m_i}:=x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
## вычислить &amp;lt;math&amp;gt;\overline{MIN}_{i, x_{m_i}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
## разослать пару &amp;lt;math&amp;gt;(x_{m_i}, \overline{MIN}_{i, x_{m_i}})&amp;lt;/math&amp;gt; при помощи широковещательной рассылки;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## принять пару &amp;lt;math&amp;gt;(x_{m_i}, \overline{MIN}_{i, x_{m_i}})&amp;lt;/math&amp;gt; в ответ на шаг 3.4;&lt;br /&gt;
## скорректировать локальные копии множеств &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt;, изменив принадлежность вектора &amp;lt;math&amp;gt;x_{m_i}&amp;lt;/math&amp;gt;, а также обновить хранимое значение вектора &amp;lt;math&amp;gt;\overline{MIN}&amp;lt;/math&amp;gt; для подготовки к следующей итерации;&lt;br /&gt;
## перейти к шагу 2.1.&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
После завершения &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; шагов фазы BUILD, множества &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt; (одинаковые для каждого процесса согласно приведенной схеме), а так же значение целевой функции являются выходными данными фазы BUILD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Возможная параллельная реализация фазы SWAP =====&lt;br /&gt;
&lt;br /&gt;
Главная задача заключается в распараллеливании многократного вычисления целевой функции на каждой итерации фазы SWAP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{m_s} \in M_{i-1} } \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min ( \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )) , \rho ( x_{o_h},x_j) )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Схема реализации&#039;&#039;: &amp;lt;br&amp;gt;(далее &amp;lt;math&amp;gt;i \in \N&amp;lt;/math&amp;gt; - номер текущей итерации)&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## положим:&lt;br /&gt;
##: &amp;lt;math&amp;gt;pos = p-(k*(n-k)) \, \bmod \, p;&amp;lt;/math&amp;gt;&lt;br /&gt;
## на каждой итерации процессу необходимо вычислить &amp;lt;math&amp;gt;r_j&amp;lt;/math&amp;gt; значений целевой функции для соответствующих ему пар вида (медоид, не-медоид)   &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h}), (x_{m_s}, x_{o_{h+1}}), ..., (x_{m_s}, x_{o_{n-k}}), (x_{m_{s+1}}, x_{o_0}), (x_{m_{s+1}}, x_{o_1}), . . . \, : &amp;lt;/math&amp;gt;&lt;br /&gt;
##:&amp;lt;math&amp;gt; r_j =   \begin{cases}&lt;br /&gt;
    \lfloor \frac{k*(n-k)}{p} \rfloor , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
    \lceil \frac{k*(n-k)}{p} \rceil , pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt;s = \begin{cases}&lt;br /&gt;
    (r_j*j) \mbox{ div } (n-k) , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
     ((r_j-1)*pos + r_j*(j-pos)) \mbox{ div } (n-k), pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&amp;lt;/math&amp;gt; - порядковый номер &amp;lt;math&amp;gt;x_{m_s};&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt;h = \begin{cases}&lt;br /&gt;
    (r_j*j) \, \bmod \,  (n-k) , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
     ((r_j-1)*pos + r_j*(j-pos)) \, \bmod \,  (n-k), pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&amp;lt;/math&amp;gt; - порядковый номер &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# каждому процессу с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## найти минимум среди вычисленных значений целевой функции для каждого из значений первого параметра &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; по соответствующим ему &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
##* в данном случае может быть использована оптимизация, описанная в [[#Вычислительное ядро алгоритма|вычислительном ядре алгоритма]];&lt;br /&gt;
## переслать процессу с номером &amp;lt;math&amp;gt;N = s \, \bmod \, p&amp;lt;/math&amp;gt; (если &amp;lt;math&amp;gt;N \neq j&amp;lt;/math&amp;gt;) тройку &amp;lt;math&amp;gt;(F_{x_{m_s}, x_{o_h}}^{i_j}, x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; для всех обработанных &amp;lt;math&amp;gt;x_{m_s};&amp;lt;/math&amp;gt;&lt;br /&gt;
# для процессов с номерами &amp;lt;math&amp;gt;j&amp;lt;\min (p, k):&amp;lt;/math&amp;gt;&lt;br /&gt;
## принять описанные выше тройки от процессов, обработавших элементы &amp;lt;math&amp;gt;x_{m_{z \, \bmod \, p = j}}, z = \overline {0, k-1},&amp;lt;/math&amp;gt; в ответ на шаг 2.2;&lt;br /&gt;
## вычислить минимальное среди полученных значений целевой функции;&lt;br /&gt;
## запомнить &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt;, на которых минимальное значение было достигнуто;&lt;br /&gt;
## при &amp;lt;math&amp;gt;j \neq 0 &amp;lt;/math&amp;gt; отправить тройку &amp;lt;math&amp;gt;(F_{x_{m_s}, x_{o_h}}^{i_j}, x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt;, соответствующую выбранной паре, процессу с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для процесса с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;:&lt;br /&gt;
## принять тройки от процессов &amp;lt;math&amp;gt;(0, min(k-1, p-1)],&amp;lt;/math&amp;gt; в ответ на шаг 3.4;&lt;br /&gt;
## найти пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt;, для которой целевая функция принимает минимальное значение;&lt;br /&gt;
## сравнить найденное минимальное значение с вычисленным значением целевой функции на предыдущей итерации фазы SWAP;&lt;br /&gt;
### (&#039;&#039; &#039;&#039;&#039;условие останова&#039;&#039;&#039; &#039;&#039;) если значение целевой функции не уменьшилось, остановить вычисление;&lt;br /&gt;
### иначе, разослать выявленную пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; при помощи широковещательной рассылки;&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## принять пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; в ответ на шаг 4.3.2;&lt;br /&gt;
## скорректировать локальные копии множеств &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt;, заменяя элемент &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; на элемент &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt; и наоборот;&lt;br /&gt;
## перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Ограничения и недостатки =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Ограничения&#039;&#039;:&lt;br /&gt;
* предполагается, что матрица расстояний &amp;lt;math&amp;gt;D^{n * n}&amp;lt;/math&amp;gt; полностью помещается в памяти каждого процесса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Недостатки&#039;&#039;:&lt;br /&gt;
* при распределении обсчитываемых целевых функций, каждому из процессов ставится в соответствие некоторая совокупность пар &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt;, для которых нужно рассчитать значения целевой функции. В случае, если количество процессов не кратно количеству обрабатываемых кластеров, различные процессы получат пары у которых совпадают &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt;, что приведёт к совершению одинаковых вычислений на различных процессах: &amp;lt;math&amp;gt;   \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )), 1 \leq j \leq n;&amp;lt;/math&amp;gt;&lt;br /&gt;
* вычисление фазы SWAP успешно распараллеливается между &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; процессами, однако после этого происходит агрегация данных в 2 этапа относительно &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; для вычисления глобального минимума. На первом этапе данные собираются на первых &amp;lt;math&amp;gt;\min (p,k)&amp;lt;/math&amp;gt; процессах, где происходят локальные вычисления минимумов среди принятых данных (для каждого &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt;), после чего полученные минимумы и связанная с ними информация агрегируются на мастер-процессе, который вычисляет глобальный минимум и извещает о нём все остальные процессы. &amp;lt;br&amp;gt;Во время описанных вычислений все незадействованные процессы простаивают.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Дополнительные возможности&#039;&#039;:&lt;br /&gt;
* помимо описанной схемы, также есть возможность распараллелить операцию вычисления матрицы расстояний &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, однако это является отдельной алгоритмической задачей и не даёт существенного прироста производительности алгоритма.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Существующие параллельные реализации алгоритма PAM ====&lt;br /&gt;
&lt;br /&gt;
Примером другой параллельной реализации алгоритма PAM может служить реализация в пакете SPRINT для языка R (подробнее см. [[#Существующие реализации алгоритма|существующие реализации алгоритма]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Структура графа алгоритма (см. 1.7 [[#Информационный граф| Информационный граф алгоритма]]) такова, что позволяет надеяться на получение хорошей масштабируемости.&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма Partitioning Around Medoids проводилось на суперкомпьютере BlueGene/P &amp;lt;ref&amp;gt;IBM BlueGene/P  - [http://hpc.cmc.msu.ru http://hpc.cmc.msu.ru]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
Отметим, что для данному суперкомпьютеру свойственна стабильность по времени вычисления одной и той же задачи при различных запусках.&lt;br /&gt;
Во время вычислений каждому MPI процессу соответствовало 1 ядро процессора PowerPC 450, 850 MHz с пиковой производительностью 3.4 GFlop/sec per core.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма:&lt;br /&gt;
* число процессов (p):&lt;br /&gt;
** [1; 4 : 512] с шагом 4 процесса;&lt;br /&gt;
* размер кластеризуемого множества (n):&lt;br /&gt;
** [4; 16; 32; 64];&lt;br /&gt;
** [128 : 3200] с шагом 128 элементов;&lt;br /&gt;
* каждому размеру кластеризуемого множества соответствует количество кластеров для разбиения (k):&lt;br /&gt;
** [2; 4; 4; 8];&lt;br /&gt;
** [2 : 50] с шагом 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Особенности проведения эксперимента:&lt;br /&gt;
* тестовые данные являются псевдослучайными;&lt;br /&gt;
* для всех экспериментов использовался одинаковый набор данных;&lt;br /&gt;
* в соответствии с описанными параметрами запуска, для множеств размерности [128 : 3200], количество кластеров (k) прямо пропорционально размеру задачи (n).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен широкий диапазон [[Глоссарий#Эффективность распараллеливания|эффективности реализации]] алгоритма:&lt;br /&gt;
* минимальная эффективность реализации: &amp;lt;math&amp;gt;3.4 * 10^{-9} \, %&amp;lt;/math&amp;gt; достигается при использовании 512 процессов для множества из 4 элементов и 2-х кластеров (результат вызван избыточностью количества процессов по отношению к размеру задачи);&lt;br /&gt;
* максимальная эффективность реализации: &amp;lt;math&amp;gt;4.1 * 10^{-4} \, %&amp;lt;/math&amp;gt; достигается при использовании 4 процессов для множества из 128 элементов и 2 кластеров.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Абсолютная величина [[Глоссарий#Производительность|производительности алгоритма]] (GFlops) определялась как количество операций с плавающей точкой за секунду. Точное количество операций было вычислено на основании [[#Последовательная сложность алгоритма| последовательной сложности алгоритма]], целочисленные операции и операции работы с памятью не учитывались.&lt;br /&gt;
&lt;br /&gt;
Таким образом, производительность обусловлена как самим алгоритмом (его внутренняя структура определяет соотношение между операциями работы с памятью, операциями над целочисленными значениями и значениями с плавающей точкой), так и используемой реализацией.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики производительности и эффективности выбранной реализации PAM в зависимости от изменяемых параметров запуска:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|style=&amp;quot;margin: 0 auto;&amp;quot;&lt;br /&gt;
 | [[file:PAM-gflops.png|thumb|none|600px|Рисунок 5а. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества.]]&lt;br /&gt;
 | [[file:PAM-gflops-top-view.png|thumb|none|600px|Рисунок 5б. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества. Вид сверху.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
[[file:PAM-gflops.png|thumb|center|800px|Рисунок 5а. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества.]]&lt;br /&gt;
[[file:PAM-gflops-top-view.png|thumb|center|800px|Рисунок 5б. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества. Вид сверху.]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
[[file:PAM-efficiency.png|thumb|center|800px|Рисунок 6. Параллельная реализация алгоритма PAM. Изменение эффективности в зависимости от числа процессов и размера множества.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На рисунке 5б, видно, что пики организованы в прямые. Это обусловлено инвариантностью величины &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; в соответствии с описанной в [[#Вычислительное ядро алгоритма | вычислительном ядре фазы SWAP]] оптимизацией. На рисунке 3 для [[#Информационный граф фазы SWAP| информационного графа фазы SWAP]] наглядно видно, что при построчном распределении вычислений &amp;lt;math&amp;gt;\overline{b}_{x_{m_s, o_h}}&amp;lt;/math&amp;gt; возможно возникновение следующих ситуаций (далее при &#039;&#039; &#039;&#039;&#039;k * (n-k) mod p == 0&#039;&#039;&#039; &#039;&#039; обозначим &#039;&#039; &#039;&#039;&#039;r = k * (n-k) / p&#039;&#039;&#039; &#039;&#039;):&lt;br /&gt;
# при &#039;&#039; &#039;&#039;&#039;r &amp;lt; (n-k)&#039;&#039;&#039; &#039;&#039;, если &#039;&#039; &#039;&#039;&#039;(n-k) mod r == 0&#039;&#039;&#039; &#039;&#039;, то каждому процессу необходимо вычислить ровно одну величину &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}};&amp;lt;/math&amp;gt;&lt;br /&gt;
# при &#039;&#039; &#039;&#039;&#039;r &amp;amp;ge; (n-k)&#039;&#039;&#039; &#039;&#039;, если &#039;&#039; &#039;&#039;&#039;k mod p == 0&#039;&#039;&#039; &#039;&#039;, то каждому процессу необходимо вычислить несколько соответствующих ему величин &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}},&amp;lt;/math&amp;gt; при этом каждая величина будет вычислена ровно в одном процессе;&lt;br /&gt;
# в остальных случаях вычисления величин &amp;lt;math&amp;gt;\overline{b}_{x_{m_s, o_h}}&amp;lt;/math&amp;gt; распределяются между процессами таким образом, что часть процессов обрабатывает больше величин &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}}&amp;lt;/math&amp;gt; нежели остальные (ситуация возникает при обработке процессом последних элементов одной строки и первых элементов следующей). Подобная ситуация приводит к дополнительным вычислениям на некоторых процессах и, как следствие, к простою остальных, в связи с чем происходит падение производительности.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Первые два случая приводят к равномерному распределению нагрузки между процессами, и им соответствуют видимые пики на приведённых рисунках 5a, 5б, 6. Для 3-го случая из-за неравномерной загрузки процессов достигаемая производительность падает.&lt;br /&gt;
&lt;br /&gt;
Второй случай соответствует размеру задачи меньшему, чем количество процессов и на практике маловероятен.&lt;br /&gt;
Таким образом, для повышения производительности следует выбирать количество процессов так, чтобы &#039;&#039; &#039;&#039;&#039;k * (n-k)&#039;&#039;&#039; &#039;&#039; и &#039;&#039; &#039;&#039;&#039;(n-k)&#039;&#039;&#039; &#039;&#039; были кратны &#039;&#039; &#039;&#039;&#039;p&#039;&#039;&#039; &#039;&#039; и &#039;&#039; &#039;&#039;&#039;k * (n-k) div p&#039;&#039;&#039; &#039;&#039; соответственно.&lt;br /&gt;
&lt;br /&gt;
Для исследуемой реализации, вычисление величин &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}}&amp;lt;/math&amp;gt; может быть оптимизировано (за счёт распределения между процессами и последующих пересылок). Однако принципиальный вид графиков останется тем же: пики будут наблюдаться для случаев равномерного распределения вычислительных задач между процессами.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Построим оценки масштабируемости выбранной реализации алгоритма PAM:&lt;br /&gt;
* По числу процессов: &amp;lt;math&amp;gt;- \, 1.6 * 10^{-8}.&amp;lt;/math&amp;gt; С ростом числа процессов эффективность на рассмотренной области изменений параметров запуска уменьшается. Этот результат вызван ростом времени, затрачиваемого на пересылку данных, то есть ростом накладных расходов на организацию параллельного выполнения.&lt;br /&gt;
* По размеру задачи: &amp;lt;math&amp;gt;3.9 * 10^{-7}&amp;lt;/math&amp;gt;. С ростом размера задачи эффективность на рассмотренной области изменений параметров запуска увеличивается. Этот результат вызван увеличением количества вычислений относительно объема пересылок данных.&lt;br /&gt;
* Общая оценка масштабируемости: &amp;lt;math&amp;gt;- \, 1.2 * 10^{-9}&amp;lt;/math&amp;gt;. При рассмотрении области параметров запуска в целом можно говорить о том, что с ростом размера задачи и числа процессов эффективность незначительно падает.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://github.com/phonexicum/pam-mpi-cmc Исследованная параллельная реализация на языке C++] (реализация написана самостоятельно)&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Последовательная реализация алгоритма PAM в пакете ClusterR на языке R:&lt;br /&gt;
#* Лицензия: MIT&lt;br /&gt;
#* [https://cran.r-project.org/package=ClusterR веб-страница фреймворка (исходный код, системные требования и справочное руководство)]&lt;br /&gt;
#* Алгоритм PAM реализован в функции &#039;Cluster_Medoids&#039;. Отличительная особенность реализации &amp;lt;ref&amp;gt;Lampros Mouselimis [https://cran.r-project.org/web/packages/cluster/cluster.pdf Package &#039;ClusterR&#039;]. October 8, 2016&amp;lt;/ref&amp;gt;:&lt;br /&gt;
#** для задания метрики между элементами кластеризуемого множества передаётся строка, задающая одну из реализованных в алгоритме метрик. Данный подход теряет одно из преимуществ алгоритма PAM, позволяющее в качестве матрицы расстояний использовать произвольную матрицу различий.&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# Последовательная реализация алгоритма PAM в пакете Cluster на языке R:&lt;br /&gt;
#* Лицензия: GPL (≥2)&lt;br /&gt;
#* [https://cran.r-project.org/package=cluster веб-страница фреймворка (исходный код, системные требования и справочное руководство)]&lt;br /&gt;
#* Алгоритм PAM реализован в функции &#039;pam()&#039;. Данная реализация имеет следующие особенности &amp;lt;ref&amp;gt;Martin Maechler., etc. [https://cran.r-project.org/web/packages/cluster/cluster.pdf Package &#039;cluster&#039;]. October 8, 2016&amp;lt;/ref&amp;gt;:&lt;br /&gt;
#** функция может принимать на вход не множество точек, а матрицу расстояний между ними;&lt;br /&gt;
#** реализация более гибкая, так как принимает на вход матрицу различий элементов, а не использует какую-либо метрику;&lt;br /&gt;
#** может быть предоставлено графическое отображение.&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# Параллельная реализация алгоритма PAM в пакете SPRINT (Simple Parallel R INTerface - параллельный фреймворк) на языке R:&lt;br /&gt;
#* Лицензия: GPL (≥3)&lt;br /&gt;
#* [https://cran.r-project.org/package=sprint веб-страница фреймворка (исходный код, системные требования и справочное руководство)]&lt;br /&gt;
#* Алгоритм PAM реализован в фукции &#039;ppam()&#039;. Входные параметры данной функции схожи с параметрами, задаваемыми в линейной реализации &#039;pam()&#039; пакета Cluster языка R. Отличительной особенностью является подача на вход предвычисленной матрицы расстояний (например, с помощью функции &#039;pcor()&#039; из этой же библиотеки).&amp;lt;ref&amp;gt;University of Edinburgh SPRINT Team. [https://cran.r-project.org/web/packages/sprint/sprint.pdf Package &#039;sprint&#039;]. February 20, 2015&amp;lt;/ref&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# Параллельная реализация алгоритма PAM на основе операций MapReduce на Hadoop кластере.&lt;br /&gt;
#* В рамках исследования по ускорению анализа информации о геноме были созданы реализации алгоритма PAM с использованием технологии MPI и технологии MapReduce. Реализация в открытом досупе отсутствует. &amp;lt;ref&amp;gt;Dobrzelecki B. et al. Managing and analysing Genomic Data using HPC and Clouds //Grid and Cloud Database Management. – Springer Berlin Heidelberg, 2011. – С. 261-277.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
MKL, ScaLAPACK, PETSc, FFTW, ATLAS, Magma и другие подобные библиотеки.&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;
гугл-запрос: &amp;quot;partitioning around medoids&amp;quot; &amp;quot;PAM&amp;quot; BUILD SWAP &amp;quot;MPI&amp;quot; &amp;quot;program&amp;quot; &amp;quot;package&amp;quot; -worldwidescience&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19022</id>
		<title>Участник:Parkhomenko/Алгоритм k средних</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19022"/>
		<updated>2016-11-23T11:58:21Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd&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(t * \log kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Parkhomenko|П.А.Пархоменко]] ([[#Общее описание алгоритма|1.1]], [[#Вычислительное ядро алгоритма|1.3]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Входные и выходные данные алгоритма|1.9]], [[#Свойства алгоритма|1.10]], [[#Масштабируемость алгоритма и его реализации|2.4]]), [[Участник:Ivan.mashonskiy|И.Д.Машонский]] ([[#Математическое описание алгоритма|1.2]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм k средних (англ. k-means) - один из алгоритмов машинного обучения, решающий задачу кластеризации. Он был изобретен в середине 1950-х математиком Гуго Штейнгаузом&amp;lt;ref&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/ref&amp;gt; и Стюартом Ллойдом&amp;lt;ref&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/ref&amp;gt;. Алгоритм стал особо популярным после публикации Маккуина&amp;lt;ref&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/ref&amp;gt;, в которой впервые был использован термин “k-means”.&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации заключается в разбиении N объектов (d-мерных векторов) на K групп (кластеров). Каждый вектор может принадлежать только одному кластеру. Количество кластеров K фиксировано, оно задается в качестве параметра.&lt;br /&gt;
&lt;br /&gt;
K-means является итерационным алгоритмом, разновидностью EM-алгоритма. Основная идея работы алгоритма заключается в проведении некоторого количества итераций, на каждой из которых происходит пересчет центра масс для каждого кластера, полученного на предыдущей итерации. После этого векторы перераспределяются по кластерам: вектор относится к тому кластеру, расстояние до центра масс которого минимально. Алгоритм завершает работу в том случае, если на очередной итерации центры масс кластеров не изменились.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Исходные данные: множество d-мерных векторов (наблюдений) &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;&amp;amp;nbsp;=&amp;amp;nbsp;&amp;lt;math&amp;gt;\{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&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;k&amp;lt;/math&amp;gt; непересекающихся множеств (кластеров) &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, таких что &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt; и значение &amp;lt;math&amp;gt;\sum_{i=1}^{k} \sum_{\mathbf x \in S_i} \left\| \mathbf x - \mu_i \right\|^2 &amp;lt;/math&amp;gt; — минимально для всех возможных наборов &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
Под &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt; здесь подразумевается центр масс векторов из множества &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Первоначальные значения центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt; определяются в начале работы алгоритма случайным образом среди векторов множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм заключается в попеременном применении двух шагов &amp;lt;ref&amp;gt;MacKay, David (2003). &amp;quot;Chapter 20. An Example Inference Task: Clustering&amp;quot; (PDF). Information Theory, Inference and Learning Algorithms. Cambridge University Press. pp. 284–292.&amp;lt;/ref&amp;gt;: &#039;&#039;&#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;S_i^{(t)}&amp;lt;/math&amp;gt;: &lt;br /&gt;
::&amp;lt;math&amp;gt;S_i^{(t)} = \big \{ x_p : \big \| x_p - \mu^{(t)}_i \big \|^2 \le \big \| x_p - \mu^{(t)}_j \big \|^2 \ \forall j, 1 \le j \le k \big\},&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс&#039;&#039;&#039;: на данном шаге вычисляются новые центры масс для кластеров, полученных на предыдущем шаге.&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм завершается, когда на очередном шаге не происходит изменения центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательного алгоритма k-means состоит из двух операций, повторяющихся на каждой итерации алгоритма:&lt;br /&gt;
* пересчета центров масс кластеров;&lt;br /&gt;
* соотнесения каждого вектора к одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
Пересчет центра масс кластера заключается в нахождении d-мерного вектора, являющегося средним арифметическим d-мерных векторов, принадлежащих кластеру. Эту операцию необходимо выполнять для каждого из K кластеров.&lt;br /&gt;
&lt;br /&gt;
Для соотнесения вектора к одному из кластеров, необходимо для каждого из N вектора посчитать расстояние до центров масс всех K кластеров. Для нахождения расстояния между двумя d-мерными векторами p и q используется Евклидова метрика: &amp;lt;math&amp;gt;\sqrt{\sum_{k=1}^d (p_k-q_k)^2}&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;&lt;br /&gt;
&lt;br /&gt;
В начале алгоритма осуществляется инициализация центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;, которая может быть реализована двумя возможными способами &amp;lt;ref&amp;gt;Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot; (PDF). Proceedings of the eleventh international conference on Information and knowledge management (CIKM).&amp;lt;/ref&amp;gt;: &lt;br /&gt;
* случайный выбор центров масс среди исходных наблюдений &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* все наблюдения случайным образом распределяются по кластерам &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, а затем для каждого кластера выполняется операция обновления центра масс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге распределения векторов по кластерам производится операция нахождения квадратов расстояний между векторами кластера и центром масс кластера: &lt;br /&gt;
::&amp;lt;math&amp;gt;\rho = \big \| x_p - \mu^{(t)}_i \big \|^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Обновление центров масс кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге обновления центров масс производится операция суммирования векторов кластера:&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &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;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;t := 1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого вектора &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;m = \underset{j \in \{1, 2, ..., k\}} {\operatorname{arg\,min}} \big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt;, &amp;lt;br/&amp;gt;вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; распределяется в кластер &amp;lt;math&amp;gt;S^{(t)}_m&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого &amp;lt;math&amp;gt;j = 1, 2, ..., k&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
#  Если &amp;lt;math&amp;gt;\mu^{(t)}_j = \mu^{(t+1)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt;, завершить выполнение алгоритма, иначе &amp;lt;math&amp;gt;t := t + 1&amp;lt;/math&amp;gt;, переход на шаг 3.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для вычисления центра массы кластера, содержащего &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; d-мерных векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n_i * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения центров масс всех k кластеров требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;k * d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения расстояния между двумя d-мерными векторами, требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;d - 1&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Для определения кластера для n d-мерного векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность одной итерации:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций умножения/деления&lt;br /&gt;
&lt;br /&gt;
Сложность всего алгоритма, состоящего из &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; итераций: &amp;lt;math&amp;gt;O(t * n * k * d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе описывается информационный граф алгоритма k-средних при &amp;lt;math&amp;gt;n = 3, k = 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 показана общая структура алгоритма: в качестве входных данных выступает матрица, строками которой являются входные векторы &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt;, на выходе получается массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; чисел, где элемент с номером &amp;lt;math&amp;gt;i = 1, 2, 3&amp;lt;/math&amp;gt; обозначает номер кластера, которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. На шаге &amp;lt;math&amp;gt;init&amp;lt;/math&amp;gt; происходит инициализация значений центров масс кластеров, после этого осуществляется итеративное повторение этапов распределения векторов по кластерам (&amp;lt;math&amp;gt;as&amp;lt;/math&amp;gt;) и обновления значений центров масс кластеров (&amp;lt;math&amp;gt;up&amp;lt;/math&amp;gt;). Когда на очередном шаге обновления центров масс кластеров значения центров масс не изменяются, алгоритм завершает работу, и в качестве выходных данных выступают номера кластеров для каждого из входных векторов.&lt;br /&gt;
[[file:k_means_main.png|thumb|center|400px|Рисунок 1. Общая схема работы алгоритма k-средних]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Распределение векторов по кластерам.&#039;&#039;&#039; На рисунке 2 изображен информационный граф для этапа распределения векторов по кластерам. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; и текущие значения центров масс кластеров &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt; (вычисленные на этапе инициализации или предшествующего этапа обновления центров масс кластеров). В качестве выходных данных выступают числа &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_i, i = 1, 2, 3&amp;lt;/math&amp;gt; - номер кластера, содержащего вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; обозначены операции векторной разности, вершинами &amp;lt;math&amp;gt;sn&amp;lt;/math&amp;gt; - операция вычисления квадрата нормы вектора, вершинами &amp;lt;math&amp;gt;am&amp;lt;/math&amp;gt; - операция &amp;lt;math&amp;gt;\underset{i \in \{1, 2\}} {\operatorname{arg\,min}}  p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k_means_info_graph_as.png|thumb|center|400px|Рисунок 2. Граф алгоритма для этапа распределения векторов по кластерам]]&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс кластеров.&#039;&#039;&#039; На рисунке 3 представлен информационный граф для этапа обновления центров масс кластеров. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; с соответствующими номерами кластеров &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, содержащих эти векторы. В качестве выходных данных здесь выступают обновленные значения центров масс кластеров: &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;s_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции суммирования векторов, принадлежащих кластеру &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;cs_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции вычисления выражения &amp;lt;math&amp;gt;1 / |S_i|&amp;lt;/math&amp;gt;, где за &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt; обозначен кластер с номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; обозначена операция произведения.&lt;br /&gt;
[[file:k_means_info_graph_up.png|thumb|center|400px|Рисунок 3. Граф алгоритма для этапа обновления центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм кластеризации k средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (распределения векторов по кластерам и обновления центров масс кластеров) лежат циклы, итерации которых являются информационно независимыми.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
В силу особенностей алгоритма k средних порядок выполнения основных этапов существенен, что не позволяет выполнять их параллельно. Таким образом, итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;T = t * (T_{as} + T_{up})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{as}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;T_{up}&amp;lt;/math&amp;gt; - параллельная сложность этапа обновления центров масс кластеров, &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - общее число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа распределения векторов по кластерам с учетом доступности неограниченного числа процессоров вычисляется следующим образом: так как вычисление значений &amp;lt;math&amp;gt;\big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt; для каждой пары векторов &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^{(t)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt; можно выполнять независимо и для вычисления этого выражения требуется &amp;lt;math&amp;gt;O(\log d)&amp;lt;/math&amp;gt; операций в соответствии с [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения сумм элементов массива сдваиванием]]. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log k)&amp;lt;/math&amp;gt;. Таким образом, сложность этапа распределения векторов по кластерам &amp;lt;math&amp;gt;O(\log kd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа обновления центров масс кластеров вычисляется следующим образом: так как обновление значений каждого из центров масс кластеров можно выполнять независимо, параллельная сложность определяется сложностью обновления центра масс самого большого кластера. В худшем случае для нахождения значения выражения &amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
С учетом вышесказанного, итоговая параллельная сложность алгоритма k средних определяется выражением &amp;lt;math&amp;gt;O(t * \log kdn)&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;k&amp;lt;/math&amp;gt; - количество кластеров;&lt;br /&gt;
* матрица координат векторов &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размерностью &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных&#039;&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; вещественных чисел (если координаты вещественные числа), &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
&#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;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности алгоритма равно &amp;lt;math&amp;gt;\frac{O(tnkd)}{O(t * \log kdn)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Число операций алгоритма k средних оценивается как &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;, суммарный объем входных и выходных данных равен &amp;lt;math&amp;gt;n(d+1)&amp;lt;/math&amp;gt;. Следовательно, вычислительная мощность алгоритма линейна по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и линейна по &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить следующие ключевые особенности алгоритма k средних:&lt;br /&gt;
* Алгоритм прост в реализации и имеет высокую скорость работы по сравнению с другими алгоритмами кластеризации (при удачном выборе начальных значений центров масс кластеров)&lt;br /&gt;
* В качестве метрики используется евклидово расстояние, а в качестве меры разброса кластера используется дисперсия&lt;br /&gt;
* Количество кластеров k является входным параметром алгоритма, поэтому неправильный выбор этого параметра может негативно повлиять на качество кластеризации&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;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование масштабируемости алгоритма k-means в зависимости от количества используемых процессов было проведено в статье Кумара&amp;lt;ref&amp;gt;Kumar, J., Mills, R. T., Hoffman, F. M., &amp;amp; Hargrove, W. W. (2011). Parallel k-means clustering for quantitative ecoregion delineation using large data sets. Procedia Computer Science, 4, 1602-1611.&amp;lt;/ref&amp;gt;. Исследование происходило на суперкомпьютере Jaguar - Cray XT5&amp;lt;ref&amp;gt;https://www.top500.org/system/176029&amp;lt;/ref&amp;gt;. На момент экспериментов данный суперкомпьютер имел следующую конфигурацию: 18,688 вычислительных узлов с двумя шестнадцатиядерными процессорами AMD Opteron 2435 (Istanbul) 2.6 GHz, 16 GB of DDR2-800 оперативной памяти, и SeaStar 2+ роутер. Всего он состоял из 224,256 вычислительных ядер, 300 TB памяти, и пиковой производительностью 2.3 petaflops.&lt;br /&gt;
&lt;br /&gt;
Реализация алгоритма была выполнена на языке программирования C с использованием MPI.&lt;br /&gt;
&lt;br /&gt;
Объем данных составлял 84 гигабайта, количество объектов (d-мерных векторов) n равнялось 1,024,767,667, размерность векторов d равнялась 22, количество кластеров k равнялось 1000. &lt;br /&gt;
&lt;br /&gt;
На рисунке 4 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно отметить, что время, затраченное на чтение данных и запись результатов кластеризации, практически не изменяется с увеличением количества задействованных процессоров. Время же работы самого алгоритма кластеризации уменьшается с увеличением количества процессоров.&lt;br /&gt;
[[Файл:k-means-proc-scalability.png|thumb|center|700px|Рисунок 4. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Также было произведено самостоятельное исследование масштабируемости алгоритма. Исследование производилось на суперкомпьютере &amp;quot;Blue Gene/P&amp;quot;&amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых параметров запуска реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* число процессоров [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];&lt;br /&gt;
* количество объектов [5000, 10000, 25000, 50000].&lt;br /&gt;
&lt;br /&gt;
Был использован набор данных &#039;&#039;Dataset for Sensorless Drive Diagnosis Data Set&#039;&#039;&amp;lt;ref&amp;gt;PASCHKE, Fabian ; BAYER, Christian ; BATOR, Martyna ; MÃ–NKS, Uwe ; DICKS, Alexander ; ENGE-ROSENBLATT, Olaf ; LOHWEG, Volker: Sensorlose ZustandsÃ¼berwachung an Synchronmotoren, Bd. 46. In: HOFFMANN, Frank; HÃœLLERMEIER, Eyke (Hrsg.): Proceedings 23. Workshop Computational Intelligence. Karlsruhe : KIT Scientific Publishing, 2013 (Schriftenreihe des Instituts fÃ¼r Angewandte Informatik - Automatisierungstechnik am Karlsruher Institut fÃ¼r Technologie, 46), S. 211-225&amp;lt;/ref&amp;gt; из репозитория &#039;&#039;Machine learning repository&#039;&#039;&amp;lt;ref&amp;gt;https://archive.ics.uci.edu/ml/datasets/Dataset+for+Sensorless+Drive+Diagnosis&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исследуемый набор данных содержит векторы, размерность которых равна 49. Компоненты векторов являются вещественными числами. Количество кластеров равно 11. Пропущенные значения отсутствуют.&lt;br /&gt;
&lt;br /&gt;
Для исследования масштабируемости алгоритма была использована реализация на языке C с использованием MPI&amp;lt;ref&amp;gt;http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html&amp;lt;/ref&amp;gt;. Код можно найти здесь: https://github.com/serban/kmeans.&lt;br /&gt;
&lt;br /&gt;
На рисунке 5 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно видеть, что при увеличении количества процессоров в N раз, время работы алгоритма уменьшается примерно в N раз (при условии, что количество объектов остается неизменным). Также можно отметить, что при фиксированном количестве процессоров, зависимость времени работы алгоритма от количества объектов также близка к линейной.&lt;br /&gt;
[[Файл:Heatmap_kmeans.png|thumb|center|900px|Рисунок 5. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (при фиксированном количестве объектов, равном 50000). При визуализации была использована логарифмическая шкала. Можно отметить, что при заданном наборе данных можно наблюдать линейную зависимость времени работы алгоритма от количества используемых процессоров, что согласуется с теоретической оценкой сложности последовательной версии алгоритма.&lt;br /&gt;
[[Файл:50000kmeans.png|thumb|center|900px|Рисунок 6. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (количество объектов равно 50000).]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие реализации алгоритма k-средних:&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++, содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [https://github.com/JuliaStats/Clustering.jl Julia k-means] реализация алгоритма &#039;&#039;k&#039;&#039;-средних на языке программирования Julia.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций &#039;&#039;k&#039;&#039;-средних на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация &#039;&#039;k&#039;&#039;-средних на языке R.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19021</id>
		<title>Участник:Parkhomenko/Алгоритм k средних</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=19021"/>
		<updated>2016-11-23T11:58:03Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd&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(t * \log kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Parkhomenko|П.А.Пархоменко]] ([[#Общее описание алгоритма|1.1]], [[#Вычислительное ядро алгоритма|1.3]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Входные и выходные данные алгоритма|1.9]], [[#Свойства алгоритма|1.10]], [[#Масштабируемость алгоритма и его реализации|2.4]]), [[Участник:Ivan.mashonskiy|И.Д.Машонский]] ([[#Математическое описание алгоритма|1.2]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм k средних (англ. k-means) - один из алгоритмов машинного обучения, решающий задачу кластеризации. Он был изобретен в середине 1950-х математиком Гуго Штейнгаузом&amp;lt;ref&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/ref&amp;gt; и Стюартом Ллойдом&amp;lt;ref&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/ref&amp;gt;. Алгоритм стал особо популярным после публикации Маккуина&amp;lt;ref&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/ref&amp;gt;, в которой впервые был использован термин “k-means”.&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации заключается в разбиении N объектов (d-мерных векторов) на K групп (кластеров). Каждый вектор может принадлежать только одному кластеру. Количество кластеров K фиксировано, оно задается в качестве параметра.&lt;br /&gt;
&lt;br /&gt;
K-means является итерационным алгоритмом, разновидностью EM-алгоритма. Основная идея работы алгоритма заключается в проведении некоторого количества итераций, на каждой из которых происходит пересчет центра масс для каждого кластера, полученного на предыдущей итерации. После этого векторы перераспределяются по кластерам: вектор относится к тому кластеру, расстояние до центра масс которого минимально. Алгоритм завершает работу в том случае, если на очередной итерации центры масс кластеров не изменились.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Исходные данные: множество d-мерных векторов (наблюдений) &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;&amp;amp;nbsp;=&amp;amp;nbsp;&amp;lt;math&amp;gt;\{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&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;k&amp;lt;/math&amp;gt; непересекающихся множеств (кластеров) &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, таких что &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt; и значение &amp;lt;math&amp;gt;\sum_{i=1}^{k} \sum_{\mathbf x \in S_i} \left\| \mathbf x - \mu_i \right\|^2 &amp;lt;/math&amp;gt; — минимально для всех возможных наборов &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
Под &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt; здесь подразумевается центр масс векторов из множества &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Первоначальные значения центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt; определяются в начале работы алгоритма случайным образом среди векторов множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм заключается в попеременном применении двух шагов &amp;lt;ref&amp;gt;MacKay, David (2003). &amp;quot;Chapter 20. An Example Inference Task: Clustering&amp;quot; (PDF). Information Theory, Inference and Learning Algorithms. Cambridge University Press. pp. 284–292.&amp;lt;/ref&amp;gt;: &#039;&#039;&#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;S_i^{(t)}&amp;lt;/math&amp;gt;: &lt;br /&gt;
::&amp;lt;math&amp;gt;S_i^{(t)} = \big \{ x_p : \big \| x_p - \mu^{(t)}_i \big \|^2 \le \big \| x_p - \mu^{(t)}_j \big \|^2 \ \forall j, 1 \le j \le k \big\},&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс&#039;&#039;&#039;: на данном шаге вычисляются новые центры масс для кластеров, полученных на предыдущем шаге.&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм завершается, когда на очередном шаге не происходит изменения центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательного алгоритма k-means состоит из двух операций, повторяющихся на каждой итерации алгоритма:&lt;br /&gt;
* пересчета центров масс кластеров;&lt;br /&gt;
* соотнесения каждого вектора к одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
Пересчет центра масс кластера заключается в нахождении d-мерного вектора, являющегося средним арифметическим d-мерных векторов, принадлежащих кластеру. Эту операцию необходимо выполнять для каждого из K кластеров.&lt;br /&gt;
&lt;br /&gt;
Для соотнесения вектора к одному из кластеров, необходимо для каждого из N вектора посчитать расстояние до центров масс всех K кластеров. Для нахождения расстояния между двумя d-мерными векторами p и q используется Евклидова метрика: &amp;lt;math&amp;gt;\sqrt{\sum_{k=1}^d (p_k-q_k)^2}&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;&lt;br /&gt;
&lt;br /&gt;
В начале алгоритма осуществляется инициализация центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;, которая может быть реализована двумя возможными способами &amp;lt;ref&amp;gt;Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot; (PDF). Proceedings of the eleventh international conference on Information and knowledge management (CIKM).&amp;lt;/ref&amp;gt;: &lt;br /&gt;
* случайный выбор центров масс среди исходных наблюдений &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* все наблюдения случайным образом распределяются по кластерам &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, а затем для каждого кластера выполняется операция обновления центра масс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге распределения векторов по кластерам производится операция нахождения квадратов расстояний между векторами кластера и центром масс кластера: &lt;br /&gt;
::&amp;lt;math&amp;gt;\rho = \big \| x_p - \mu^{(t)}_i \big \|^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Обновление центров масс кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге обновления центров масс производится операция суммирования векторов кластера:&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &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;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;t := 1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого вектора &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;m = \underset{j \in \{1, 2, ..., k\}} {\operatorname{arg\,min}} \big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt;, &amp;lt;br/&amp;gt;вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; распределяется в кластер &amp;lt;math&amp;gt;S^{(t)}_m&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого &amp;lt;math&amp;gt;j = 1, 2, ..., k&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
#  Если &amp;lt;math&amp;gt;\mu^{(t)}_j = \mu^{(t+1)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt;, завершить выполнение алгоритма, иначе &amp;lt;math&amp;gt;t := t + 1&amp;lt;/math&amp;gt;, переход на шаг 3.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для вычисления центра массы кластера, содержащего &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; d-мерных векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n_i * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения центров масс всех k кластеров требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;k * d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения расстояния между двумя d-мерными векторами, требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;d - 1&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Для определения кластера для n d-мерного векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность одной итерации:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций умножения/деления&lt;br /&gt;
&lt;br /&gt;
Сложность всего алгоритма, состоящего из &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; итераций: &amp;lt;math&amp;gt;O(t * n * k * d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе описывается информационный граф алгоритма k-средних при &amp;lt;math&amp;gt;n = 3, k = 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 показана общая структура алгоритма: в качестве входных данных выступает матрица, строками которой являются входные векторы &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt;, на выходе получается массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; чисел, где элемент с номером &amp;lt;math&amp;gt;i = 1, 2, 3&amp;lt;/math&amp;gt; обозначает номер кластера, которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. На шаге &amp;lt;math&amp;gt;init&amp;lt;/math&amp;gt; происходит инициализация значений центров масс кластеров, после этого осуществляется итеративное повторение этапов распределения векторов по кластерам (&amp;lt;math&amp;gt;as&amp;lt;/math&amp;gt;) и обновления значений центров масс кластеров (&amp;lt;math&amp;gt;up&amp;lt;/math&amp;gt;). Когда на очередном шаге обновления центров масс кластеров значения центров масс не изменяются, алгоритм завершает работу, и в качестве выходных данных выступают номера кластеров для каждого из входных векторов.&lt;br /&gt;
[[file:k_means_main.png|thumb|center|400px|Рисунок 1. Общая схема работы алгоритма k-средних]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Распределение векторов по кластерам.&#039;&#039;&#039; На рисунке 2 изображен информационный граф для этапа распределения векторов по кластерам. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; и текущие значения центров масс кластеров &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt; (вычисленные на этапе инициализации или предшествующего этапа обновления центров масс кластеров). В качестве выходных данных выступают числа &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_i, i = 1, 2, 3&amp;lt;/math&amp;gt; - номер кластера, содержащего вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; обозначены операции векторной разности, вершинами &amp;lt;math&amp;gt;sn&amp;lt;/math&amp;gt; - операция вычисления квадрата нормы вектора, вершинами &amp;lt;math&amp;gt;am&amp;lt;/math&amp;gt; - операция &amp;lt;math&amp;gt;\underset{i \in \{1, 2\}} {\operatorname{arg\,min}}  p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k_means_info_graph_as.png|thumb|center|400px|Рисунок 2. Граф алгоритма для этапа распределения векторов по кластерам]]&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс кластеров.&#039;&#039;&#039; На рисунке 3 представлен информационный граф для этапа обновления центров масс кластеров. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; с соответствующими номерами кластеров &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, содержащих эти векторы. В качестве выходных данных здесь выступают обновленные значения центров масс кластеров: &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;s_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции суммирования векторов, принадлежащих кластеру &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;cs_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции вычисления выражения &amp;lt;math&amp;gt;1 / |S_i|&amp;lt;/math&amp;gt;, где за &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt; обозначен кластер с номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; обозначена операция произведения.&lt;br /&gt;
[[file:k_means_info_graph_up.png|thumb|center|400px|Рисунок 3. Граф алгоритма для этапа обновления центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм кластеризации k средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (распределения векторов по кластерам и обновления центров масс кластеров) лежат циклы, итерации которых являются информационно независимыми.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
В силу особенностей алгоритма k средних порядок выполнения основных этапов существенен, что не позволяет выполнять их параллельно. Таким образом, итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;T = t * (T_{as} + T_{up})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{as}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;T_{up}&amp;lt;/math&amp;gt; - параллельная сложность этапа обновления центров масс кластеров, &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - общее число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа распределения векторов по кластерам с учетом доступности неограниченного числа процессоров вычисляется следующим образом: так как вычисление значений &amp;lt;math&amp;gt;\big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt; для каждой пары векторов &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^{(t)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt; можно выполнять независимо и для вычисления этого выражения требуется &amp;lt;math&amp;gt;O(\log d)&amp;lt;/math&amp;gt; операций в соответствии с [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения сумм элементов массива сдваиванием]]. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log k)&amp;lt;/math&amp;gt;. Таким образом, сложность этапа распределения векторов по кластерам &amp;lt;math&amp;gt;O(\log kd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа обновления центров масс кластеров вычисляется следующим образом: так как обновление значений каждого из центров масс кластеров можно выполнять независимо, параллельная сложность определяется сложностью обновления центра масс самого большого кластера. В худшем случае для нахождения значения выражения &amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
С учетом вышесказанного, итоговая параллельная сложность алгоритма k средних определяется выражением &amp;lt;math&amp;gt;O(t * \log kdn)&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;k&amp;lt;/math&amp;gt; - количество кластеров;&lt;br /&gt;
* матрица координат векторов &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размерностью &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных&#039;&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; вещественных чисел (если координаты вещественные числа), &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
&#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;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности алгоритма равно &amp;lt;math&amp;gt;\frac{O(tnkd)}{O(t * \log kdn)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Число операций алгоритма k средних оценивается как &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;, суммарный объем входных и выходных данных равен &amp;lt;math&amp;gt;n(d+1)&amp;lt;/math&amp;gt;. Следовательно, вычислительная мощность алгоритма линейна по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и линейна по &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить следующие ключевые особенности алгоритма k средних:&lt;br /&gt;
* Алгоритм прост в реализации и имеет высокую скорость работы по сравнению с другими алгоритмами кластеризации (при удачном выборе начальных значений центров масс кластеров)&lt;br /&gt;
* В качестве метрики используется евклидово расстояние, а в качестве меры разброса кластера используется дисперсия&lt;br /&gt;
* Количество кластеров k является входным параметром алгоритма, поэтому неправильный выбор этого параметра может негативно повлиять на качество кластеризации&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;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование масштабируемости алгоритма k-means в зависимости от количества используемых процессов было проведено в статье Кумара&amp;lt;ref&amp;gt;Kumar, J., Mills, R. T., Hoffman, F. M., &amp;amp; Hargrove, W. W. (2011). Parallel k-means clustering for quantitative ecoregion delineation using large data sets. Procedia Computer Science, 4, 1602-1611.&amp;lt;/ref&amp;gt;. Исследование происходило на суперкомпьютере Jaguar - Cray XT5&amp;lt;ref&amp;gt;https://www.top500.org/system/176029&amp;lt;/ref&amp;gt;. На момент экспериментов данный суперкомпьютер имел следующую конфигурацию: 18,688 вычислительных узлов с двумя шестнадцатиядерными процессорами AMD Opteron 2435 (Istanbul) 2.6 GHz, 16 GB of DDR2-800 оперативной памяти, и SeaStar 2+ роутер. Всего он состоял из 224,256 вычислительных ядер, 300 TB памяти, и пиковой производительностью 2.3 petaflops.&lt;br /&gt;
&lt;br /&gt;
Реализация алгоритма была выполнена на языке программирования C с использованием MPI.&lt;br /&gt;
&lt;br /&gt;
Объем данных составлял 84 гигабайта, количество объектов (d-мерных векторов) n равнялось 1,024,767,667, размерность векторов d равнялась 22, количество кластеров k равнялось 1000. &lt;br /&gt;
&lt;br /&gt;
На рисунке 4 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно отметить, что время, затраченное на чтение данных и запись результатов кластеризации, практически не изменяется с увеличением количества задействованных процессоров. Время же работы самого алгоритма кластеризации уменьшается с увеличением количества процессоров.&lt;br /&gt;
[[Файл:k-means-proc-scalability.png|thumb|center|700px|Рисунок 4. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Также было произведено самостоятельное исследование масштабируемости алгоритма. Исследование производилось на суперкомпьютере &amp;quot;Blue Gene/P&amp;quot;&amp;lt;ref&amp;gt;http://hpc.cmc.msu.ru/bgp&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых параметров запуска реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* число процессоров [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];&lt;br /&gt;
* количество объектов [5000, 10000, 25000, 50000].&lt;br /&gt;
&lt;br /&gt;
Был использован набор данных &#039;&#039;Dataset for Sensorless Drive Diagnosis Data Set&#039;&#039;&amp;lt;ref&amp;gt;PASCHKE, Fabian ; BAYER, Christian ; BATOR, Martyna ; MÃ–NKS, Uwe ; DICKS, Alexander ; ENGE-ROSENBLATT, Olaf ; LOHWEG, Volker: Sensorlose ZustandsÃ¼berwachung an Synchronmotoren, Bd. 46. In: HOFFMANN, Frank; HÃœLLERMEIER, Eyke (Hrsg.): Proceedings 23. Workshop Computational Intelligence. Karlsruhe : KIT Scientific Publishing, 2013 (Schriftenreihe des Instituts fÃ¼r Angewandte Informatik - Automatisierungstechnik am Karlsruher Institut fÃ¼r Technologie, 46), S. 211-225&amp;lt;/ref&amp;gt; из репозитория &#039;&#039;Machine learning repository&#039;&#039;&amp;lt;ref&amp;gt;https://archive.ics.uci.edu/ml/datasets/Dataset+for+Sensorless+Drive+Diagnosis&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исследуемый набор данных содержит векторы, размерность которых равна 49. Компоненты векторов являются вещественными числами. Количество кластеров равно 11. Пропущенные значения отсутствуют.&lt;br /&gt;
&lt;br /&gt;
Для исследования масштабируемости алгоритма была использована реализация на языке C с использованием MPI&amp;lt;ref&amp;gt;http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html&amp;lt;/ref&amp;gt;. Код можно найти здесь: https://github.com/serban/kmeans.&lt;br /&gt;
&lt;br /&gt;
На рисунке 5 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно видеть, что при увеличении количества процессоров в N раз, время работы алгоритма уменьшается примерно в N раз (при условии, что количество объектов остается неизменным). Также можно отметить, что при фиксированном количестве процессоров, зависимость времени работы алгоритма от количества объектов также близка к линейной.&lt;br /&gt;
[[Файл:Heatmap_kmeans.png|thumb|center|900px|Рисунок 5. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (при фиксированном количестве объектов, равном 50000). При визуализации была использована логарифмическая шкала. Можно отметить, что при заданном наборе данных можно наблюдать линейную зависимость времени работы алгоритма от количества используемых процессоров, что согласуется с теоретической оценкой сложности последовательной версии алгоритма.&lt;br /&gt;
[[Файл:50000kmeans.png|thumb|center|900px|Рисунок 6. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров (количество объектов равно 50000).]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие реализации алгоритма k-средних:&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++, содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [https://github.com/JuliaStats/Clustering.jl Julia k-means] реализация алгоритма &#039;&#039;k&#039;&#039;-средних на языке программирования Julia.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций &#039;&#039;k&#039;&#039;-средних на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация &#039;&#039;k&#039;&#039;-средних на языке R.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:IanaV/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_means&amp;diff=19020</id>
		<title>Участник:IanaV/Алгоритм k means</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:IanaV/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_means&amp;diff=19020"/>
		<updated>2016-11-23T11:53:17Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{level-a}}&lt;br /&gt;
Авторы страницы: [[Участник:IanaV|Валуйская Я.А.]], [[Участник:GlotovES|Глотов Е.С.]]&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;p&amp;gt;Алгоритм &amp;lt;i&amp;gt;k-means&amp;lt;/i&amp;gt; (&amp;lt;i&amp;gt;k средних&amp;lt;/i&amp;gt;) - один из наиболее популярных алгоритмов кластеризации. Наиболее простой, но в то же время достаточно неточный метод кластеризации в классической реализации.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм был разработан в 1956 году математиком Гуго Штейнгаузом &amp;lt;ref&amp;gt;[http://www.laurent-duval.eu/Documents/Steinhaus_H_1956_j-bull-acad-polon-sci_division_cmp-k-means.pdf Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.]&amp;lt;/ref&amp;gt;, и почти одновременно его изобрел Стюарт Ллойд &amp;lt;ref&amp;gt;[http://www-evasion.imag.fr/people/Franck.Hetroy/Teaching/ProjetsImage/2007/Bib/lloyd-1982.pdf Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.]&amp;lt;/ref&amp;gt;. Особую популярность алгоритм снискал после работы Джеймса Маккуина &amp;lt;ref&amp;gt;[https://pdfs.semanticscholar.org/a718/b85520bea702533ca9a5954c33576fd162b0.pdf MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.]&amp;lt;/ref&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм кластеризации &amp;lt;i&amp;gt;k-means&amp;lt;/i&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;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм k-means является итерационным алгоритмом кластеризации. На каждой итерации происходит пересчет центра масс для каждого кластера, полученного на предыдущем шаге, затем исходные векторы заново распределяются по кластерам в соответствии с тем, какой из новых центров оказался ближе по выбранной метрике. Алгоритм завершается, когда на какой-то итерации не происходит изменения кластеров.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Исходные данные:&#039;&#039; &lt;br /&gt;
* множество наблюдений &amp;lt;math&amp;gt;X = \{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, где каждое наблюдение &amp;lt;math&amp;gt;x_i \in R^d, i = 1, ..., n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* количество кластеров &amp;lt;math&amp;gt;k \in N, k \leq n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Обозначения:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;S = \{S_1, S_2, ..., S_k \} &amp;lt;/math&amp;gt; - множество кластеров, которые удовлетворяют следующим условиям:&lt;br /&gt;
** &amp;lt;math&amp;gt;S_i \bigcap S_j = \emptyset, i \neq j&amp;lt;/math&amp;gt;; &lt;br /&gt;
** &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;\mu_i \in R^d, i = 1, ..., k&amp;lt;/math&amp;gt; - центр масс кластера &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;d(u, v)&amp;lt;/math&amp;gt; - расстояние между векторами &amp;lt;math&amp;gt;u \in R^m, v \in R^m&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;L = (l_1, l_2, ..., l_n)&amp;lt;/math&amp;gt; - набор меток, где метка &amp;lt;math&amp;gt;l_i \in N_{[1, k]}&amp;lt;/math&amp;gt; - является порядковым номером кластера, к которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt; x_i \in S_{l_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;Цель алгоритма k-means&#039;&#039; - распределить наблюдения из входного множества &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;S = \{S_1, S_2, ..., S_k \} &amp;lt;/math&amp;gt; таким образом, чтобы сумма квадратов расстояний от каждой точки кластера до его центра масс по всем кластерам была минимальной: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\arg\min_{S} \sum_{i=1}^{k} \sum_{x \in S_i} (d(x, \mu_i))^2 &amp;lt;/math&amp;gt;,&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Алгоритм состоит из следующих шагов:&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;i&amp;gt;Инициализация центров масс&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге задаются начальные значения центров масс &amp;lt;math&amp;gt; \mu_1^1, ..., \mu_k^1&amp;lt;/math&amp;gt;. Существует несколько способов их выбора. Они будут рассмотрены ниже.&lt;br /&gt;
# &amp;lt;i&amp;gt;t-ый шаг итерации:&amp;lt;/i&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге каждый вектор &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; распределяется в свой кластер &amp;lt;math&amp;gt;S_j^t&amp;lt;/math&amp;gt; так, что: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;l_i^t = \arg \min_{j} (d(x_i, \mu_j^t))^2 , j = 1, ..., k; i = 1, ..., n&amp;lt;/math&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге происходит пересчет центров масс кластеров, полученных на предыдущем этапе: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;Критерий останова&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^t = \mu_i^{t+1},&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Способы инициализации начальных данных:&amp;lt;/i&amp;gt;&lt;br /&gt;
# Метод Forgy &amp;lt;br&amp;gt; Случайным образом выбираются &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества наблюдений &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;. Эти вектора используются в качестве центров масс кластеров &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;&lt;br /&gt;
# Метод случайного разбиения (Random Partition) &amp;lt;ref&amp;gt;[http://people.csail.mit.edu/tieu/notebook/kmeans/15_p600-hamerly.pdf Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot;. Proceedings of the eleventh international conference on Information and knowledge management (CIKM).]&amp;lt;/ref&amp;gt; &amp;lt;br&amp;gt; Каждый вектор &amp;lt;math&amp;gt;x_i, i = 1, .., n&amp;lt;/math&amp;gt; случайным образом распределяется в кластер &amp;lt;math&amp;gt;S_j, j = 1, .., k&amp;lt;/math&amp;gt;, затем для каждого кластера вычисляется его центр масс &amp;lt;math&amp;gt;\mu_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительном ядром алгоритма является шаг 2, состоящий из следующих этапов:&lt;br /&gt;
# распределение векторов по кластерам;&lt;br /&gt;
# пересчет центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; заключается в следующем: для каждого вектора &amp;lt;math&amp;gt;x_i \in X, i = 1, ..., n&amp;lt;/math&amp;gt; необходимо посчитать расстояние между этим вектором и центром масс кластера &amp;lt;math&amp;gt;\mu_j^t, j = 1, ..., k&amp;lt;/math&amp;gt;. Следовательно, на каждой итерации необходимо выполнить &amp;lt;math&amp;gt;n * k&amp;lt;/math&amp;gt; операций вычисления расстояния между векторами. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; заключается в следующем: для каждого кластера &amp;lt;math&amp;gt;S_i^t \in S, i = 1, ..., k&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;
&lt;br /&gt;
# Макрооперация &amp;lt;i&amp;gt;&amp;quot;Расстояние между векторами&amp;quot;&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; В данном алгоритме используется Евклидова метрика:  &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;v \in R^m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;u \in R^m: d(v, u) = \sqrt{\sum_{j=1}^{m} (v_j - u_j)^2} &amp;lt;/math&amp;gt;;&lt;br /&gt;
# Макрооперация &amp;lt;i&amp;gt;&amp;quot;Пересчет центра масс кластера&amp;quot;&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Таким образом, алгоритм в терминах макроопераций выглядит следующим образом:&amp;lt;/p&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;Инициализация центров масс&amp;lt;/i&amp;gt; &amp;lt;p&amp;gt;Метод Forgy: центры масс &amp;lt;math&amp;gt;\mu_j^1, j = 1, ..., k&amp;lt;/math&amp;gt; выбираются случайным образом.&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;Метод случайного разбиения: вектора &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; случайным образом распределяются по кластерам. Для каждого кластера выполняется макрооперация &amp;quot;Пересчет центра масс кластера&amp;quot; для нахождения &amp;lt;math&amp;gt;\mu_j^1&amp;lt;/math&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;t-ый шаг итерации:&amp;lt;/i&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге для каждого вектора &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; k раз выполняется макрооперация &amp;quot;Расстояние между векторами&amp;quot; для нахождения расстояния между &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu_j^t, j = 1, ..., k&amp;lt;/math&amp;gt;. Затем для каждого вектора &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; находится минимальное расстояние.&lt;br /&gt;
#* &amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге для каждого кластера выполняется макрооперация &amp;quot;Пересчет центра масс&amp;quot; для нахождения &amp;lt;math&amp;gt;\mu_j^{t+1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;i&amp;gt;Критерий останова&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^t = \mu_i^{t+1},&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Псевдокод алгоритма&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;b&amp;gt;Входные данные&amp;lt;/b&amp;gt;  : Множество векторов &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;lt;b&amp;gt;Выходные данные&amp;lt;/b&amp;gt; : Набор меток &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; принадлежности к кластеру&lt;br /&gt;
 1  Инициализация центров масс &amp;lt;math&amp;gt;\mu_i^1, i = 1, ..., k&amp;lt;/math&amp;gt;;&lt;br /&gt;
 2  t := 1;&lt;br /&gt;
 3  Для каждого вектора &amp;lt;math&amp;gt;x_i \in X, i = 1, ..., n:&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;l_i^t = \arg \min_{j} (d(x_i,\mu_j^t))^2 , j = 1, ..., k&amp;lt;/math&amp;gt;;&lt;br /&gt;
 4  Для каждого кластера &amp;lt;math&amp;gt;S_i^t, i = 1, ..., k&amp;lt;/math&amp;gt; выполняется макрооперация &amp;lt;i&amp;gt;&amp;quot;Пересчет центра масс&amp;quot;&amp;lt;/i&amp;gt;;&lt;br /&gt;
 5  &amp;lt;b&amp;gt;if&amp;lt;/b&amp;gt; (&amp;lt;math&amp;gt;\exists i = 1, ..., k: \mu_i^t \neq \mu_i^{t+1}&amp;lt;/math&amp;gt;) {&lt;br /&gt;
 6      t := t + 1;&lt;br /&gt;
 7      &amp;lt;b&amp;gt;goto&amp;lt;/b&amp;gt; 3;&lt;br /&gt;
 8  } &amp;lt;b&amp;gt;else&amp;lt;/b&amp;gt; {&lt;br /&gt;
 9      &amp;lt;b&amp;gt;stop&amp;lt;/b&amp;gt;;&lt;br /&gt;
 10 };&lt;br /&gt;
&lt;br /&gt;
Существующие способы оптимизации алгоритма &amp;lt;ref&amp;gt;[http://link.springer.com/chapter/10.1007/3-540-45643-0_13  Phillips S. J. Acceleration of k-means and related clustering algorithms //Workshop on Algorithm Engineering and Experimentation. – Springer Berlin Heidelberg, 2002. – С. 166-177.]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.187.3017&amp;amp;rep=rep1&amp;amp;type=pdf  Hamerly G. Making k-means Even Faster //SDM. – 2010. – С. 130-140.]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;[http://cseweb.ucsd.edu/~elkan/kmeansicml03.pdf  Elkan C. Using the triangle inequality to accelerate k-means //ICML. – 2003. – Т. 3. – С. 147-153.]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Подсчитаем количество операций для каждого шага алгоритма.&lt;br /&gt;
&lt;br /&gt;
# Инициализация:&lt;br /&gt;
#* Метод Forgy: &amp;lt;math&amp;gt;N_{init} = 0&amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метод случайного разбиения: &lt;br /&gt;
#** Количество операций деления: &amp;lt;math&amp;gt;N^{/}_{init} = k * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#** Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{init} = d * (n - k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Распределение &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов &amp;lt;math&amp;gt;x_i \in R^d&amp;lt;/math&amp;gt; по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам на одной итерации:&lt;br /&gt;
#* Количество операций умножения: &amp;lt;math&amp;gt;N^{*}_{distr} = k * n * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{distr} = k * n * (d - 1)&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций вычитания: &amp;lt;math&amp;gt;N^{-}_{distr} = k * n * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций взятия минимума: &amp;lt;math&amp;gt;N^{min}_{distr} = n * (k - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Пересчет центра масс &amp;lt;math&amp;gt;\mu_i \in R^d&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров на одной итерации:&lt;br /&gt;
#* Количество операций деления: &amp;lt;math&amp;gt;N^{/}_{update} = k * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{update} = d * (n - k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Следовательно, в случае если алгоритм сошелся за &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, получаем:&lt;br /&gt;
* Операций сложения/вычитания: &amp;lt;math&amp;gt;N^{+,-}_{k-means} = N^{+}_{init} + i*(N^{+}_{distr} + N^{-}_{distr} + N^{+}_{update}) = d * (n - k) + i*(k*n*(d-1)+k*n*d+d*(n-k)) \thicksim O(i*k*n*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Операций умножения/деления: &amp;lt;math&amp;gt;N^{*,/}_{k-means} = N^{/}_{init} + i*(N^{*}_{distr} + N^{/}_{update}) = k * d + i*(k*n*d+k*d) \thicksim O(i*k*n*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, [[глоссарий#Последовательная сложность|&#039;&#039;последовательная сложность&#039;&#039;]] алгоритма k-means для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерных векторов и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров за &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, требуемых для сходимости алгоритма:  &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;O(i * n * k * d)&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф алгоритма k-means.&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 1&amp;lt;/i&amp;gt; представлена общая схема алгоритма k-means, согласно разделу [[#Схема реализации последовательного алгоритма|&amp;quot;Схема реализации последовательного алгоритма&amp;quot;]]. &lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Init&amp;lt;/i&amp;gt; - этап инициализации.&lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Distribute&amp;lt;/i&amp;gt; - этап распределения векторов по кластерам.&lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Update&amp;lt;/i&amp;gt; - этап пересчета центров масс кластеров.&lt;br /&gt;
[[file:k-means-all.PNG|800px|thumb|center|Рисунок 1. Общая схема алгоритма k-means]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 2&amp;lt;/i&amp;gt; представлен информационный граф этапа распределения векторов по кластерам. На вход данному этапу подают множество векторов &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt; из входных данных и множество центров масс кластеров &amp;lt;math&amp;gt;\mu^t_1, ..., \mu^t_k&amp;lt;/math&amp;gt;, полученных с шага инициализации или с предыдущей итерации. Для каждой пары векторов &amp;lt;math&amp;gt;x_i, \mu^t_j, i = 1, ..., n; j = 1,..., k&amp;lt;/math&amp;gt; считается расстояние между ними (вершина &amp;lt;i&amp;gt;dist&amp;lt;/i&amp;gt;). Информационный граф вычисления расстояния между двумя векторами рассмотрен ниже (&amp;lt;i&amp;gt;рисунок 3&amp;lt;/i&amp;gt;). Затем выполняется операция нахождения минимума (вершина &amp;lt;i&amp;gt;min&amp;lt;/i&amp;gt;), в результате которой каждому вектору &amp;lt;math&amp;gt;x_i, i = 1, ..., n&amp;lt;/math&amp;gt; приписывается метка &amp;lt;math&amp;gt;l^t_i&amp;lt;/math&amp;gt;, равная порядковому номеру кластера &amp;lt;math&amp;gt;S^t_j, j = 1 ..., k&amp;lt;/math&amp;gt;, которому принадлежит данный вектор: &amp;lt;math&amp;gt;x_i \in S^t_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k-means-distribute.PNG|800px|thumb|center|Рисунок 2. Схема этапа распределения векторов по кластерам]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 3&amp;lt;/i&amp;gt; представлен информационный граф вычисления расстояния между двумя векторами &amp;lt;math&amp;gt;x_i = (x_{i1}, ..., x_{id}), i = 1, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^t_j = (\mu^t_{j1}, ..., \mu^t_{jd}) , j = 1, ..., k&amp;lt;/math&amp;gt;. Выполняется операция поэлементного вычитания двух векторов (вершины &amp;lt;i&amp;gt;-&amp;lt;/i&amp;gt;), затем возведение в квадрат (вершины &amp;lt;i&amp;gt;sqr&amp;lt;/i&amp;gt;), затем суммирование полученных результатов (вершина &amp;lt;i&amp;gt;+&amp;lt;/i&amp;gt;). На выходе получаем квадрат расстояния между векторами.&lt;br /&gt;
[[file:K-means-distance.PNG|800px|thumb|center|Рисунок 3. Схема вычисления расстояния между двумя векторами]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 4&amp;lt;/i&amp;gt; представлен информационный граф этапа пересчета центров масс кластеров &amp;lt;math&amp;gt;\mu^t_1, \mu^t_2,..., \mu^t_k&amp;lt;/math&amp;gt;. Для каждого кластера &amp;lt;math&amp;gt;S^t_j, j = 1, ..., k&amp;lt;/math&amp;gt; выполняется операция суммирования (вершина &amp;lt;math&amp;gt;sum_j&amp;lt;/math&amp;gt;) векторов, которые принадлежат данному кластеру. Кроме того, для каждого кластера выполняется операция определения количества элементов в нем (вершина &amp;lt;math&amp;gt;size_j&amp;lt;/math&amp;gt;). Затем выполняется операция деления (вершина &amp;lt;i&amp;gt;/&amp;lt;/i&amp;gt;). На выходе получаем новые центры масс кластеров &amp;lt;math&amp;gt;\mu^{t+1}_1, \mu^{t+1}_2,..., \mu^{t+1}_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k-means-update.PNG|800px|thumb|center|Рисунок 4. Схема этапа пересчета центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма имеет большие возможности для параллелизма:&lt;br /&gt;
* Распределение векторов по кластерам: вектора &amp;lt;math&amp;gt;x_i, x_j, i \neq j; i, j = 1, ..., n&amp;lt;/math&amp;gt; могут быть распределены по кластерам независимо друг от друга;&lt;br /&gt;
* Пересчет центров масс кластеров: центры масс кластеров &amp;lt;math&amp;gt;\mu_i, \mu_j, i \neq j; i, j = 1, ..., k&amp;lt;/math&amp;gt; могут быть пересчитаны независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
Однако, этап пересчета центров масс кластеров зависит от этапа распределения векторов по кластерам, так что эти этапы должны выполняться строго последовательно. Таким образом, параллельная сложность алгоритма может быть вычислена следующим образом: &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;N_{k-means} = i * (N_{distr} + N_{update})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - количество итераций, необходимых для сходимости алгоритма, &amp;lt;math&amp;gt;N_{distr}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;N_{update}&amp;lt;/math&amp;gt; - параллельная сложность этапа пересчета центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассчитаем параллельную сложность &amp;lt;math&amp;gt;N_{distr}&amp;lt;/math&amp;gt; этапа &amp;lt;i&amp;gt;распределения векторов по кластерам&amp;lt;/i&amp;gt; в предположении наличия неограниченного числа процессоров для одной итерации. Для каждой пары векторов &amp;lt;math&amp;gt;(x_i, \mu_j), i = 1,...,n; j = 1, ..., k&amp;lt;/math&amp;gt; операция вычисления расстояния выполняется независимо.&lt;br /&gt;
* При выполнении операция вычисления расстояния операции вычитания и умножения выполняются независимо друг от друга, поэтому требуется только одно вычитание и одно умножение. Число операций сложения: &amp;lt;math&amp;gt;N^{+}_{distr} = O(log(d))&amp;lt;/math&amp;gt; согласно [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритму нахождения частичной суммы элементов массива путем сдваивания]]; &lt;br /&gt;
* Для нахождения минимума среди &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов требуется &amp;lt;math&amp;gt;O(log(k))&amp;lt;/math&amp;gt; операций сравнения.&lt;br /&gt;
Таким образом, получаем, что &amp;lt;math&amp;gt;N_{distr} = O(log(k*d))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассчитаем параллельную сложность &amp;lt;math&amp;gt;N_{update}&amp;lt;/math&amp;gt; этапа &amp;lt;i&amp;gt;пересчета центров масс кластеров&amp;lt;/i&amp;gt; в предположении наличия неограниченного числа процессоров для одной итерации. Нахождение центра масс &amp;lt;math&amp;gt;\mu_j, j = 1, ..., k&amp;lt;/math&amp;gt; кластера &amp;lt;math&amp;gt;S_j&amp;lt;/math&amp;gt; может выполняться независимо от нахождения центров масс других кластеров. Таким образом, получаем, что &amp;lt;math&amp;gt;N_{update} \leq O(log(n))&amp;lt;/math&amp;gt; в худшем случае, когда почти все векторы находятся в одном кластере. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итоговая параллельная сложность алгоритма: &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;N_{k-means} \leq i * (log(k*d) + log(n)) \thicksim O(i * log(k*d*n)); i&amp;lt;/math&amp;gt; - количество итераций, необходимых для сходимости.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Входные данные&amp;lt;/i&amp;gt;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов &amp;lt;math&amp;gt;x_i \in R^d, i = 1, ..., n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* число кластеров &amp;lt;math&amp;gt;k \in N_{[1,n]}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Объем входных данных&amp;lt;/i&amp;gt;:&lt;br /&gt;
&amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; действительных чисел, 1 целое положительное число&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Выходные данные&amp;lt;/i&amp;gt;:&lt;br /&gt;
* вектор меток &amp;lt;math&amp;gt;L \in N^n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Объем выходных данных&amp;lt;/i&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;
# Число кластеров является входным аргументом алгоритма. Таким образом, некорректный выбор данного параметра может привести к плохим результатам работы алгоритма, поэтому зачастую проводят несколько прогонов алгоритма с разными значениями &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, чтобы подобрать оптимальный;&lt;br /&gt;
# Алгоритм может сойтись к локальному минимуму (достижение глобального минимума не гарантируется);&lt;br /&gt;
# Результат сильно зависит от выбора начальных значений центров масс кластеров. Существует улучшенная версия алгоритма - &amp;lt;b&amp;gt;k-means++ &amp;lt;/b&amp;gt;&amp;lt;ref&amp;gt;[http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf Arthur, D.; Vassilvitskii, S. (2007). &amp;quot;k-means++: the advantages of careful seeding&amp;quot;. Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms. Society for Industrial and Applied Mathematics Philadelphia, PA, USA. pp. 1027–1035.]&amp;lt;/ref&amp;gt;, которая предлагает свой способ нахождения начальных оптимальных значений центров масс кластеров;&lt;br /&gt;
# Чувствительность к выбросам и шумам, так как они учитываются при вычислении центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
;[[глоссарий#Устойчивость|&#039;&#039;Устойчивость&#039;&#039;]]: Алгоритм является устойчивым к погрешностям во входных данных, так как при вычислении центров кластеров расстояния между объектами усредняются, что приводит к уменьшению ошибки. Алгоритм также является устойчивым к погрешностям, допускаемых при вычислениях, так как операция сложения на каждой новой итерации выполняется над входными данными. &lt;br /&gt;
&lt;br /&gt;
;Сбалансированность: &amp;lt;p&amp;gt;Множество входных векторов можно равномерно разделить между параллельными вычислителями. Центры всех кластеров можно распространить на все вычислители (так как их заведомо меньше, чем число входных векторов). В таком случае на каждой итерации определение для каждого объекта ближайшего кластера представляет собой вычисление расстояния от каждого входного вектора, находящегося на вычислителе, до каждого кластера, и является сбалансированной операцией. После данного этапа происходит пересчёт центров кластеров. Данная операция является несбалансированной в случаях, когда число  объектов в одном из кластеров значительно больше числа объектов в других кластерах, из-за чего один кластер занимает большое число вычислителей, а остальные занимают по одному или несколько вычислителей. В таком случае для данного кластера потребуется вычислить центр [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритмом нахождения частичной суммы элементов массива путем сдваивания]]. Данная операция представляет несбалансированность порядка &amp;lt;math&amp;gt;O(log (k*n))&amp;lt;/math&amp;gt;. После данного шага происходит сбалансированная операция рассылки центров кластеров на все вычислители.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;В идеальном случае параллелизации алгоритм не является сбалансированным, однако в реальных задачах, когда &amp;lt;math&amp;gt;n &amp;gt;&amp;gt; p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - количество вычислителей, эта несбалансированность сглаживается наличием объектов из каждого кластера на каждом вычислителе&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Соотношение последовательной и параллельной сложности: &amp;lt;math&amp;gt;{O(n*d*k*i) \over O(log(n*d*k)*i)} \thicksim O \bigg( {n*d*k \over log(n*d*k)} \bigg)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]: &amp;lt;p&amp;gt;Последовательный алгоритм: &amp;lt;math&amp;gt;{O(n*d*k*i) \over n*d+n+1} \thicksim {O(d*k*i) \over d+1} \thicksim O(k*i)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число входных векторов, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; - размерность векторов, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число кластеров, &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - число итераций, требуемое для сходимости&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Параллельный алгоритм: &amp;lt;math&amp;gt;O(log(n*d*k)*i) \over n*d+n+1&amp;lt;/math&amp;gt;&amp;lt;/p&amp;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;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского Государственного Университета имени М.В. Ломоносова]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Набор данных для тестирования состоял из 946000 векторов размерности 2 (координаты на сфере)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Набор и границы значений изменяемых параметров запуска реализации алгоритма:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* число процессов (виртуальных ядер) [8 : 512];&lt;br /&gt;
* число кластеров [128 : 384].&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон эффективности реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* минимальная эффективность реализации &amp;lt;math&amp;gt;2,47%&amp;lt;/math&amp;gt; достигается при делении исходных данных на 128 кластеров с использованием 512 процессов;&lt;br /&gt;
* максимальная эффективность реализации &amp;lt;math&amp;gt;7,13%&amp;lt;/math&amp;gt; достигается при делении исходных данных на 352 кластера с использованием 8 процессов.&lt;br /&gt;
На следующих рисунках приведены графики производительности и эффективности выбранной реализации &amp;lt;b&amp;gt;k means&amp;lt;/b&amp;gt; в зависимости от изменяемых параметров запуска.&lt;br /&gt;
[[file:kmeans_performance.jpg|thumb|center|720px|Рисунок 5. Параллельная реализация k means. Максимальная производительность. ]]&lt;br /&gt;
[[file:kmeans_efficiency.jpg|thumb|center|720px|Рисунок 6. Параллельная реализация k means. Максимальная эффективность. ]]&lt;br /&gt;
Исследовалась [https://github.com/serban/kmeans параллельная реализация алгоритма k means]. Для вычисления производительности в неё был вписан код на MPI, реализующий подсчёт числа операций с плавающей точкой: [https://github.com/like-a-bauss/kmeans окончательный текст программы].&lt;br /&gt;
&amp;lt;p&amp;gt;Были получены следующие оценки масштабируемости реализации алгоритма k means:&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;i&amp;gt;По числу процессов:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;-0.02209&amp;lt;/math&amp;gt;. Следовательно, с ростом числа процессов эффективность уменьшается.&lt;br /&gt;
*&amp;lt;i&amp;gt;По размеру задачи:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;0.01252&amp;lt;/math&amp;gt;. Следовательно, с ростом размера задачи (числа кластеров) эффективность увеличивается. &lt;br /&gt;
*&amp;lt;i&amp;gt;Общая оценка:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;-0.00081&amp;lt;/math&amp;gt;. Таким образом, с ростом и размера задачи, и числа процессов эффективность уменьшается.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие Open Source реализации алгоритма:&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de/ ELKI] - содержит реализацию алгоритма k-means на языке Java (в том числе реализацию улучшенного алгоритма k-means++)&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka/ Weka] - содержит реализацию k-means на языке Java&lt;br /&gt;
* [http://mahout.apache.org/ Apache Mahout] - содержит реализацию k-means в парадигме MapReduce&lt;br /&gt;
* [https://spark.apache.org/ Spark Mllib] - содержит распределенную реализацию k-means&lt;br /&gt;
* [http://accord-framework.net/ Accord.NET] - содержит реализацию k-means на C# (в том числе реализацию улучшенного алгоритма k-means++)&lt;br /&gt;
* [https://github.com/mlpack/mlpack MLPACK] - содержит реализацию k-means на языке C++&lt;br /&gt;
* [http://opencv.org/ OpenCV] - содержит реализацию k-means на C++. А также есть обертки для языков Python и Java&lt;br /&gt;
* [https://www.scipy.org/ SciPy] - содержит реализацию k-means на языке Python&lt;br /&gt;
* [http://scikit-learn.org/ Scikit-learn] - содержит реализацию k-means на языке Python&lt;br /&gt;
* [http://julialang.org/ Julia] - содержит реализацию алгоритма k-means на языке Julia&lt;br /&gt;
* [https://www.gnu.org/software/octave/ Octave] - содержит реализацию k-means на языке Octave&lt;br /&gt;
* [https://www.r-project.org/ R] - содержит реализацию k-means на языке R&lt;br /&gt;
* [http://torch.ch/ Torch] - содержит реализацию k-means на языке Lua&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:IanaV&amp;diff=19019</id>
		<title>Обсуждение участника:IanaV</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:IanaV&amp;diff=19019"/>
		<updated>2016-11-23T11:53:05Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;Для вычисления производительности в неё был вписан код на MPI, реализующий подсчёт числа операций с плавающей точкой&amp;quot; - вписан Вами? Тогда где эта реализация? Если нет, зачем это отдельно отмечать?&lt;br /&gt;
* К графикам нет внятных пояснений.&lt;/div&gt;</summary>
		<author><name>Zhum</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:Astandrik/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19018</id>
		<title>Участник:Astandrik/Алгоритм кластеризации, основанный на минимальном остовном дереве</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:Astandrik/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19018"/>
		<updated>2016-11-23T11:43:50Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на минимальном остовном дереве&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O(N^2 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;
Основные авторы описания: [[Участник:Astandrik|Стандрик Антон Сергеевич]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм предназначен для решения задач кластеризации. Говоря в общем, кластеризация — многомерная статистическая процедура, выполняющая сбор данных, содержащих информацию о выборке объектов, и затем упорядочивающая объекты в сравнительно однородные группы &amp;lt;ref&amp;gt;Классификация и кластер. Под ред. Дж. Вэн Райзина. М.: Мир, 1980. 390 с.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Впервые идея использования минимального остовного дерева для кластеризации данных была преждожена C. T. Zah в 1971 году&amp;lt;ref name=zakh&amp;gt;C. T. Zahn, “graph-Theriotical methos for detecting and describing getalt clusters,” IEEE Trans. Computers, vol. 20, no. 1, pp. 68-86, 1971 &amp;lt;/ref&amp;gt;. Этот метод широко используется исследователями при анализе изображений &amp;lt;ref&amp;gt;Y. Xu, V. Olman and E. C. Uberbacher, “A mentation using minimum spanning trees,” Image and Vission Computing, vol. 15, pp. 47-57,1997.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Zhi Min Wang, Yeng Chai Soh, Qing Song, Kang Sim, “Adaptive spatial information-theoretic clustering for image segmentation,” Pattern Recognition, vol. 42, no. 9, pp. 2029-2044, 2009.&amp;lt;/ref&amp;gt;, а также при анализе биологических данных &amp;lt;ref&amp;gt;Y. Xu, V. Olman and D. Xu, “Clustering gene expression data using a graph-Theriotic approach: An application of minimum spanning trees,” Bioinformatics, vol. 18, no. 4, pp. 536-545, 2002.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем описании работы алгоритма можно выделить следующие шаги:&lt;br /&gt;
&lt;br /&gt;
# Разбиение входных векторов на пары и вычисление расстояний в них. Для этого небходим выбор метрики пространства, в котором осуществляется кластерный анализ. В качестве примеров метрик можно привести:&lt;br /&gt;
#* Метрика Евклида: &amp;lt;math&amp;gt;d_{ij} = \sqrt{\sum^n_{k=1}(x_{ik} - x_{jk})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метрика city-block (манхэттенская метрика): &amp;lt;math&amp;gt;d_{ij} = \sum^n_{k=1}|x_{ik} - x_{jk}| &amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метрика Брея-Картиса: &amp;lt;math&amp;gt;d_{ij} = \sum^n_{k=1}\frac{|x_{ik} - x_{jk}|}{|x_{ik} + x_{jk}| }&amp;lt;/math&amp;gt;&lt;br /&gt;
# Построение минимального остовного дерева по заданному множеству. Существует три основных алгоритма [[Построение минимального остовного дерева (MST) | построения минимального остовного дерева]]:&lt;br /&gt;
#* [[Алгоритм Прима]]&lt;br /&gt;
#* [[Алгоритм Крускала]]&lt;br /&gt;
#* [[Алгоритм Борувки]]&lt;br /&gt;
# Разделение полученного дерева на несвязное множество поддеревьев в соответствии с некоторой стратегией. Можно выделить три популярные стратегии кластеризации:&lt;br /&gt;
#* SEMST&amp;lt;ref&amp;gt;Asano, M.K.T., Bhattacharya, B., Yao, F.: Clustering algorithms based on minimum and maximum spanning trees. In: In Proceedings of the fourth annual symposium on Computational Geometry. (1998) 252–257&amp;lt;/ref&amp;gt; (standard Euclidean minimum spanning tree clustering algorithm) - предполагается, что нам заранее известно число кластеров. В таком случае, если число кластеров равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, то из полученного в первом пункте остовного дерева удаляются &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; связей с наибольшим весом.&lt;br /&gt;
#* CEMST&amp;lt;ref&amp;gt;Ye, B., Chao, K.M.: Spanning Trees and Optimization Problems. Chapman and&lt;br /&gt;
Hall (2004)&amp;lt;/ref&amp;gt;(The maximum cost minimum spanning tree) - Работает так же как и SEMST, однако вместо веса вершины используется её стоимость, которая вычисляется следующим образом: Пусть ребро &amp;lt;math&amp;gt;e = (x_{i}, x_{j})&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;cost(e) = w(e) * degree(x_{i}) * degree(x_{j})&amp;lt;/math&amp;gt;. Далее, зная число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и стоимости рёбер, удаляются &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; рёбер с максимальной стоимостью.&lt;br /&gt;
#* ZEMST&amp;lt;ref name=zakh /&amp;gt; (The Zahn’s minimum spanning tree) - от двух предыдущих эта стратегия отличается тем, что здесь число кластеров не известно заранее, а вычисляется в процессе кластеризации. Его суть в том, что удаляются те рёбра, веса которых значительно превосходят средний вес ребра в соседних поддеревьях.&lt;br /&gt;
&lt;br /&gt;
==== Достоинства ====&lt;br /&gt;
&lt;br /&gt;
Основным достоинством алгоритма является его способность выделять кластеры произвольной формы. На следующем примере можно видеть, как алгоритм даже в случае SEMST алгоритма корректно разбивает вложенные окружности на три кластера:&lt;br /&gt;
&lt;br /&gt;
[[File:Selection_117_.png|frame|center|alt=Point-connected clusters| Слева направо: Рисунок 1: Начальные данные. Рисунок 2: построенное минимальное остовное дерево. Рисунок 3: разбиение на кластеры путём удаления самых длинных связей.]]&lt;br /&gt;
&lt;br /&gt;
==== Недостатки ====&lt;br /&gt;
&lt;br /&gt;
Недостатком является трудность определения четких критериев, при которых осуществляется разбиение данных на разные кластеры.&lt;br /&gt;
&lt;br /&gt;
Кроме того, существуют наборы данных, на которых популярные алгоритмы (SEMST, CEMST, ZEMST) не дают правильного результата, и для которых требуется применение более сложных алгоритмов, к числу которых можно отнести, например, алгоритм HEMST &amp;lt;ref&amp;gt;Clustering with Minimum Spanning Trees Yan Zhou, Oleksandr Grygorash, Thomas F. Hain, Meador Inge Zach Jorgensen School of Computer and Information Sciences University of South Alabama, Mobile, AL 36688 USA Urban Insight Inc. 5657 Wilshire Blvd Ste 290, Los Angeles, CA 90036&amp;lt;/ref&amp;gt; с временем работы &amp;lt;math&amp;gt;O(n^2 * log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В качестве иллюстрации можно привести два похожих визуально, но различных с точки зрения разбиения на кластеры с помощью описываемого алгоритма, примера. (В приведенных примерах первый рисунок обозначает начальный набор данных. Второй - построенное на основе данных минимальное остовное дерево. Третий рисунок - разбиение на кластеры.):&lt;br /&gt;
&lt;br /&gt;
[[File:Selection_116_.png|frame|center|alt=Point-connected clusters| Правильное разбиение на два кластера.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Selection_115_.png|frame|center|alt=Point-connected clusters| Неверное разбиение на один кластер.]]&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; : &amp;lt;math&amp;gt;(a_1, a_2, ..., a_n)&amp;lt;/math&amp;gt;, где   &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,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;E&amp;lt;/math&amp;gt; - множество рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин.&lt;br /&gt;
* &amp;lt;math&amp;gt;G = (V,E) &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;
* &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt; (Minimum Spanning Tree) - минимальное остовное дерево графа &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;. Остовное дерево — ациклический связный подграф данного связного неориентированного графа, в который входят все его вершины. Минимальное остовное дерево в связанном взвешенном неориентированном графе — это остовное дерево этого графа, имеющее минимальный возможный вес, где под весом дерева понимается сумма весов входящих в него рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;MSF(G)&amp;lt;/math&amp;gt; (Minimum Spanning Forest) - набор минимальных остовных деревьев для всех компонент связности графа &amp;lt;math&amp;gt; G &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В описании алгоритмов этой статьи часто используется термин &amp;quot;построение MST&amp;quot;, подразумевая, что на самом деле в результате построения может получиться как одна компонента связности (MST), так и множество компонент связности (MSF).&lt;br /&gt;
&lt;br /&gt;
==== Алгоритмы кластеризации ====&lt;br /&gt;
&#039;&#039;&#039;SEMST&#039;&#039;&#039;(standard Euclidean minimum spanning tree) - простейший алгоритм, основанный на заранее известном числе кластеров &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;. Путём удаления &amp;lt;math&amp;gt; k - 1 &amp;lt;/math&amp;gt; связей с наибольшим весом предполагается разбиение остовного дерева на нужное количество кластеров. Иными словами минимальное остовное дерево разбивается на &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;  поддеревьев: &amp;lt;math&amp;gt; G = \{G_1,G_2,...,G_k\} &amp;lt;/math&amp;gt; путём минимизации суммы всех весов: &amp;lt;math&amp;gt; W = \sum ^k_{l=1} \sum _{x_i,x_j \in V_l} w_{x_i,x_j} -&amp;gt; min &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; V_l &amp;lt;/math&amp;gt; - множество вершин в поддереве &amp;lt;math&amp;gt; G_l &amp;lt;/math&amp;gt;. В конце такой процедуры каждое множество &amp;lt;math&amp;gt; V_l &amp;lt;/math&amp;gt; становится кластером &amp;lt;math&amp;gt; C_l &amp;lt;/math&amp;gt;. Поскольку (в случае использования алгоритма Крускала для построения остовного дерева) рёбра перед началом кластеризации уже отсортированы, для удаления наибольших &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; рёбер требуется &amp;lt;math&amp;gt; O(k) &amp;lt;/math&amp;gt; операций, и для распределения получившихся вершин по кластерам требуется время &amp;lt;math&amp;gt; O(n) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CEMST&#039;&#039;&#039;(maximum cost Euclidean minimum spanning tree) - Работает в точности, как и SEMST, но здесь вместо весов рёбер используется т.н. &amp;quot;стоимость&amp;quot;, которая высчитывается следующим образом: если ребро &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt; соединяет вершины &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; x_j &amp;lt;/math&amp;gt;, то его &amp;quot;стоимость&amp;quot; считается как: &amp;lt;math&amp;gt; Cost_{x_i, x_j} = w_{x_i,x_j} * Deg(x_i) * Deg(x_j) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; Deg(x_i) &amp;lt;/math&amp;gt; - степень вершины &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt; (т.е.  количество рёбер графа G, инцидентных вершине &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt;). В остальном алгоритм действует точно так же, как и SEMST, удаляя &amp;lt;math&amp;gt; k-1 &amp;lt;/math&amp;gt; рёбер с наибольшей &amp;quot;стоимостью&amp;quot;. Сложность &amp;lt;math&amp;gt; O(k) &amp;lt;/math&amp;gt; . Алгоритм направлен на удаление рёбер, соединяющих высоко нагруженные (с точки зрения количества инцидентных рёбер) вершины.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ZEMST&#039;&#039;&#039; (The Zahn’s maximum spanning tree (Zahn, 1971)) &amp;lt;ref&amp;gt; Attribute Clustering Based on Heuristic Tree Partition Jorge Cordero H. and Yifeng Zeng Department of Computer Science Aalborg University 9220 Aalborg, Denmark&amp;lt;/ref&amp;gt; - отличается от двух предыдущих алгоритмов в первую очередь тем, что здесь количество кластеров заранее не известно, но определяется исходя из данных. Данный алгоритм не является жадным, в отличие от первых двух, так как в нём рассматривается не только само ребро и его вес, а также и поддеревья, которые соединены этим ребром. То есть для ребра &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt; рассматриваются поддеревья &amp;lt;math&amp;gt; N_i = (V_{N_i}, E_{N_i}), N_j = (V_{N_j}, E_{N_j}) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; N_i &amp;lt;/math&amp;gt; - поддерево  глубины(высоты) &amp;lt;math&amp;gt; d &amp;lt;/math&amp;gt;(величина d определяется эмпирически), состоящее из всех достижимых из вершины &amp;lt;math&amp;gt; x_j &amp;lt;/math&amp;gt; вершин за &amp;lt;math&amp;gt; d &amp;lt;/math&amp;gt; шагов через все достижимые из неё рёбра (исключая само ребро &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt;). &lt;br /&gt;
Введём обозначения:&lt;br /&gt;
*&amp;lt;math&amp;gt;\overline{w_{N_i}} = \frac{\sum_{(x_r, x_s) \in E_{N_j}}w_{x_r,x_s}}{|E_{N_j}|} &amp;lt;/math&amp;gt; - средний вес ребра в поддереве &amp;lt;math&amp;gt; N_i &amp;lt;/math&amp;gt;&lt;br /&gt;
*&amp;lt;math&amp;gt;\sigma_{N_i} = \sqrt{\frac{\sum_{(x_r, x_s) \in E_{N_j}}(w_{x_r,x_s} - \overline{w_{N_i}})^2}{|E_{N_j}|}} &amp;lt;/math&amp;gt; - стандартное отклонение веса ребра в поддереве &amp;lt;math&amp;gt; N_i &amp;lt;/math&amp;gt;&lt;br /&gt;
Ребро &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt; удаляется из списка в случае, если выполняется хотя бы одно из двух условий:&lt;br /&gt;
#&amp;lt;math&amp;gt; w_{x_i,x_j} &amp;gt; \overline{w_{N_i}} - \sigma_{N_i} &amp;lt;/math&amp;gt;&lt;br /&gt;
#&amp;lt;math&amp;gt; w_{x_i,x_j} &amp;gt; \overline{w_{N_j}} - \sigma_{N_j} &amp;lt;/math&amp;gt;&lt;br /&gt;
#&amp;lt;math&amp;gt; w_{x_i,x_j} &amp;gt; \frac{\sum_{(x_r, x_s) \in E_i \cup E_j}w_{x_r,x_s}}{|E_i \cup E_j|} &amp;lt;/math&amp;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(Nd) &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; O(n^2) &amp;lt;/math&amp;gt; &lt;br /&gt;
# Поиск минимального остовного дерева. Последовательная сложность: &amp;lt;math&amp;gt; O(E*log(E)) &amp;lt;/math&amp;gt;&lt;br /&gt;
# Разбиение остовного дерева на кластеры. Последовательная сложность зависит от реализации. (от &amp;lt;math&amp;gt; O(1) &amp;lt;/math&amp;gt; в случае SEMST до &amp;lt;math&amp;gt; O(n^2) &amp;lt;/math&amp;gt; в случае ZEMST).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В реализации алгоритма используются следующие структуры данных:&lt;br /&gt;
#&#039;&#039;&#039;Узел&#039;&#039;&#039;(Node)(Вершина) - Представляется набором координат в выбранном пространстве.&lt;br /&gt;
#&#039;&#039;&#039;Ребро&#039;&#039;&#039;(Edge) - Представляется: двумя индексами вершин, которые соединяет; своим весом; флагом, который указывает, входит ли данное ребро в минимальное остовное дерево.&lt;br /&gt;
&lt;br /&gt;
В дальнейшем описании алгоритма используются следующие макрооперации:&lt;br /&gt;
#Вычисление расстояния между вершинами.&lt;br /&gt;
#Добавление ребра в граф - Осуществляется путём выставление специального флага у соответствующего ребра в true;&lt;br /&gt;
#Удаление ребра из графа - Операция, обратная к добавление: соответствующий флаг устанавливается в false. В начальный момент времени у всех рёбер флаг установлен в false, что означает отсутствие рёбер в графе. &lt;br /&gt;
&lt;br /&gt;
Следующие три используемые операции относятся к реализации алгоритма Крускала при помощи леса непересекающихся множеств. Подробнее об этой структуре и связанных операциях можно почитать, например, здесь &amp;lt;ref&amp;gt; Томас Кормен и др. Алгоритмы: построение и анализ = INTRODUCTION TO ALGORITHMS. — 2-е изд. — М.: «Вильямс», 2006. — С. 1296. — ISBN 0-07-013151-1. (Глава 21. Раздел 21.3: &amp;quot;Леса непересекающихся множеств) &amp;lt;/ref&amp;gt;, в данном пункте они будут только перечислены:&lt;br /&gt;
#Создание дерева с одним узлом - Make-Set&lt;br /&gt;
#Объединение компонент связности - Union&lt;br /&gt;
#Поиск компонент связности - Find-Set&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
int N;        // Размер входных данных&lt;br /&gt;
int number_of_clusters;        // Число кластеров&lt;br /&gt;
int size = N* (N-1) / 2;        // Количество связей в полном графе&lt;br /&gt;
Node nodes[N];        // Вершины(точки), заданные начальными условиями&lt;br /&gt;
Edge edges[size];        // Все расстояния между вершинами &lt;br /&gt;
vector&amp;lt;Edge&amp;gt; MST;        // Рёбра минимального остовного дерева&lt;br /&gt;
read_data(&amp;amp;nodes, file_name);        // Считывание начальных данных&lt;br /&gt;
&lt;br /&gt;
//----------------Часть 1--------------//&lt;br /&gt;
for(int i = 0; i &amp;lt; N-1; i++) {&lt;br /&gt;
  for(int j = i+1; j &amp;lt; N; j++) {&lt;br /&gt;
	double distance = getDistance(node[i], node[j]);        // Вычисление расстояния между вершинами&lt;br /&gt;
	edges[i*N + j] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//----------------Часть 2--------------//&lt;br /&gt;
sort(edges, size);        // Сортировка рёбер графа в порядке возрастания&lt;br /&gt;
for(int i = 0; i &amp;lt; size; i++) {        // Обходим список рёбер по возрастанию &lt;br /&gt;
	if(find_set(edges[i].first) != find_set(edges[i].second)) {        // Если вершины принадлежат разным подграфам - связываем их&lt;br /&gt;
          Union(edges[i].first, edges[i].second);        // Если вершины принадлежат разным подграфам - связываем их&lt;br /&gt;
	  MST.push_back(edges[i]);        // Запись использованного ребра в минимальное остовное дерево&lt;br /&gt;
	  edges[i]-&amp;gt;is_in_MST = true;     // Запись использованного ребра в минимальное остовное дерево&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//----------------Часть 3--------------//&lt;br /&gt;
for(int i = MST.size() - 1; i &amp;gt; MST.size() - 1 - number_of_clusters; i--) {&lt;br /&gt;
	remove_edge(MST, i);        // Удаление самых длинных рёбер из MST - формирование кластеров&lt;br /&gt;
}&lt;br /&gt;
write_data(MST);        // Запись выходных данных&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как описано в пункте 1.3, весь описываемый алгоритм (начиная от момента получения данных и заканчивая моментом, когда данные отданы на вывод), может быть условно разделён на три части. В то время как первая часть может быть достаточно эффективно решена &amp;quot;в лоб&amp;quot;, вторая и третьи части, как указано в пункте 1.1, могут иметь альтернативы. В данном фрагменте кода подробно описано использование [[Алгоритм Крускала | Алгоритма Крускала]] для построения MST, и алгоритма SEMST для разбиения на кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как и в остальных пунктах, здесь сложность алгоритма состоит их суммы сложностей трёх его составных частей. Если оценивать сложность асимптотически в зависимости от входных данных, то:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T(n) = O(n^2) + O(\frac{n (n-1)}{2})log(\frac{n (n-1)}{2})) + O(1) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На первый взгляд второе слагаемое может показаться слишком большим. Однако, верны и следующие равенства:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; O(\frac{n (n-1)}{2} log(\frac{n (n-1)}{2}) = O(n^2 log(n^2)) = O(2 n^2 log(n)) = O(n^2 log(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^2 log(n)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:Graph_algo10.png|frame|center|alt=Алгоритм| Макро-описание параллельного алгоритма кластеризации]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Данная задача хорошо распараллеливается благодаря тому, что все &amp;quot;тяжелые&amp;quot; вычислительные задачи, начиная с момента считывания входных данных, можно распределить по процессам практически равномерно. Это достигается за счет следующей реализации:&lt;br /&gt;
#Мастер-процесс считывает входные данные и отправляет их остальным процессам с помощью MPI_Broadcast.&lt;br /&gt;
#Все пары точек разбиваются так, что каждому процессу достается одинаковое количество пар.&lt;br /&gt;
#Каждый процесс-рабочий строит MST на своём наборе пар точек.&lt;br /&gt;
#Процессы объединяют свои деревья по алгоритму, изображенному в информационном графе. То есть объединение осуществляется последовательно между группами размера &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; k \in 1,2,..log_2p&amp;lt;/math&amp;gt;, где p - число процессов.&lt;br /&gt;
&lt;br /&gt;
Таким образом &#039;&#039;&#039;высота Ярусно-Параллельной формы&#039;&#039;&#039; (при наличии &amp;lt;math&amp;gt; p = 2^i &amp;lt;/math&amp;gt; процессоров) будет &amp;lt;math&amp;gt; 1 + i + 1 + 1 &amp;lt;/math&amp;gt;. &lt;br /&gt;
В то же время, как будет показано в пункте 1.10, оптимальное количетво процессоров может быть оценено по формуле  &amp;lt;math&amp;gt; p(N) \approx \sqrt {\frac{N-1}{2}} &amp;lt;/math&amp;gt;. Тогда &#039;&#039;&#039;ширину Ярусно-Параллельной формы&#039;&#039;&#039; можно оценить как &amp;lt;math&amp;gt; O(\sqrt{N}) &amp;lt;/math&amp;gt;. Учитывая приближенную формулу для числа процессоров, оценим высоту ЯП при помощи соотношения &amp;lt;math&amp;gt; 2^i \approx \sqrt {\frac{N-1}{2}} &amp;lt;/math&amp;gt;. Тогда высоту ЯП можно оценить как &amp;lt;math&amp;gt; O(log(N)) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Говоря о праллельной реализации вообще и, в частности, обращаясь к информационному графу, видно, что в основе параллельного алгоритма лежит массовый параллелизм, и единственным проблематичным местом может оказаться самый начальный этап считывания данных мастер-процессом с последующей рассылкой их всем процессам-рабочим. Проблема в том, что объем данных, пересылаемых каждому процессу, практически нельзя уменьшить из-за того, что в расчете MST на каждом наборе рёбер потенциально могут участвовать все вершины графа, и поэтому для их обработки требуется знание их пространственных координат.&lt;br /&gt;
&lt;br /&gt;
Самой &amp;quot;тяжелой&amp;quot; операцией здесь является самый первый расчёт MST, поскольку он выполняется чуть больше чем за квадратичное время. Все последующие объединения деревьев занимают асимптотически одинаковое количество процессорного времени и не зависят друг от друга (вплоть до последнего объединения в MST).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Входные данные ====&lt;br /&gt;
В качестве входных данных программе дается множество векторов &amp;lt;math&amp;gt;  [x_1,x_2,...,x_N]&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; x_j = (a_{1j}, a_{2j}, ..., a_{nj})&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Для удобства изучения свойств алгоритма и отображения результатов можно брать массив векторов размерности 2. В этом случае наглядность будет обеспечена естественной возможностью отобразить вектора в плоскости.&lt;br /&gt;
&lt;br /&gt;
Кроме того может оказаться полезным особым образом подготовить данные перед анализом (удаление &amp;quot;шумов&amp;quot;, выбросов и масштабирование данных по тем или иным координатам).&lt;br /&gt;
==== Выходные данные ====&lt;br /&gt;
Выходными данными программы будет множество &amp;lt;math&amp;gt; M = ((x_1,x_2,..,x_k),(x_{k+1},...),...) &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;(x_{i}, Cluster-Number)&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; p = 2^i &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; i \in \mathbb {N} &amp;lt;/math&amp;gt;. Ясно, что такое допущение не повлияет на качество анализа, так как другое число процессоров лишь приведет к незначительным накладным расходам, сложность которых асимптотически стремится к нулю. В таком случае можно также ввести допущение о размере входных данных: &amp;lt;math&amp;gt; N = 2^j &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; j \in \mathbb {N}, j &amp;gt; i &amp;lt;/math&amp;gt;. В этом случае MST-часть дерева графа алгоритма является идеально сбалансированным бинарным деревом. Тогда сложность можно оценить следующим образом:&lt;br /&gt;
#На первом этапе каждый процесс производит расчет на &amp;lt;math&amp;gt; 2^{j - i} * (2^j - 1)&amp;lt;/math&amp;gt; рёбрах, получая локальное MST(или MSF) на не более чем &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt; вершинах. &lt;br /&gt;
#Далее следует &amp;lt;math&amp;gt; i = log_2(2^i) &amp;lt;/math&amp;gt; шагов объединения не более чем по N рёбер в каждом.&lt;br /&gt;
&lt;br /&gt;
Таким образом, задача эффективного запуска алгоритма состоит в том, чтобы найти оптимальный баланс между количеством расчетов на каждом узле на первом шаге и общим количеством объединений поддеревьев. Пусть дано &amp;lt;math&amp;gt; p = 2^i &amp;lt;/math&amp;gt; вычислительных узлов и &amp;lt;math&amp;gt; N = 2^j &amp;lt;/math&amp;gt;. В таком случае каждый узел получает считает &amp;lt;math&amp;gt; 2^{j - i} * (2^j - 1)&amp;lt;/math&amp;gt; рёбер на первом шаге. Высота части ЯП-формы, отвечающей за MST, равна &amp;lt;math&amp;gt; i &amp;lt;/math&amp;gt;. Тогда всего будет &amp;lt;math&amp;gt; i &amp;lt;/math&amp;gt; уровней объединений MST с числом рёбер не более чем &amp;lt;math&amp;gt; 2N &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;  T&#039;(N,p) = \frac{N(N - 1)}{2 p} * logN  + 2N*log(N) * log_2p&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Объяснение формулы: первое слагаемое, исходя из оценки для сложности алгоритма Прима: &amp;lt;math&amp;gt; O(ElogV) &amp;lt;/math&amp;gt;, - время(число операций) расчета на каждом процессоре на первом шаге. После первого шага (поскольку построено минимальное остовное дерево) каждый процесс передает не более чем &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt; рёбер дальше. Второе слагаемое: каждый процесс после первого шага получает не более чем &amp;lt;math&amp;gt; 2N &amp;lt;/math&amp;gt; рёбер (от двух процессоров при объединении), и всего таких шагов объединения не более чем &amp;lt;math&amp;gt; log_2p &amp;lt;/math&amp;gt;, при этом предполагается, что сложностью объединения &amp;lt;math&amp;gt; 2N &amp;lt;/math&amp;gt; рёбер на каждом шаге можно пренебречь (или допустить его пренебрежительно малым), поскольку предполагается, что все процессы после первого шага получают свою часть уже отсортированных рёбер(то есть наибольшее ребро процессора &amp;lt;math&amp;gt; i &amp;lt;/math&amp;gt; меньше наименьшего ребра процессора &amp;lt;math&amp;gt; i+1 &amp;lt;/math&amp;gt;), и объединение будет просто конкатенацией блоков памяти.&lt;br /&gt;
&lt;br /&gt;
С помощью этой формулы можно построить графики зависимости времени вычислений от количества процессоров и входных данных.Поскольку рост вычислительного времени при увеличении объёма входных данных достаточно очевиден, этот объём будет зафиксирован, и будет исследоваться поведение приближенного априорного вычислительного времени алгоритма в зависимости от числа процессоров. Проведём два вычислительных эксперимента:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt; N = 10^4&amp;lt;/math&amp;gt;&lt;br /&gt;
[[File:График1_.JPG|frame|center|alt=Алгоритм| По оси абсцисс: количество вычислительных узлов. По оси ординат: время вычислений. При фиксированном &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt; N = 1.5*10^4 &amp;lt;/math&amp;gt;&lt;br /&gt;
[[File:График2_.JPG|frame|center|alt=Алгоритм| По оси абсцисс: количество вычислительных узлов. По оси ординат: время вычислений. При фиксированном &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Из этих графиков становится видно качественное поведение времени вычислений при увеличении числа процессоров. Можно заметить, что для каждого набора входных данных есть число процессоров, минимизирующее время вычислений. Но в то же время видно, что при слишком большом количестве процессоров алгоритм начинает работать хуже, чем при меньшем. Это вполне понятно: Слишком большое число процессоров порождает большое число обменов между ними, объединений деревьев, сортировок, и т.д. Таким образом, именно эта часть &amp;lt;math&amp;gt;  2N*log(N) * log_2p &amp;lt;/math&amp;gt; оценки времени вычислений отвечает за ухудшение эффективности алгоритма при числе процессоров, которое больше оптимального. Кроме того, видно, что при увеличении объёма входных данных оптимальное количество процессоров также увеличивается: количество обменов и слияний деревьев компенсируется уменьшением вычислительной нагрузки на каждый отдельный процессор.&lt;br /&gt;
&lt;br /&gt;
Благодаря такой оценке становится возможным оценить примерное оптимальное число процессоров &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; в зависимости от объёма входных данных &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;. Продифференцировав приближенную формулу &amp;lt;math&amp;gt; T&#039;(N,p) = \frac{N(N - 1)}{2 p} * logN  + 2N*log(N) * log_2p &amp;lt;/math&amp;gt; по &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; и приравняв производную нулю, получим следующее выражение для оптимального числа процессоров:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; p(N) \approx \frac{N-1}{4} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Вычислительная мощность параллельного алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Согласно определению, [[глоссарий#Вычислительная мощность|&#039;&#039;вычислительная мощность&#039;&#039;]] алгоритма равна отношению числа операций к суммарному объему входных и выходных данных. В то же время, учитывая вышесказанное, видно, что число операций в данном алгоритме зависит не только от объема входных данных, но также от числа процессоров. Таким образом, имеет смысл оценить вычислительную мощность как в случае оптимального числа процессоров, так и в общем случае числа процессоров, равного &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем случае, исходя из предыдущего пункта, число операций будет равно&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \frac{N(N - 1)}{2} * logN  + \sum_{i = 0}^{log_2p}(2N*log(N) * \frac{p}{2^i}) = N logN (\frac{N-1}{2} + 2 * p * \sum_{i = 0}^{log_2p}( \frac{1}{2^i})) = &amp;lt;/math&amp;gt; {переходя к пределу в сумме} &amp;lt;math&amp;gt; \approx  N logN (\frac{N-1}{2} + 2 * p) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, вычислительная мощность в общем случае: &amp;lt;math&amp;gt; \approx \frac{N logN (\frac{N-1}{2} + 2 * p)}{2N} = \frac{logN *(\frac{N-1}{2} + 2 * p)}{2} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В случае оптимального числа вычислительных узлов:  &amp;lt;math&amp;gt; \approx \frac{N logN (\frac{N-1}{2} + \frac{N-1}{2})}{2N} =  \frac{logN *  (N-1)}{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;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
В настоящей статье приводится исследование самостоятельной реализации алгоритма: [http://pastebin.com/gH09k6id main.cpp]. Запуск производился на системе IBM Blue Gene/P. &lt;br /&gt;
Для генерации данных использовался скрипт на питоне [http://pastebin.com/5YNxqqpA testGenerator]. Для отображения кластеров использовалась библиотека matplotlib и скрипт [http://pastebin.com/mTDZ6bqp graphDraw]. &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;
&lt;br /&gt;
Поскольку оптимальное число процессоров достаточно велико и линейно зависит от объема(размера) входных данных, задача хорошо масштабируется, и на графике ниже можно увидеть почти линейный рост производительности при фиксированном количестве входных данных и увеличении числа процессоров в том случае, когда число процессоров близко к оптимальному. В тоже время из графика видно, что когда входных данных слишком мало, а число процессоров возрастает, то происходит либо небольшой рост производительности, либо роста производительности нет.&lt;br /&gt;
&lt;br /&gt;
[[File:Performance3.JPG|frame|center|alt=Алгоритм| Производительность алгоритма]]&lt;br /&gt;
&lt;br /&gt;
Говоря о вычислительном времени и проводя тесты на реальных данных, можно заметить концептуальную правильность сделанных качественных выводов о поведении времени вычислений для различного набора входных данных и числа процессоров. На графиках ниже видно, что время вычисления задачи уменьшается до определенного момента, когда количество процессоров, нужное для задачи, становится слишком большим, после чего начинает увеличиваться, когда число процессоров растет.&lt;br /&gt;
&lt;br /&gt;
 [[File:Timeline1.JPG|frame|center|alt=Алгоритм| Вычислительное время алгоритма для &amp;lt;math&amp;gt;N = 17000&amp;lt;/math&amp;gt;]]&lt;br /&gt;
 [[File:Снимок2.JPG|frame|center|alt=Алгоритм| Вычислительное время алгоритма для &amp;lt;math&amp;gt;N = 3000&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;
Как таковые, алгоритмы самого процесса кластеризации в популярных библиотеках (таких, как Scipy, Numpy или Boost) не реализованы (в отличие от других алгоритмов кластеризации). Однако, можно найти пользовательские любительские реализации (некоторые из которых приведены в списке ниже). Кроме того, существует масса реализаций основного коспонента данного алгоритма - вычисления MST - на многих языках программирования в разных библиотеках. Ссылки на некоторые из этих реализаций(перечислить все было бы невозможно) также приведены в списке ниже.&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/jakevdp/mst_clustering  Minimum Spanning Tree Clustering in Python  Author: Jake VanderPlas]&lt;br /&gt;
*[https://github.com/amueller/information-theoretic-mst Information Theoretic Clustering using Minimum Spanning Trees]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_51_0/libs/graph/doc/prim_minimum_spanning_tree.html C++ Boost library prim_minimum_spanning_tree]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_35_0/libs/graph/doc/kruskal_min_spanning_tree.html C++ Boost library kruskal_minimum_spanning_tree]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Java library JGraphT PrimMinimumSpanningTree]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Java library JGraphT  KruskalMinimumSpanningTree] &lt;br /&gt;
*[http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.sparse.csgraph.minimum_spanning_tree.html SciPy library MST]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:SenderovichNikita/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BF%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B8_%D0%BA%D0%B0%D1%80%D0%BA%D0%B0%D1%81%D0%B0&amp;diff=19014</id>
		<title>Участник:SenderovichNikita/Алгоритм кластеризации, основанный на построении каркаса</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:SenderovichNikita/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BF%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B8_%D0%BA%D0%B0%D1%80%D0%BA%D0%B0%D1%81%D0%B0&amp;diff=19014"/>
		<updated>2016-11-23T11:12:37Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = MST-Кластеризация &lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N^2\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log^2 N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор статьи: [[Участник:SenderovichNikita|Сендерович Никита Леонидович]]&lt;br /&gt;
&lt;br /&gt;
Данный документ содержит описание алгоритма кластеризации, основанного на построении [[Построение минимального остовного дерева (MST)|минимального остовного дерева]] (каркаса, остова, кратчайшего незамкнутого пути, минимального покрывающего дерева, англ. Minimum Spanning Tree (MST)) - эвристического графового алгоритма построения разбиения множества объектов на такие непересекающиеся подмножества объектов, что объекты, принадлежащие одному подмножеству, схожи по заданной метрике в большей степени, чем объекты, принадлежащие разным группам.&lt;br /&gt;
&lt;br /&gt;
Как и все алгоритмы кластеризации, данный алгоритм может быть применён для решения задач кластерного анализа, выделения типовых и нетипичных объектов в рассматриваемой выборке объектов. Его достоинствами по сравнению с другими аналогичными алгоритмами машинного обучения являются наглядность, идейная простота, возможность контролировать число кластеров. Главным недостатком является ограниченная применимость: алгоритм лучше всего подходит для выделения кластеров типа сгущений или лент, наличие разреженного фона или узких перемычек между кластерами может приводить к неадекватному с точки зрения аналитика результату&amp;lt;ref&amp;gt;[http://www.ccas.ru/voron/download/Clustering.pdf Воронцов К.В. &amp;quot;Лекции по алгоритмам кластеризации и многомерного шкалирования&amp;quot;]&amp;lt;/ref&amp;gt;. Другим недостатком является высокая вычислительная сложность: на выборках более 100 тыс. объектов без применения параллельных вычислений применить алгоритм невозможно. Рассмотрению соответствующих параллельных алгоритмов и посвящена данная статья.&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Рассматривается выборка &amp;lt;math&amp;gt;X = \{x_i\}, i = 1, 2, \ldots, N&amp;lt;/math&amp;gt;, состоящая из &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; объектов с заданной симметричной неотрицательной функцией расстояния &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt;. Данную выборку можно рассматривать как связный неориентированный взвешенный граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, множество вершин которого совпадает со множеством объектов выборки, веса рёбер соответствуют значениям функции расстояния между соответствующими вершинами.&lt;br /&gt;
&lt;br /&gt;
Также должно быть задано искомое число кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;. [[file:MSTClusteringFull.png|thumb|700px|Рис.1. Ключевая идея алгоритма кластеризации на основе построения остовного дерева, разбиение на 3 кластера]]&lt;br /&gt;
&lt;br /&gt;
Идея рассматриваемого алгоритма кластеризации очень проста:&lt;br /&gt;
* с помощью какого-либо алгоритма на определённом выше графе строится [[Построение минимального остовного дерева (MST)| минимальное остовное дерево]]&lt;br /&gt;
* из полученного дерева выбрасывается &amp;lt;math&amp;gt;K - 1&amp;lt;/math&amp;gt; ребро с наибольшими весами, что приводит к появлению в остовном дереве &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; компонент связности, каждая из которых объявляется кластером&lt;br /&gt;
&lt;br /&gt;
Иллюстрация работы алгоритма приведена на рис. 1. В данном случае рассматриваются 13 объектов на евклидовой плоскости, построено остовное дерево, после чего из него выброшены 2 наиболее длинных ребра и получены три кластера.&lt;br /&gt;
&lt;br /&gt;
Существуют различные модификации описанного алгоритма, связанные с другим алгоритмом конструирования исходного графа или условием выбрасывания рёбер из построенного остовного дерева. Например, число &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; может быть полезно определить не изначально, а подобрать, рассматривая отсортированную по убыванию последовательность весов рёбер в остовном дереве: если имеется резкое уменьшение весов, то можно удалить все рёбра до него. Формализацией такого подхода является алгоритм ZEMST&amp;lt;ref&amp;gt;[http://www4.ncsu.edu/~zjorgen/ictai06.pdf Minimum Spanning Tree Based Clustering Algorithms]&amp;lt;/ref&amp;gt;. Однако в дальнейшем ограничимся анализом определённого выше простейшего варианта.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан связный неориентированный граф на множестве объектов &amp;lt;math&amp;gt;G = (X, E)&amp;lt;/math&amp;gt; с неотрицательными весами рёбер &amp;lt;math&amp;gt;\rho(e) = \rho(x_i, x_j) \geqslant 0&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Наиболее частый используемый на практике сценарий следующий:&lt;br /&gt;
* каждый объект описывается &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерным вектором в пространстве&lt;br /&gt;
* используется функция расстояния &amp;lt;math&amp;gt;\ell_p&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, случай, когда в качестве метрики выбирается евклидова метрика &amp;lt;math&amp;gt;\ell_2&amp;lt;/math&amp;gt; в литературе называется EMST - Euclidean Minimum Spanning Tree.&lt;br /&gt;
&lt;br /&gt;
Хотя в некоторых задачах возможна ситуация, когда данный граф не является полным (по каким-то причинам определены не все расстояния между вершинами), но в описанных типовых случаях граф является полным, содержит &amp;lt;math&amp;gt;\frac{N(N - 1)}{2} = O(N^2)&amp;lt;/math&amp;gt; рёбер.&lt;br /&gt;
&lt;br /&gt;
==== Обзор алгоритмов поиска минимального остова ====&lt;br /&gt;
Для поиска минимального остовного дерева на практике используется следующие три похожих друг на друга алгоритма и их комбинации: [[Алгоритм Прима]]&amp;lt;ref&amp;gt;Prim, R C. “Shortest Connection Networks and Some Generalizations.” Bell System Technical Journal 36, no. 6 (November 1957): 1389–1401. doi:10.1002/j.1538-7305.1957.tb01515.x.&amp;lt;/ref&amp;gt;, [[Алгоритм Крускала]]&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; и [[Алгоритм Борувки]]&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&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;T&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; состоит из одной вершины, выбранной произвольно. Далее на каждом шаге отыскивается минимальное ребро, ровно один из концов которого принадлежит &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, оно добавляется в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; вместе со вторым своим концом. Алгоритм завершается через &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;gt; шаг - ровно столько рёбер в дереве на &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вершинах. Последовательная реализация, при которой минимальное ребро на каждом шаге ищется полным перебором рёбер, исходящих из &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, имеет сложность &amp;lt;math&amp;gt;O(N^3)&amp;lt;/math&amp;gt; на полном графе. Алгоритм можно ускорить, если поддерживать двоичное дерево поиска по рёбрам, ровно один из концов которых принадлежит &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Тогда при добавлении очередной вершины в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; необходимо добавить в двоичное дерево не более чем &amp;lt;math&amp;gt;N - 1&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(\log 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 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;В алгоритме Крускала&#039;&#039;&#039; остов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; изначально пуст, и в остов на каждом шаге добавляется вместе со своими концами минимальное ребро, имеющееся в графе, не нарушающее свойства ацикличности уже выбранного на предыдущих шагах леса &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Через &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;gt; шаг лес &amp;lt;math&amp;gt;T&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^3)&amp;lt;/math&amp;gt;. Если же предварительно отсортировать все рёбра по весу за &amp;lt;math&amp;gt;O(N^2 \log N^2) = O(N^2 \log N)&amp;lt;/math&amp;gt;, то дальше все рёбра искомого остова можно будет добавить в процессе одного прохода по этому массиву рёбер в порядке возрастания весов - для каждого ребра проверяем, не нарушает ли оно свойство ацикличности &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; и добавляем его в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, если не нарушает. Для того чтобы быстро проверять свойство принадлежности обоих концов ребра одной из компонент &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, используется эффективная структура данных - система непересекающихся множеств&amp;lt;ref&amp;gt;[http://e-maxx.ru/algo/dsu Система непересекающихся множеств]&amp;lt;/ref&amp;gt;. Сложность одной такой проверки &amp;lt;math&amp;gt;O(\alpha(N))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\alpha(N)&amp;lt;/math&amp;gt; - обратная функция Аккермана, значение которой не превосходит 4 для &amp;lt;math&amp;gt;N \leqslant 10^{500}&amp;lt;/math&amp;gt;. Итоговая сложность такого алгоритма равна сложности сортировки и также составляет &amp;lt;math&amp;gt;O(N^2 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;В алгориме Борувки&#039;&#039;&#039;, как и в алгоритме Крускала, поддерживается лес &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Изначально лес состоит из &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; изолированных вершин графа. На каждом шаге путём перебора всех рёбер графа для каждого дерева в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; ищется минимальное ребро, ровно один конец которого принадлежит этому дереву. После этого все найденные рёбра добавляются в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поскольку после каждой итерации число деревьев в лесе &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, исходно равное &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, уменьшается не менее чем в 2 раза, таких шагов будет произведено не более &amp;lt;math&amp;gt;\log N&amp;lt;/math&amp;gt;, поэтому итоговая сложность алгоритма составляет &amp;lt;math&amp;gt;O(N^2\log N)&amp;lt;/math&amp;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;T&amp;lt;/math&amp;gt;, трудно приспособить к распределённым архитектурам. А при работе с полным графом с большим числом вершин хранить его становится практически невозможно. В алгоритме Крускала можно распараллелить этап сортировки рёбер, но на последнем шаге алгоритма необходимо последовательно просмотреть отсортированный массив &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt; рёбер, поддерживая СНМ, что сводит на нет параллелизм. Таким образом, для этих двух алгоритмов необходима существенная переработка для получения эффективного параллельного решения &amp;lt;ref&amp;gt;[http://www.cslab.ntua.gr/~knikas/files/papers/mtaap12kruskal.pdf An approach to parallelize Kruskal&#039;s algorithm using Helper Threads]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На этом фоне выгодно выделяется исторически первый алгоритм Борувки. Он был разработан чешским учёным Отакаром Борувкой в 1926 году для решения задачи построения оптимальной электрической сети в Моравии. Он эффективнее всего использует тат факт, что остовные деревья сперва можно найти для отдельных частей графа, после чего объединить полученные деревья. Далее будем рассматривать именно этот метод для построения минимального остовного дерева в алгоритме кластеризации.&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;\rho(x_i, x_j)&amp;lt;/math&amp;gt; между объектами (вершинами полного графа)&lt;br /&gt;
* функции для работы с графом - инициализация добавление, удаление ребра&lt;br /&gt;
* обход в глубину для выделения компонент связности графа: обход леса производится за &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; с помощью функции &#039;&#039;обновить_компоненты&#039;&#039;, которая возвращает массив &#039;&#039;компонента&#039;&#039;, содержащий номер компоненты связности для каждой вершины&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;
&amp;lt;source lang=&amp;quot;text&amp;quot; line start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
Вход: Выборка объектов X размера N&lt;br /&gt;
Выход: рёбра минимального остовного дерева T&lt;br /&gt;
Алгоритм:&lt;br /&gt;
  T := пустое множество рёбер&lt;br /&gt;
  компонента := обновить_компоненты(T)&lt;br /&gt;
  есть_разные_компоненты := 1&lt;br /&gt;
  Пока (есть_разные_компоненты равно 1):&lt;br /&gt;
    есть_разные_компоненты = 0&lt;br /&gt;
    инициализировать массив D кратчайших рёбер, исходящих из компонент, бесконечно длинными рёбрами&lt;br /&gt;
    цикл по всем парам вершин (x, y) из X:&lt;br /&gt;
      если компонента(x) не равно компонента(y):&lt;br /&gt;
        есть разные компоненты := 1&lt;br /&gt;
        d := расстояние(x, y)&lt;br /&gt;
        если (ребро (x, y) &amp;lt; ребро D(компонента(x))):&lt;br /&gt;
            D(компонента(x)) := (x, y)&lt;br /&gt;
        если (ребро (x, y) &amp;lt; ребро D(компонента(y))):&lt;br /&gt;
            D(компонента(y)) := (x, y)&lt;br /&gt;
    цикл по рёбрам E из D:&lt;br /&gt;
       добавить E в множество T&lt;br /&gt;
    компонента := обновить_компоненты(T)&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;text&amp;quot; line start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
Вход: Выборка объектов X, число кластеров K&lt;br /&gt;
Выход: метки кластеров для каждого объекта из X&lt;br /&gt;
Алгоритм:&lt;br /&gt;
   T := рёбра минимального остовного дерева для набора объектов X&lt;br /&gt;
   E := отсортировать рёбра T по убыванию&lt;br /&gt;
   цикл по первым K рёбрам в E:&lt;br /&gt;
      удалить ребро из T&lt;br /&gt;
   компонента := обновить_компоненты(T)&lt;br /&gt;
   вернуть компонента&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
В разделе &amp;quot;Обзор алгоритмов поиска минимального остова&amp;quot; уже было показано, что алгоритм Борувки поиска остова имеет сложность &amp;lt;math&amp;gt;O(N^2 \log N)&amp;lt;/math&amp;gt;: поскольку на каждом шаге число компонент уменьшается не менее чем в 2 раза, цикл &#039;&#039;while&#039;&#039; выполнится не более &amp;lt;math&amp;gt;\log N&amp;lt;/math&amp;gt; раз. Внутри цикла находятся: &lt;br /&gt;
* инициализация массива &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; кратчайших рёбер, исходящих из компонент связности, бесконечностями - &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
* перебор по всем рёбрам полного графа и обновление содержимого ячеек массива &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
* добавление найденных рёбер из массива &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; в граф &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
* обновление компонент связности в графе &amp;lt;math&amp;gt;T&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(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;O(N)&amp;lt;/math&amp;gt; определяются финальные метки кластеров.&lt;br /&gt;
&lt;br /&gt;
Итоговая сложность &amp;lt;math&amp;gt;O(N^2 \log N)&amp;lt;/math&amp;gt; операций. В ходе работы основными являются операции сравнения и обращения в память. Обратим внимание, что &amp;quot;узким местом&amp;quot; алгоритма является перебор по всем рёбрам для нахождения минимальных рёбер, исходящих из каждой компоненты связности остовного леса &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приведём информационный граф, оставив за скобками детали реализации последних шагов алгоритма - сортировки и выбрасывания рёбер:&lt;br /&gt;
[[file:InformationGraph.png|thumb|center|700px|Рис.2. Информационная структура алгоритма кластеризации, основанного на поиске минимального остовного дерева]]&lt;br /&gt;
&lt;br /&gt;
Кратко опишем шаги схемы. Для вычисления массива &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; необходимо обработать некоторые из перебираемых рёбер (конкретные связи зависят от структуры текущего остовного дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), поэтому на этом этапе присутствует нерегулярность по данным; кроме того необходима инициализация массива &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;. Поскольку на шаге 2 для каждой компоненты необходимо выбирать минимум из набора рёбер, размер которого не ограничен константой, данная операция взятия минимума имеет, вообще говоря, параллельную сложность не менее чем &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;. Операции обновления структуры остовного графа независимы, могут быть распараллелены и склеены в шаг 3. Алгоритмы сортировки и поиска компонент связности теоретически имеют высоту ярусно-параллельной формы &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; итераций цикла, на каждой из которых высота ярусно параллельной формы имеет порядок &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;, итоговая высота ярусно-параллельной формы (и, соответственно, параллельная сложность алгоритма) - &amp;lt;math&amp;gt;O(\log^2 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;
Здесь необходимо отметить, что описанный алгоритм для поиска остовного дерева теоретически не является наиболее оптимальным: в статье &amp;lt;ref&amp;gt;[http://i.cs.hku.hk/~twlam/papers/mst-jacm.ps Concurrent threads and optimal parallel minimum spanning trees algorithm]&amp;lt;/ref&amp;gt; приводится алгоритм для графа &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt;, имеющий оптимальную параллельную сложность &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;. Однако, будучи оптимальным в теории, данный алгоритм не практичен - он исходит из числа процессоров порядка &amp;lt;math&amp;gt;O(V + E)&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;T&amp;lt;/math&amp;gt;. Информационный граф говорит о том, что этот этап можно эффективно распаралелить, так как выбор минимального исходящего ребра для каждой компоненты связности остовного дерева и даже для каждой вершины может исполняться независимо. Процедуры инициализации массива &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, добавления найденных рёбер в граф могут быть произведены независимо по элементам. Параллельно может быть проведена и сортировка, и выбрасывание рёбер в конце, хотя это не так принципиально в реальности. Но это позволяет оценить высоту ярусно-параллельной формы и теоретическую параллельную сложность алгоритма.&lt;br /&gt;
&lt;br /&gt;
Операции на ключевом этапе алгоритма достаточно сбалансированы - в полном графе для каждой из вершин необходимо просмотреть всех её соседей, поэтому в случае равномерного по вершинам графа распределения работ, эффективность параллельной реализации будет высокой. В целом, имеет место массовый параллелизм на итерациях вычисления ближайших вершин для текущего набора компонент связности &amp;lt;math&amp;gt;T&amp;lt;/math&amp;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;d&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;N&amp;lt;/math&amp;gt; целых чисел от 1 до &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;
Распараллеливание описанного алгоритма может принести значительный выигрыш - без него алгоритм фактически невозможно запустить на данных с числом объектов более 100 тысяч. Теоретические возможности распараллеливания на самом сложном этапе - вычислении минимального остовного дерева - были подробно обсуждены выше. Однако на практике может возникнуть ряд трудностей, связанных с эффективным обменом информацией между процессами. Так, всем параллельно работающим процессам необходимо быстро отвечать на вопрос о расположении вершин графа в одной компоненте связности - решения этой проблемы можно добиться, если синхронизировать информацию о найденных оптимальных рёбрах в конце каждой итерации.&lt;br /&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;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
С помощью технологии OpenMP был реализован параллельный алгоритм построения остовного дерева, основанный на алгоритме Борувки. Был распараллелен основной bottleneck алгоритма - поиск минимального ребра, исходящего из кластеров. В качестве данных для экспериментов использовались сгенерированные случайным образом точки на плоскости.&lt;br /&gt;
&lt;br /&gt;
Эксперимент производился на 16-процессорной машине под управлением операционной системы Linux, характеристики процессора: 2,6GHz Intel E3-12xx (Sandy Bridge), 8 instruction per cycle. Опираясь на эти характеристики, получаем, что пиковая теоретическая производительность одного процессора составляет 20,8 Gflops, а вычислительной системы в целом составляет 332,8 GFlops.&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность каждой задачи оценивалась сверху по формуле: &amp;lt;math&amp;gt;7N^2\log N&amp;lt;/math&amp;gt;, где константу 7 определяет число операций с плавающей точкой, необходимое для вычисления квадрата расстояния между двумя точками на плоскости (для выбора минимального ребра в алгоритме Борувки достаточно знать квадрат его длины).&lt;br /&gt;
&lt;br /&gt;
Перебирались следующие параметры запуска, от которых самым существенным образом зависит время работы программы:&lt;br /&gt;
* число процессоров: от 2 до 16 с шагом 2&lt;br /&gt;
* число кластеризуемых объектов (точек): от 50 тысяч до 150 тысяч с шагом 10 тысяч&lt;br /&gt;
&lt;br /&gt;
Для получения наиболее устойчивых результатов каждый эксперимент производился 5 раз, после чего время работы усреднялось по запускам.&lt;br /&gt;
&lt;br /&gt;
Ссылка на код реализованного алгоритма и лог эксперимента: https://drive.google.com/open?id=0B4KJzu2I13loaldSbF9XUnJXQ00&lt;br /&gt;
&lt;br /&gt;
Приведём графики времени работы, производительности и эффективности в зависимости от параметров, демонстрирующие свойства параллельной реализации:&lt;br /&gt;
[[file:Times_plot.png|thumb|center|500px|Рис.3. График времени работы программы в зависимости от числа процессов и размера задачи]]&lt;br /&gt;
Видим, что использование параллелизма позволяет существенно сократить временные затраты, однако в связи с необходимостью синхронизации процессов при переходе к очередной итерации алгоритма эффект параллелизма падает с увеличением числа процессоров. К примеру, на самой большой рассматриваемой задаче (150 тыс. точек) по сравнению с использованием 2 процессоров при использовании 4 процессоров время работы снижается примерно в 1.95 раз (с 469 до 240 секунд), а при использовании всех 16 процессоров - только в 6.8 раз вместо идеальных 8 (с 469 до 69 секунд).&lt;br /&gt;
&lt;br /&gt;
[[file:Perf_plot.png|thumb|center|500px|Рис.4. График производительности программы в зависимости от числа процессов и размера задачи]]&lt;br /&gt;
Производительность по числу процессоров напоминает линейную функцию, но при переходе от числа процессоров 14 к 16 видим существенное изменение наклона, которое говорит о том, что этот переход даёт несколько меньший выигрыш по времени по сравнению с предыдущими.&lt;br /&gt;
&lt;br /&gt;
Вывод о понижении эффективности параллелизма с увеличением числа процессоров подтверждает и следующий график:&lt;br /&gt;
[[file:Eff_plot.png|thumb|center|500px|Рис.5. График эффективности программы в зависимости от числа процессов и размера задачи]]&lt;br /&gt;
&lt;br /&gt;
Отметим, что в зависимости от количества точек (данных) графики производительности и эффективности ведут себя неустойчиво - это связано с нерегулярностью в алгоритме решения задачи, существенной зависимостью количества вычислений от структуры получающихся по ходу работы кластеров.&lt;br /&gt;
&lt;br /&gt;
Масштабируемость программы описывается следующими вычисленными согласно предложенной методике ключевыми показателями:&lt;br /&gt;
* минимальная эффективность - 7,32%, максимальная эффективность - 11,75%. Как видим, в архитектуре с общей памятью удалось достичь весьма высоких показателей эффективности&lt;br /&gt;
* оценка масштабируемости по числу процессов: -0.00041 - данная цифра говорит о том, что при повышении числа процессов эффективность медленно падает. Это вызвано ростом накладных расходов на синхронизацию результатов, независимо полученных полученных процессорами.&lt;br /&gt;
* оценка масштабируемости по размеру задачи: 0.00002 - данная оценка имеет весьма большую дисперсию из-за наблюдаемых перепадов эффективности при изменении размерности задачи, однако в среднем с ростом задачи эффективность очень медленно растёт. Это подтверждает естественное соображение о том, что для больших задач использование параллелизма прирносит наибольшую пользу.&lt;br /&gt;
* оценка масштабируемости по двум направлениям: -0.00002 - при увеличении одновременно вычислительной сложности и числа процессоров имеем очень медленное убывание эффективности.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Как правило, в библиотеках реализуют не собственно алгоритм кластеризации с использованием остовного дерева, а только алгоритм остовного дерева, предоставляя пользователю возможность самостоятельно реализовать функции построения графа и обработки результатов работы алгоритма. &lt;br /&gt;
&lt;br /&gt;
Параллельная реализация алгоритма MST:&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
Последовательные реализации доступны во многих библиотеках:&lt;br /&gt;
* [https://networkx.github.io/documentation/networkx-1.9/_modules/networkx/algorithms/mst.html NetworkX MST] - реализация в библиотеке для работы с графами на Python&lt;br /&gt;
* [http://www.boost.org/doc/libs/1_38_0/libs/graph/doc/prim_minimum_spanning_tree.html Алгоритм Прима], [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/kruskal_min_spanning_tree.html Алгоритм Крускала] - C++ реализация в boost&lt;br /&gt;
* [http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Алгоритм Прима], [http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Алгоритм Крускала] - Java реализация в JGraphT&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Zhum</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:Tereshinvs/MST_Clusterization&amp;diff=19011</id>
		<title>Участник:Tereshinvs/MST Clusterization</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:Tereshinvs/MST_Clusterization&amp;diff=19011"/>
		<updated>2016-11-23T10:55:51Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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; V = \left\{ v_1, v_2, \ldots, v_N \right\} &amp;lt;/math&amp;gt; в некотором метрическом пространстве с метрикой &amp;lt;math&amp;gt; \rho(x, y) &amp;lt;/math&amp;gt;&lt;br /&gt;
# Матрица отношений между объектами: симметричная матрица вещественных чисел размером &amp;lt;math&amp;gt; N \times N &amp;lt;/math&amp;gt; с неотрицательными элементами с нулевой диагональю.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается постановка с известным количеством кластеров &amp;lt;math&amp;gt; K &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается алгоритм алгоритм кластеризации на основе MST (Minimum spanning tree — минимальное остовное дерево).&lt;br /&gt;
&lt;br /&gt;
Минимальное остовное дерево (или минимальное покрывающее дерево) в связанном взвешенном неориентированном графе — это остовное дерево этого графа, имеющее минимальный возможный вес, где под весом дерева понимается сумма весов входящих в него рёбер.&lt;br /&gt;
&lt;br /&gt;
Общая схема алгоритма:&lt;br /&gt;
# Построение минимального остовного дерева одним из известных методов (алгоритмы Крускала, Прима, Борувки и их комбинации; наиболее пригодным для параллельного исполнения считается алгоритм Борувки)&lt;br /&gt;
# Удаление &amp;lt;math&amp;gt; K-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;
Пусть &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; — множество объектов, &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — множество номеров (имён, меток) кластеров. Задана функция расстояния между объектами &amp;lt;math&amp;gt;\rho(x,y)&amp;lt;/math&amp;gt;. Требуется разбить выборку на непересекающиеся подмножества, называемые &#039;&#039;кластерами&#039;&#039;, так, чтобы каждый кластер состоял из объектов, близких по метрике &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt;, а объекты разных кластеров существенно отличались. При этом каждому объекту &amp;lt;math&amp;gt;v_i\in V&amp;lt;/math&amp;gt;&lt;br /&gt;
приписывается номер кластера &amp;lt;math&amp;gt;y_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Алгоритм кластеризации&#039;&#039; — это функция &amp;lt;math&amp;gt;a\colon V\to Y&amp;lt;/math&amp;gt;, которая любому объекту &amp;lt;math&amp;gt;v\in V&amp;lt;/math&amp;gt; ставит в соответствие номер кластера &amp;lt;math&amp;gt;y\in Y&amp;lt;/math&amp;gt;. Множество &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; в некоторых случаях известно заранее, однако чаще ставится задача определить оптимальное число кластеров, с точки зрения того или иного &#039;&#039;критерия качества&#039;&#039; кластеризации.&lt;br /&gt;
&lt;br /&gt;
В данном случае задачу можно сформулировать так: выбрать пороговое значение веса ребра &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt;, так чтобы объекты, соединённые ребром весом меньше &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; лежали в одном кластере, а соединённые ребром весом больше &amp;lt;math&amp;gt;W&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;
# Добавление найденных наименьших вершин в MST&lt;br /&gt;
# &amp;quot;Склейка&amp;quot; компонент связности MST&lt;br /&gt;
# Нахождение веса &amp;lt;math&amp;gt;W_{K-1}&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(K-1)&amp;lt;/math&amp;gt;-ого наибольшего ребра&lt;br /&gt;
# Удаление рёбер весом не меньше чем &amp;lt;math&amp;gt;W_{K-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot; line start=&amp;quot;0&amp;quot; highlight=&amp;quot;1,9&amp;quot;&amp;gt;&lt;br /&gt;
  Ввод: граф V&lt;br /&gt;
&lt;br /&gt;
  Поместить каждую вершину в отдельное дерево леса T&lt;br /&gt;
  Пока в T больше одной компоненты связности:&lt;br /&gt;
    Для каждой компоненты C из T:&lt;br /&gt;
      S — пустое множество рёбер&lt;br /&gt;
      Для каждой вершины v в C:&lt;br /&gt;
        Найти кратчайшее ребро с одним концом v, а другим не из C, и поместить его в S&lt;br /&gt;
      Добавить кратчайшее ребро из S в T&lt;br /&gt;
    Объединить компоненты связности&lt;br /&gt;
  T — MST&lt;br /&gt;
  &lt;br /&gt;
  Найти W — вес (K-1)-ого максимального ребра в T&lt;br /&gt;
  Удалить из T рёбра с весом большим или равным W&lt;br /&gt;
&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;
&amp;lt;!--# Для приближённого построения триангуляции Делонэ существуют алгоритмы разделяй-и-властвуй, имеющие гарантированную сложность &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;gt;&lt;br /&gt;
# Нахождение ребер наименьшего веса для всех компонент связности требует &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; операций на каждом шаге, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; — число рёбер в графе.&lt;br /&gt;
# Добавление новых рёбер в MST требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; операций за всё время работы алгоритма.&lt;br /&gt;
# При использовании структуры данных система непересекающихся множеств с ранговой эвристикой, объединение деревьев требует примерно , где &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
# Доказано, что количество шагов алгоритма &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итого &amp;lt;math&amp;gt;O(V \log N)&amp;lt;/math&amp;gt; операций. При использовании триангуляции Делонэ в некоторых случаях &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; имеет тот же порядок, что и &amp;lt;math&amp;gt;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^2 \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удаление &amp;lt;math&amp;gt;K-1&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(N^2 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[File:Tereshinvs_mst_scheme.png|frame|center|alt=Информационный граф|Информационный граф алгоритма кластеризации на основе минимального остовного дерева]]&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины находятся в своих собственных деревьях (стадия вершин). Затем с помощью нахождения кратчайших рёбер, соединяющих разные поддеревья, происходит слияние поддеревьев (стадия объединения деревьев) до тех пор, пока не останется одно дерево, являющееся минимальным остовным деревом исходного графа. После этого нужно будет лишь удалить нужное количество рёбер (выделение кластеров).&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Основным местом для распараллеливания алгоритма является поиск наименьшего ребра из одной компоненты связности в другие. Так как компоненты связности в данном случае независимы, то отдельные узлы вычислительной системы могут выполнять поиск наименьших рёбер независимо.&lt;br /&gt;
&lt;br /&gt;
Узким местом является нахождение k-ой порядковой статистики для выделения кластеров, а так же объединение компонент связности. Но всего объединений компонент связности &amp;lt;math&amp;gt;N-1&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; N &amp;lt;/math&amp;gt; (количество элементов рассматриваемого множества) и матрица вещественных чисел размера &amp;lt;math&amp;gt; N \times N &amp;lt;/math&amp;gt; отношений между элементами этого множества. Матрица предполагается симметричной, а её диагональные элементы равными нулю, поэтому её размер можно оценить как &amp;lt;math&amp;gt; \tfrac{N(N-1)}{2} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Кроме того, имеется натуральное число &amp;lt;math&amp;gt; K \leqslant 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; 1 &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;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Интересной особенностью использования библиотеки Boost::graph::distributed является возможность задания изначального распределения вершин графа по процессам. К сложностям использования можно отнести нечитаемые сообщения об ошибках инстанцирования шаблонов С++ и отсутствие большого числа примеров использования библиотеки в интернете.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
В этом параграфе описывается масштабируемость параллельной реализации алгоритма из библиотеки Boost::graph::distributed.&lt;br /&gt;
&lt;br /&gt;
С использованием библиотеки Boost::graph::distributed программу можно реализовать следующим образом (указана только часть построения MST):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot; line start=&amp;quot;0&amp;quot; highlight=&amp;quot;1,9&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/use_mpi.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/mpi_process_group.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/adjacency_list.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/vertex_list_adaptor.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/metis.hpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;fstream&amp;gt;&lt;br /&gt;
#include &amp;lt;vector&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef boost::adjacency_list&amp;lt;&lt;br /&gt;
	boost::listS,&lt;br /&gt;
	boost::distributedS&amp;lt;&lt;br /&gt;
		boost::graph::distributed::mpi_process_group,&lt;br /&gt;
		boost::vecS&lt;br /&gt;
	&amp;gt;,&lt;br /&gt;
	boost::undirectedS,&lt;br /&gt;
	boost::no_property,&lt;br /&gt;
	boost::property&amp;lt;&lt;br /&gt;
		boost::edge_weight_t,&lt;br /&gt;
		float&lt;br /&gt;
	&amp;gt;&lt;br /&gt;
&amp;gt; Graph;&lt;br /&gt;
&lt;br /&gt;
typedef boost::graph_traits&amp;lt;Graph&amp;gt;::edge_descriptor Edge;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[])&lt;br /&gt;
{&lt;br /&gt;
	boost::mpi::environment env(argc, argv);&lt;br /&gt;
&lt;br /&gt;
	std::ifstream in(&amp;quot;big.graph&amp;quot;);&lt;br /&gt;
	boost::graph::metis_reader reader(in);&lt;br /&gt;
&lt;br /&gt;
	Graph graph(&lt;br /&gt;
		reader.begin(), reader.end(),&lt;br /&gt;
		reader.weight_begin(),&lt;br /&gt;
		reader.num_vertices()&lt;br /&gt;
	);&lt;br /&gt;
&lt;br /&gt;
	typedef boost::property_map&amp;lt;Graph, boost::edge_weight_t&amp;gt;::type WeightMap;&lt;br /&gt;
	WeightMap weight_map = boost::get(boost::edge_weight, graph);&lt;br /&gt;
&lt;br /&gt;
	std::vector&amp;lt;Edge&amp;gt; mst_edges;&lt;br /&gt;
&lt;br /&gt;
	boost::graph::distributed::dense_boruvka_minimum_spanning_tree(&lt;br /&gt;
            boost::graph::make_vertex_list_adaptor(graph),&lt;br /&gt;
	    weight_map,&lt;br /&gt;
	    std::back_inserter(mst_edges)&lt;br /&gt;
	);&lt;br /&gt;
&lt;br /&gt;
	if (process_id(graph.process_group()) == 0)&lt;br /&gt;
            std::cout &amp;lt;&amp;lt; &amp;quot;Finished&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
К сожалению, не удалось запустить программу, использующую данную библиотеку на суперкомпьютере IBM BlueGene/P, поэтому замеры производительности производились на компьютере с процессором Intel Core i5 4210U (два ядра и четыре потока, тактовая частота 1.7ГГц, до 2.7ГГц в режиме Turbo Boost), 6GB оперативной памяти DDR3, жёстким диском с частотой вращения шпинделя 5400 об/мин под управлением Windows 10 64-bit с использованием Windows Subsystem for Linux.&lt;br /&gt;
&lt;br /&gt;
Замеры производились только для построения минимального остовного дерева для полного графа (вершины — объекты, рёбра с весами — отношения), так как это является вычислительным ядром алгоритма.&lt;br /&gt;
&lt;br /&gt;
[[File:boost_mst_time.png|frame|center|alt=Время работы|Время работы алгоритма построения MST на полном графе из 5000 вершин, синем показана полученное время работы в секундах, зелёным — ожидаемое относительно увеличения количества процессором]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Библиотека шаблонов для С++ Boost предлагает прямую параллельную реализацию алгоритма Борувки в компоненте graph/distributed. Кроме этого, в ней имеются реализации других алгоритмов построения MST, а также много других параллельных алгоритмов на графах. Библиотека основана на технологии MPI. IBM предоставил патч для Boost 1.47.0 для её использования с компиляторами IBM CL C/C++.&lt;br /&gt;
&lt;br /&gt;
Кроме этой библиотеки не существует распространённой библиотеки, реализующей параллельный алгоритм построения минимального остовного дерева. Но имеется достаточно большое количество библиотек, реализующих однопоточное построение MST:&lt;br /&gt;
* JGraphT для Java&lt;br /&gt;
* SciPy MST&lt;br /&gt;
* множество любительских реализаций, который можно легко найти в интернете по запросу &amp;quot;mst algorithm&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации, http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf&lt;br /&gt;
# Minimum spanning tree, https://en.wikipedia.org/wiki/Minimum_spanning_tree&lt;br /&gt;
# Алгоритм Борувки, https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm&lt;br /&gt;
# Euclidean minimum spanning tree, https://en.wikipedia.org/wiki/Euclidean_minimum_spanning_tree&lt;br /&gt;
# Система непересекающихся множеств, http://e-maxx.ru/algo/dsu&lt;br /&gt;
# Boost Library, http://www.boost.org/&lt;br /&gt;
# Параллельное построение MST в библиотеке Boost, http://www.boost.org/doc/libs/1_55_0/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#id14&lt;br /&gt;
# Патч Boost 1.47.0 для IBM CL C/C++, http://www-01.ibm.com/support/docview.wss?uid=swg27027469&lt;/div&gt;</summary>
		<author><name>Zhum</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:Tereshinvs&amp;diff=19010</id>
		<title>Обсуждение участника:Tereshinvs</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:Tereshinvs&amp;diff=19010"/>
		<updated>2016-11-23T10:55:34Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет пояснений к графику&lt;/div&gt;</summary>
		<author><name>Zhum</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:Guryanovak/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19006</id>
		<title>Участник:Guryanovak/Алгоритм кластеризации, основанный на минимальном остовном дереве</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:Guryanovak/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=19006"/>
		<updated>2016-11-23T10:44:10Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Авторы описания: [[Участник:guryanovak|Гурьянов Алексей Константинович]], [[Участник:Валерия Кибитова|Кибитова Валерия Николаевна]]&lt;br /&gt;
&lt;br /&gt;
Оба автора внесли в данную статью равноценный вклад. Сбор информации, построение алгоритмов, а также составление данной статьи происходило совместно обоими авторами, четкого распределения ответственности произвести невозможно.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на минимальном остовном дереве&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N^2 \log_2 N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O(Nm) &amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &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;
&lt;br /&gt;
В отличии от классических алгоритмов кластеризации, данный алгоритм не предполагает, что кластеры являются сферической формы, и способен выделять кластеры произвольной формы. Данный алгоритм применяется в кластеризации цветов в различных изображениях, например, таких как: фотографии, карты, коммерческой рекламе, а также в различных биологических изображениях.&lt;br /&gt;
&lt;br /&gt;
Проблема нахождения минимального остовного дерева, а также ее решение было предложено Борувка в 1926 году. Идея использовать минимальное остовное дерево для кластеризации впервые была предложена Zahn в 1971&amp;lt;ref&amp;gt;Zahn C. T. Graph-theoretical methods for detecting and describing gestalt clusters //IEEE Transactions on computers. – 1971. – Т. 100. – №. 1. – С. 68-86.&amp;lt;/ref&amp;gt;, в последующие годы данная идея развивалась такими учеными как: Asano, Bhattacharya, Keil, и Yao&amp;lt;ref&amp;gt;Asano T. et al. Clustering algorithms based on minimum and maximum spanning trees //Proceedings of the fourth annual symposium on Computational geometry. – ACM, 1988. – С. 252-257.&amp;lt;/ref&amp;gt;, Avis&amp;lt;ref&amp;gt;Avis D. Diameter partitioning //Discrete &amp;amp; Computational Geometry. – 1986. – Т. 1. – №. 3. – С. 265-276.&amp;lt;/ref&amp;gt;, Eldershaw и Hegland&amp;lt;ref&amp;gt;Eldershaw C., Hegland M. Cluster analysis using triangulation //Computational Techniques and Applications. – 1997. – С. 201-208.&amp;lt;/ref&amp;gt;, Paivinen и другими&amp;lt;ref&amp;gt;Grygorash O., Zhou Y., Jorgensen Z. Minimum spanning tree based clustering algorithms //2006 18th IEEE International Conference on Tools with Artificial Intelligence (ICTAI&#039;06). – IEEE, 2006. – С. 73-81.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основная идея алгоритма состоит из следующих шагов:&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;
В минимальном остовном дереве, выбирается &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; ребро (где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; – это заранее определенное число кластеров), имеющие наибольшой вес. Выбранные ребра удаляются. Оставшиеся k компонент связности считаются кластерами.&lt;br /&gt;
&lt;br /&gt;
В данной статье алгоритм будет описываться с использованием алгоритма Борувки. Подробное описание которого можно найти пройдя по ссылке [[https://algowiki-project.org/ru/Алгоритм_Борувки Алгоритм Борувки]]&lt;br /&gt;
&lt;br /&gt;
Общая идея его состоит в следующем:&lt;br /&gt;
#Изначально каждая вершина графа — тривиальное дерево, а ребра не принадлежат никакому дереву.&lt;br /&gt;
#Для каждого дерева найдем минимальное по весу инцидентное ему ребро. Добавим все такие ребра.&lt;br /&gt;
#Повторяем шаг 2 пока в графе не останется только одно дерево.&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;m&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;(a_{i,1}, a_{i,2}, ..., a_{i,m})&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,m], i \in [1..N] &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;N&amp;lt;/math&amp;gt; чисел от &amp;lt;math&amp;gt;1&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;
&lt;br /&gt;
* &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин.&lt;br /&gt;
* &amp;lt;math&amp;gt;G = (V,E) &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;
* &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt; (Minimum Spanning Tree) - минимальное остовное дерево графа &amp;lt;math&amp;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;
* &amp;lt;math&amp;gt; | x , y | = \sqrt{\sum_i^m (x_i - y_i)^2} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Работу алгоритма можно разделить на три блока:&lt;br /&gt;
*1: Вычисление расстояний между вершинами и заполнение данных о компонентах связности&lt;br /&gt;
*2: Построение минимального основного дерева&lt;br /&gt;
*3: Разделение на K кластеров&lt;br /&gt;
&lt;br /&gt;
Из них основное время работы алгоритма занимают блоки 1 и 2.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
В алгоритме используются следующие структуры данных:&lt;br /&gt;
* Node – структура данных, описывающая вершину пространства. Включает в себя значения координат точки.&lt;br /&gt;
* Edge – структура данных, описывающая узел между двумя вершинами. Включает в себя индексы двух вершин и расстояние между ними&lt;br /&gt;
* Components – класс, описывающий состояние компонент связности в промежуточных состояниях графа по мере добавления в него ребер. Включает в себя информацию о том, какой компоненте связности принадлежит каждая точка и о расстоянии между каждой парой компонент связности, которая равна минимальному ребру между двумя вершинами в этих двух компонентах.&lt;br /&gt;
&lt;br /&gt;
Для описания алгоритма вводятся следующие макрооперации:&lt;br /&gt;
*1: getDistance – находит расстояние между двумя вершинами графа как расстояние между соответствующими им точками пространства. (Сложность – &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*2: initializeComponents – инициализирует состояние класса Components информацией о вершинах и ребрах. В каждую исходную компоненту связности входит одна вершина. (Сложность – &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*3: findMinimalOutgoingEdge – находит ребро c наименьшей длиной, исходящее из заданной компоненты связности. (Cложность – &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*4: findComp  – возвращает индекс компоненты связности в которой находится вершина (Cложность – &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*5: connectComponentsWithEdge – соединяет две компоненты связности, связанные поданным на вход ребром. Объединение компонент включает в себя замену ребер инцидентности новой компоненты на минимальные ребра из пары объединяемых компонент. (Сложность – &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Макрооперации 2-5 относятся к последовательной реализации [[Алгоритм Борувки|алгоритма Борувки]], который был выбран как основа вычислительного ядра алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Схема работы алгоритма была создана с учетом последующей параллельной реализации,&amp;lt;ref&amp;gt;Chung S., Condon A. Parallel implementation of Bouvka&#039;s minimum spanning tree algorithm //Parallel Processing Symposium, 1996., Proceedings of IPPS&#039;96, The 10th International. – IEEE, 1996. – С. 302-308.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
int N;        // Размер входных данных&lt;br /&gt;
int number_of_clusters;        // Число кластеров&lt;br /&gt;
int size = N*N;        // Размер матрицы инцидентности&lt;br /&gt;
Node nodes[N];        // Вершины(точки), заданные начальными условиями&lt;br /&gt;
Edge edges[size];        // Все расстояния между вершинами &lt;br /&gt;
vector&amp;lt;Edge&amp;gt; tree;        // Рёбра минимального остовного дерева&lt;br /&gt;
Components comps;         // Структура компонент связности&lt;br /&gt;
read_data(&amp;amp;nodes, file_name);    // Считывание начальных данных&lt;br /&gt;
&lt;br /&gt;
//----------------Вычисление расстояний между вершинами----------------//&lt;br /&gt;
for(int i = 0; i &amp;lt; N-1; i++) {&lt;br /&gt;
    for(int j = i+1; j &amp;lt; N; j++) {&lt;br /&gt;
        double distance = getDistance(node[i], node[j]);        // Вычисление расстояний между вершинами&lt;br /&gt;
	edges[i*N + j] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
        edges[j*N + i] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//----------------Заполнение данных о компонентах связности--------------//&lt;br /&gt;
initializeComponents(&amp;amp;comps, nodes, edges); &lt;br /&gt;
&lt;br /&gt;
//----------------Построение минимального основного дерева--------------//&lt;br /&gt;
while(comps.size &amp;gt; 1) {&lt;br /&gt;
    vector&amp;lt;edges&amp;gt; mstedges;&lt;br /&gt;
    for component in comps {&lt;br /&gt;
        edge e = findMinimalOutgoingEdge(component, comps, edges) // Нахождение минимального ребра инцидентного данной компоненте&lt;br /&gt;
        mstedges.add(e)&lt;br /&gt;
    }&lt;br /&gt;
    for mstedge in mstedges {&lt;br /&gt;
        if (findComp(comps, mstedge.first) != findComp(comps, mstedge.second)) { &lt;br /&gt;
             connectComponentsWithEdge(comps, mstedge) // Объединение двух компонент&lt;br /&gt;
             tree.add(mstedge) // Добавление ребра в минимальное основное дерево&lt;br /&gt;
        }&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//-----------------------Разделение на K кластеров--------------------------//&lt;br /&gt;
sort(tree)&lt;br /&gt;
for(int i = tree.size() - 1; i &amp;gt; tree.size() - 1 - number_of_clusters; i--) {&lt;br /&gt;
    remove_edge(tree, i);        // Удаление самых длинных рёбер из MST - формирование кластеров&lt;br /&gt;
}&lt;br /&gt;
write_data(tree);        // Запись выходных данных&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Среди выделенных в описании частей алгоритма, самую большую сложность для оценивания временной сложности вызывает часть 3, являющаяся реализацией алгоритма Борувки. Однако если заметить, что после выполнения каждого тела внешнего цикла количество компонент связности будет уменьшаться как минимум вдвое (так как одно ребро в списке ребер минимального основного дерева может повториться максимум двое), то можно сделать вывод, что сложность в худшем случае этого блока алгоритма является &amp;lt;math&amp;gt; O (N^2 \log_2 N) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Асимптотическую сложность алгоритма можно выразить как сумму временных сложностей четырех выделенных в описании частей:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; TC(N, m) = O(N^2m) + O(N^2) + O(N^2 \log_2 N) + O(N \log_2 N) = O(N^2 (m + \log_2 N)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если посчитать размерность пространства входных точек за константу при расчете сложности, то временная сложность выразится как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; TC(N, m) = O(N^2 \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;
[[Файл:InformationGraph-2-8.png|frame|center|alt=Алгоритм| Информационный граф параллельного алгоритма кластеризации на основе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
В то время как узлы каждого слоя не зависят информационно от каждого узла предыдущего слоя, из-за того что структура информационного графа определяется динамически по ходу действия программы (на основе выбора ребер, по которым будет проводится слияние компонент связности), составить информационный граф, использующий более разреженную структуру связей между слоями, не представляется возможным.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Данная задача хорошо параллелизуется благодаря тому, что наиболее требующие вычислительных ресурсов блоки алгоритмического ядра алгоритма можно эффективно разделить по процессам. Процесс считывания входных данных и процесс расчета расстояния между точками, которые представляет собой большое количество попарных взятий евклидова расстояния, можно выполнить за &amp;lt;math&amp;gt;O(1)&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(1)&amp;lt;/math&amp;gt; времени при наличии как минимум &amp;lt;math&amp;gt;N^3&amp;lt;/math&amp;gt; процессоров. Процесс обновления информации о компонентах связности, который заключается в обновлении индексов кластеров вершин и в обновлении расстояний между кластерами, можно представить в виде независимой последовательности операций взятия минимума и операций присваивания в количестве  &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; штук (количество компонент связности), что позволит выполнить ярус, ответственный за обновление структуры компонент связности за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; при наличие как минимум &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; процессоров.  Это значит, что каждый ярус будет выполняться за &amp;lt;math&amp;gt; O(1) &amp;lt;/math&amp;gt; времени, и аналогично последовательному алгоритму Борувки, после выполнения 2 ярусов количество компонент связности уменьшится минимум вдвое, что гарантирует то, что за время выполнения алгоритма повторится не более чем &amp;lt;math&amp;gt; 4 * \log_2 N &amp;lt;/math&amp;gt; ярусов. Учитывая ярусы, посвященные считыванию ввода, вычислению расстояний между точками пространства, удалению &amp;lt;math&amp;gt;K-1&amp;lt;/math&amp;gt; ребер из минимального опорного дерева и выводу ответа, итоговую параллельная сложность алгоритма можно оценить как &amp;lt;math&amp;gt; 2 * \log_2 N + 5&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; векторов размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;(a_{i,1}, a_{i,2}, ..., a_{i,m})&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,m], i \in [1..N] &amp;lt;/math&amp;gt; и количество кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;, на которые необходимо разбить множество точек. Общий объем входных данных можно оценить как &amp;lt;math&amp;gt;O(Nm)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если оценивать размерность пространства входных точек константой, то объем входных данных можно оценить как &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;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;1&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; \frac{N^2 \log_2 N} {\log_2 N} = N^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вычислительную мощность алгоритма можно оценить как &amp;lt;math&amp;gt; \frac {N^2 \log_2 N} {N}   = N \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;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Авторами данной статьи было проведено исследование масштабируемости параллельной реализации алгоритма кластеризации, основанном на минимальном остовном дереве. Исследование алгоритма проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster суперкомпьютерного комплекса Московского университета]. Параллельная реализация была написана с помощью OpenMP. Распараллеливание проводилось по числу объектов кластеризации, поэтому исследование масштабируемости проводилось для числа объектов кластеризации.  Алгоритм исследовался на наборе данных, который был сгенерирован при помощи функции [http://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_blobs.html make_blobs (sklearn)] (количество признаков – 2, количество кластеров – 2).&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 16] с шагом 1;&lt;br /&gt;
*	число входных объектов [100 : 1000] с шагом 100.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена производительность и эффективность распараллеливания алгоритма кластеризации, основанного на минимальном остовном дереве, в зависимости от изменяемых параметров запуска.&lt;br /&gt;
[[File:Execution_GK_MST.png|800px|thumb|center|Рис.1. Изменение производительности в зависимости от числа процессов и числа входных объектов]]&lt;br /&gt;
[[File:Efficiency_MST_GK.png|800px|thumb|center|Рис.2. Изменение эффективности распараллеливания в зависимости от числа процессов и числа входных объектов]]&lt;br /&gt;
&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;&#039;&#039; - При увеличении числа процессов происходит снижение эффективности на всей области рассматриваемых значений параметров, при этом чем больше число процессов, тем снижение эффективности происходит медленнее.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;При одновременном увеличении количества процессов и размерности системы&#039;&#039;&#039; происходит снижение эффективности, при этом скорость убывания эффективности довольно невысокая. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://bitbucket.org/GuryanovKibitova/mstclusterization/src/ccaffad7780e3aee9645845539e6e1a311295208?at=master Параллельная реализация на языке C++ (+OpenMP)]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Существующих реализаций алгоритма кластеризации в настоящий момент немного. Ниже приводится список некоторых из них:&lt;br /&gt;
*[https://github.com/jakevdp/mst_clustering MST Clustering Python]&lt;br /&gt;
Однако существует множество реализаций алгоритма поиска минимального остовного дерева, на основе которых можно реализовать алгоритм кластеризации.&lt;br /&gt;
*[https://github.com/umarali/boruvka-mst-parallel  Parallel Implementation of Borůvka&#039;s MST Algorithm ]&lt;br /&gt;
*[http://www.mlpack.org/docs/mlpack-2.0.2/doxygen.php?doc=namespacemlpack_1_1emst.html  С++ mlpack MST Algorithm ]&lt;br /&gt;
*[https://github.com/nastra/algorithms-java Java Graph Algorithms]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Kruskal Java MST Algorithm ]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Prim Java MST Algorithm ]&lt;br /&gt;
*[https://github.com/dineshappavoo/GraphMinimumSpanningTree Java Graph Minimum Spanning Tree ]&lt;br /&gt;
*[https://pypi.python.org/pypi/PyAlgDat Python PyAlgDat ]&lt;br /&gt;
*[https://networkx.github.io/documentation/networkx-1.10/reference/algorithms.mst.html Python NetworkX Minimum Spanning Tree]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_53_0/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html C++ Boost Minimum Spanning Tree]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Guryanovak&amp;diff=19005</id>
		<title>Обсуждение участника:Guryanovak</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:Guryanovak&amp;diff=19005"/>
		<updated>2016-11-23T10:43:55Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;quot;При увеличении числа процессов происходит снижение эффективности на всей области рассматриваемых значений параметров, при этом чем больше число процессов, тем снижение эффективности происходит медленнее&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Ничего не понятно - увеличиваем число процессоров и эффективность ПАДАЕТ. Но медленнее... Медленнее чего? В каких случаях она ещё падает по сравнению с этим?&lt;br /&gt;
&lt;br /&gt;
Переформулируйте мысль.&lt;/div&gt;</summary>
		<author><name>Zhum</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%91%D1%80%D0%BE%D1%82%D0%B8%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%94%D0%B0%D0%BD%D1%83%D1%82%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-means&amp;diff=19002</id>
		<title>Участник:Бротиковская Данута/Алгоритм k-means</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%91%D1%80%D0%BE%D1%82%D0%B8%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%94%D0%B0%D0%BD%D1%83%D1%82%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-means&amp;diff=19002"/>
		<updated>2016-11-23T10:19:09Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних (k means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(\Tau*k*d*n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; d*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;
Авторы страницы &amp;lt;b&amp;gt;[[Участник:Бротиковская Данута|Данута Бротиковская]]&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;[[Участник:DennZo1993|Денис Зобнин]]&amp;lt;/b&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;b&amp;gt;&amp;lt;i&amp;gt;k средних&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; (&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt;) &amp;amp;ndash; неиерархический&amp;lt;ref&amp;gt;&amp;quot;https://ru.wikipedia.org/wiki/Иерархическая_кластеризация&amp;quot;&amp;lt;/ref&amp;gt;, итерационный метод кластеризации&amp;lt;ref&amp;gt;&amp;quot;https://ru.wikipedia.org/wiki/Кластерный_анализ&amp;quot;&amp;lt;/ref&amp;gt;, получивший большую популярность благодаря своей простоте, наглядности реализации и достаточно высокому качеству работы. Был изобретен в 1950-х годах математиком &amp;lt;i&amp;gt;Гуго Штейнгаузом&amp;lt;/i&amp;gt;&amp;lt;ref&amp;gt;Steinhaus, Hugo. &amp;quot;Sur la division des corp materiels en parties.&amp;quot; Bull. Acad. Polon. Sci 1.804 (1956): 801.&amp;lt;/ref&amp;gt; и почти одновременно &amp;lt;i&amp;gt;Стюартом Ллойдом&amp;lt;/i&amp;gt;&amp;lt;ref&amp;gt;Lloyd, S. P. &amp;quot;Least square quantization in PCM. Bell Telephone Laboratories Paper. Published in journal much later: Lloyd, SP: Least squares quantization in PCM.&amp;quot; IEEE Trans. Inform. Theor.(1957/1982).&amp;lt;/ref&amp;gt;. Особую популярность приобрел после публикации работы &amp;lt;i&amp;gt;МакКуина&amp;lt;/i&amp;gt;&amp;lt;ref&amp;gt;MacQueen, James. &amp;quot;Some methods for classification and analysis of multivariate observations.&amp;quot; Proceedings of the fifth Berkeley symposium on mathematical statistics and probability. Vol. 1. No. 14. 1967.&amp;lt;/ref&amp;gt; в 1967.&lt;br /&gt;
&lt;br /&gt;
Алгоритм представляет собой версию EM-алгоритма&amp;lt;ref&amp;gt;&amp;quot;https://ru.wikipedia.org/wiki/EM-алгоритм&amp;quot;&amp;lt;/ref&amp;gt;, применяемого также для разделения смеси гауссиан. Основная идея &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;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; кластеров таким образом, чтобы каждое наблюдение принадлежало ровно одному кластеру, расположенному на наименьшем расстоянии от наблюдения.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Дано:&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
* набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; наблюдений &amp;lt;math&amp;gt;X=\{\mathbf{x}_1, \mathbf{x}_2, ..., \mathbf{x}_n\}, \mathbf{x}_i \in \mathbb{R}^d, i=\overline{1, 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 \mathbb{N}, k \leq n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Требуется:&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&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;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;:&lt;br /&gt;
*   &amp;lt;math&amp;gt;S_i \cap S_j= \varnothing, \quad i \ne j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*   &amp;lt;math&amp;gt;\bigcup_{i=1}^{k} S_i = X&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Действие алгоритма:&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм &amp;lt;i&amp;gt;k means&amp;lt;/i&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;S_1, S_2, ..., S_k,&amp;lt;/math&amp;gt; таким образом, чтобы минимизировать сумму квадратов расстояний от каждой точки кластера до его центра (центр масс кластера). Введем обозначение, &amp;lt;math&amp;gt;S=\{S_1, S_2, ..., S_k\}&amp;lt;/math&amp;gt;. Тогда действие алгоритма &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; равносильно поиску:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;table style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;center&amp;quot; width=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\arg\min_{S} \sum\limits_{i=1}^k \sum\limits_{\mathbf{x} \in S_i} \rho(\mathbf{x}, \mathbf{\mu}_i )^2,&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;&amp;lt;math&amp;gt;(1)&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\mathbf{\mu}_i&amp;lt;/math&amp;gt; &amp;amp;ndash; центры кластеров, &amp;lt;math&amp;gt;i=\overline{1,k}, \quad \rho(\mathbf{x}, \mathbf{\mu}_i)&amp;lt;/math&amp;gt; &amp;amp;ndash; функция расстояния между &amp;lt;math&amp;gt;\mathbf{x}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Шаги алгоритма: &amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Начальный шаг: инициализация кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Выбирается произвольное множество точек &amp;lt;math&amp;gt;\mu_i, i=\overline{1,k}&amp;lt;/math&amp;gt;, рассматриваемых как начальные центры кластеров: &amp;lt;math&amp;gt;\mu_i^{(0)} = \mu_i, \quad i=\overline{1, k}&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Шаг&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;t: \forall \mathbf{x}_i \in X, i=\overline{1,n}: \mathbf{x}_i \in S_j \iff  j=\arg\min_{k}\rho(\mathbf{x}_i,\mathbf{\mu}_k^{(t-1)})^2&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Пересчет центров кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Шаг &amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;t: \forall i=\overline{1,k}:  \mu_i^{(t)} = \cfrac{1}{|S_i|}\sum_{\mathbf{x}\in S_i}\mathbf{x}&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Проверка условия останова:&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &#039;&#039;&#039;if&#039;&#039;&#039; &amp;lt;math&amp;gt;\exist i\in \overline{1,k}: \mu_i^{(t)} \ne \mu_i^{(t-1)}&amp;lt;/math&amp;gt; &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
        t = t + 1;&lt;br /&gt;
        goto 2;&lt;br /&gt;
    &#039;&#039;&#039;else&#039;&#039;&#039;&lt;br /&gt;
        &#039;&#039;&#039;stop&#039;&#039;&#039;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительным ядром являются шаги 2 и 3 приведенного выше алгоритма: &amp;lt;b&amp;gt;&amp;lt;i&amp;gt;распределение векторов по кластерам&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;&amp;lt;i&amp;gt;пересчет центров кластеров&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;Распределение векторов&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; по кластерам предполагает вычисление расстояний между каждым вектором &amp;lt;math&amp;gt;\mathbf{x}_i \in X, i= \overline{1,n}&amp;lt;/math&amp;gt; и центрами кластера &amp;lt;math&amp;gt;\mathbf{\mu}_j, j= \overline{1,k}&amp;lt;/math&amp;gt;. Таким образом, данный шаг предполагает &amp;lt;math&amp;gt;k*n&amp;lt;/math&amp;gt; вычислений расстояний между d-мерными векторами. &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;Пересчет центров кластеров&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; предполагает &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; вычислений центров масс &amp;lt;math&amp;gt;\mathbf{\mu}_i&amp;lt;/math&amp;gt; множеств &amp;lt;math&amp;gt;S_i, i=\overline{1,k}&amp;lt;/math&amp;gt; представленных выражением в шаге 3 представленного выше алгоритма.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Инициализация центров масс &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Наиболее распространенными являются следующие стратегии:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Метод Forgy&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;В качестве начальных значений &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt; берутся случайно выбранные векторы.&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Метод случайно разделения (Random Partitioning)&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;Для каждого вектора &amp;lt;math&amp;gt;\mathbf{x}_i \in X, i=\overline{1, n}&amp;lt;/math&amp;gt; выбирается случайным образом кластер &amp;lt;math&amp;gt;S_1, ..., S_k&amp;lt;/math&amp;gt;, после чего для каждого полученного кластера вычисляются значения &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&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;\mathbf{x}_i \in X, i=\overline{1, n}&amp;lt;/math&amp;gt; и центрами кластеров &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt; вычисляются &amp;lt;b&amp;gt;расстояния&amp;lt;/b&amp;gt; по формуле (как правило, используется Евлидово расстояние):&lt;br /&gt;
&amp;lt;table style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;center&amp;quot; width=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;math&amp;gt; \mathbf{v}_1, \mathbf{v}_2 \in \mathbb{R}^d,  \quad \rho(\mathbf{v}_1, \mathbf{v}_2) = \lVert \mathbf{v}_1- \mathbf{v}_2 \rVert= \sqrt{\sum_{i=1}^{d}(\mathbf{v}_{1,i} - \mathbf{v}_{2,i})^2}&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;&amp;lt;math&amp;gt;(2)&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&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;b&amp;gt;формуле вычисления центра масс&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;table style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;center&amp;quot; width=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;math&amp;gt; \mu = \cfrac{1}{|S|}\sum_{\mathbf{x}\in S}\mathbf{x}&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;&amp;lt;math&amp;gt;(3)&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;algorithm&#039;&#039;&#039; k-means &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
     &#039;&#039;&#039;1.&#039;&#039;&#039; Инициализировать центры кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_i^{(1)}, i=\overline{1,k}&amp;lt;/math&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;2.&#039;&#039;&#039; &amp;lt;math&amp;gt;t \leftarrow 1&amp;lt;/math&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;3.&#039;&#039;&#039; &#039;&#039;&#039;Распределение по кластерам&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
        &amp;lt;math&amp;gt;S_i^{(t)}=\{\mathbf{x}_p: \lVert\mathbf{x}_p-\mathbf{\mu}_i^{(t)}\rVert^2 \leq \lVert\mathbf{x}_p-\mathbf{\mu}_j^{(t)}\rVert^2 \quad \forall j=\overline{1,k}\},&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        где каждый вектор &amp;lt;math&amp;gt;\mathbf{x}_p&amp;lt;/math&amp;gt; соотносится единственному кластеру &amp;lt;math&amp;gt;S^{(t)}&amp;lt;/math&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;4.&#039;&#039;&#039; &#039;&#039;&#039;Обновление центров кластеров&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
        &amp;lt;math&amp;gt;\mathbf{\mu}_i^{(t+1)} = \frac{1}{|S^{(t)}_i|} \sum_{\mathbf{x}_j \in S^{(t)}_i} \mathbf{x}_j &amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;5.&#039;&#039;&#039; &#039;&#039;&#039;if&#039;&#039;&#039; &amp;lt;math&amp;gt;\exist i \in \overline{1,k}: \mathbf{\mu}_i^{(t+1)} \ne \mathbf{\mu}_i^{(t)}&amp;lt;/math&amp;gt; &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
            t = t + 1;&lt;br /&gt;
            goto &#039;&#039;&#039;3&#039;&#039;&#039;;&lt;br /&gt;
        &#039;&#039;&#039;else&#039;&#039;&#039;&lt;br /&gt;
            &#039;&#039;&#039;stop&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма===&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Обозначим &amp;lt;math&amp;gt;\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; временную сложность вычисления центорида кластера, число элементов которого равна &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;,  в d-мерном пространстве.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Аналогично  &amp;lt;math&amp;gt;\Theta_{distance}^d&amp;lt;/math&amp;gt; &amp;amp;ndash; временная сложность вычисления расстояния между двумя d-мерными векторами.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Сложность шага инициализации &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров мощности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; в d-мерном пространстве &amp;amp;ndash; &amp;lt;math&amp;gt;\Theta_{init}^{k, d, m}&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Стратерия Forgy&amp;lt;/i&amp;gt;: вычисления не требуются, &amp;lt;math&amp;gt;\Theta_{init}^{k, d, m} = 0&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Стратегия случайного разбиения&amp;lt;/i&amp;gt;: вычисление центров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров,  &amp;lt;math&amp;gt;\Theta_{init}^{k, d, m} = k*\Theta_{centroid}^{d, m}, m \le n&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Cложность шага распределения d мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам &amp;amp;ndash; &amp;lt;math&amp;gt;\Theta_{distribute}^{k, d}&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
На этом шаге для каждого вектора &amp;lt;math&amp;gt;\mathbf{x}_i \in X, i=\overline{1, n}&amp;lt;/math&amp;gt; вычисляется k расстояний до центров кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ...\mathbf{\mu}_k&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;math&amp;gt;\Theta_{distribute}^{k, d} = n*k*\Theta_{distance}^d&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Сложность шага пересчета центров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров размера &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; в d-мерном пространстве &amp;amp;ndash; &amp;lt;math&amp;gt;\Theta_{recenter}^{k, d, m}&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;На этом шаге вычисляется &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; центров кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ...\mathbf{\mu}_k&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Theta_{recenter}^{k, d, m} = k*\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; &amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Рассчитаем &amp;lt;math&amp;gt;\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; для кластера, число элементов которого равно &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;m * d&amp;lt;/math&amp;gt; сложений + &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; делений &amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Рассчитаем &amp;lt;math&amp;gt;\Theta_{distance}^d&amp;lt;/math&amp;gt; в соответствие с формулой &amp;lt;math&amp;gt;(2)&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Theta_{distance}^d&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; вычитаний + &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; умножений + &amp;lt;math&amp;gt;(d-1)&amp;lt;/math&amp;gt; сложение&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;Предположим, что алгоритм сошелся за &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; итераций, тогда временная сложность алгоритма &amp;lt;math&amp;gt;\Theta_{k-means}^{d, n}&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt;\Theta_{k-means}^{d, n} \le \Theta_{init}^{k, d, n} + \Tau*(\Theta_{distribute}^{k, d} + \Theta_{recenter}^{k, d, n})&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 5px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Операции сложения/вычитания:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt;\Theta_{k-means}^{d, n} \le k*n*d+ \Tau*(k*n*(2d-1) + k*n*d) = k*n*d+ \Tau*(k*n*(3d-1)) \thicksim O(\Tau*k*d*n)&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Операции умножения/деления:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt;\Theta_{k-means}^{d, n} \le k*d + \Tau*(k*n*d + k*d) = k*d + \Tau * k*d*(n+1) \thicksim O(\Tau*k*d*n) &amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Получаем, что &amp;lt;b&amp;gt;временная сложность&amp;lt;/b&amp;gt; алгоритма &amp;lt;b&amp;gt;k means&amp;lt;/b&amp;gt; кластеризации &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; &amp;lt;b&amp;gt;d-мерных&amp;lt;/b&amp;gt; векторов на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров за &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; итераций:&amp;lt;/p&amp;gt;&lt;br /&gt;
 &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt; \Theta_{k-means}^{d, n} \thicksim O(\Tau*k*d*n) &amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф алгоритма. Алгоритм &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; начинается с этапа инициализации, после которого следуют итерации, на каждой из которых выполняется два последовательных шага (см. [[#Схема реализации последовательного алгоритма|&amp;quot;Схема реализации последовательного алгоритма&amp;quot;]]): &lt;br /&gt;
* распределение векторов по кластерам&lt;br /&gt;
* перерасчет центров кластеров&lt;br /&gt;
&lt;br /&gt;
Поскольку основная часть вычислений приходится на шаги итераций, распишем информационные графы данных шагов.&lt;br /&gt;
&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Информационный граф шага распределения векторов по кластерам представлен на &#039;&#039;рисунке 1&#039;&#039;. Исходами данного графа является исходные векторы &amp;lt;math&amp;gt;\mathbf{x}_1, ..., \mathbf{x}_n&amp;lt;/math&amp;gt;, а также центры кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ... \mathbf{\mu}_k&amp;lt;/math&amp;gt;, вычисленные ранее (на шаге инициализации, если рассматривается первая итерация алгоритма, или на шаге пересчета центров кластеров предыдущей итерации в противном случае). Каждая пара векторов данных &amp;lt;math&amp;gt;\mathbf{x}_i, i=\overline{1,n}&amp;lt;/math&amp;gt; и центров кластера &amp;lt;math&amp;gt;\mathbf{\mu}_j, j=\overline{1, k}&amp;lt;/math&amp;gt; : (&amp;lt;math&amp;gt;\mathbf{x}_i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\mathbf{\mu}_j&amp;lt;/math&amp;gt;) подаются на независимые узлы &amp;lt;i&amp;gt;&amp;quot;d&amp;quot;&amp;lt;/i&amp;gt; вычисления расстояния между векторами (более подробная схема вычисления расстояния представлена далее, &#039;&#039;рисунок 2&#039;&#039;). Далее узлы вычисления расстояния &amp;lt;i&amp;gt;&amp;quot;d&amp;quot;&amp;lt;/i&amp;gt;, соответствующие одному и тому же исходному вектору &amp;lt;math&amp;gt;\mathbf{x}_i&amp;lt;/math&amp;gt; передаются на один узел &amp;lt;i&amp;gt;&amp;quot;m&amp;quot;&amp;lt;/i&amp;gt;, где далее происходит вычисление новой метки кластера для каждого вектора &amp;lt;math&amp;gt;\mathbf{x}_i&amp;lt;/math&amp;gt; (берется кластер с минимальным результатом вычисления расстояния). На выходе графа выдаются метки кластеров , &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt;, такие что &amp;lt;math&amp;gt;\forall \mathbf{x}_i, i=\overline{1, n}, \mathbf{x}_i \in S_j \Leftrightarrow L_i = j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[file:Clusterization.png|thumb|center|800px|Рис. 1. Схема распределения векторов по кластерам. &#039;&#039;d&#039;&#039; &amp;amp;ndash; вычисление расстояния между векторами; &#039;&#039;m&#039;&#039; &amp;amp;ndash; вычисление минимума.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Вычисление расстояния между векторами&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Подробная схема вычисления расстояния между векторами &amp;lt;math&amp;gt;\mathbf{x}_i, \mathbf{\mu}_j&amp;lt;/math&amp;gt; представлена на &#039;&#039;рисунке 2&#039;&#039;. Как показано на графе, узел вычисления расстояния между векторами &amp;lt;i&amp;gt;&amp;quot;d&amp;quot;&amp;lt;/i&amp;gt; состоит из шага взятия разности между векторами (узел &amp;quot;&amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&amp;quot;) и взятия нормы получившегося вектора разности (узел &amp;quot;&amp;lt;math&amp;gt;||\cdot||^2&amp;lt;/math&amp;gt;&amp;quot;). Более подробно, вычисление расстояния между векторами &amp;lt;math&amp;gt;\mathbf{x}_i = {x_{i1,}, ...,{x_{in}}}, \mathbf{\mu}_j = {\mu_{j1}, ...,\mu_{jn}}&amp;lt;/math&amp;gt; может быть представлено как вычисление разности между каждой парой компонент &amp;lt;math&amp;gt;(x_{iz}, \mu_{jz}), z=\overline{1, d}&amp;lt;/math&amp;gt; (узел &amp;quot;&amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&amp;quot;), далее возведение в квадрат для каждого узла &amp;quot;&amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&amp;quot; (узел &amp;quot;&amp;lt;math&amp;gt;()^2&amp;lt;/math&amp;gt;&amp;quot;) и суммирования выходов всех узлов &amp;quot;&amp;lt;math&amp;gt;()^2&amp;lt;/math&amp;gt;&amp;quot; (узел &amp;quot;&amp;lt;math&amp;gt;+&amp;lt;/math&amp;gt;&amp;quot;).  &lt;br /&gt;
&lt;br /&gt;
[[file:dist_calc.png|thumb|center|800px|Рис. 2. Схема вычисления расстояния между вектором и центром кластера.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Пересчет центров кластеров&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Информационный граф шага пересчета центров кластеров представлен на &#039;&#039;рисунке 3&#039;&#039;. Исходами данного графа является исходные векторы &amp;lt;math&amp;gt;\mathbf{x}_1, ..., \mathbf{x}_n&amp;lt;/math&amp;gt;, а также им соответствующие метки кластера, &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt;, такие что &amp;lt;math&amp;gt;\forall x_i, i=\overline{1, n}, \mathbf{x}_i \in S_j \Leftrightarrow L_i = j&amp;lt;/math&amp;gt;, вычисленные на этапе распределения векторов по кластерам. Все векторы &amp;lt;math&amp;gt;\mathbf{x}_1, ..., \mathbf{x}_n&amp;lt;/math&amp;gt; подаются в узлы &amp;lt;math&amp;gt;+_1, ...+_k&amp;lt;/math&amp;gt;, каждый узел &amp;lt;math&amp;gt;+_m, m = \overline{1,k}&amp;lt;/math&amp;gt; соответствует операции сложения векторов кластера с номером &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;. Метки кластера &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt; также совместно передаются на узлы &amp;lt;math&amp;gt;S_m, m = \overline{1,k}&amp;lt;/math&amp;gt;, на каждом из которых вычисляется количество векторов в соответствующем кластере (количество меток с соответствующим значением). Далее каждая пара выходов узлов &amp;lt;math&amp;gt;+_m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S_m&amp;lt;/math&amp;gt; подается на узел &amp;quot;&amp;lt;math&amp;gt;/&amp;lt;/math&amp;gt;&amp;quot;, где производится деление суммы векторов кластера на количество элементов в нем. Значения, вычисленные на узлах &amp;quot;&amp;lt;math&amp;gt;/&amp;lt;/math&amp;gt;&amp;quot;, присваиваются новым центрам кластеров (выходные значения графа).&lt;br /&gt;
&lt;br /&gt;
[[file:Recluster.png|thumb|center|800px|Рис. 3. Схема пересчета центров кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Работа алгоритма состоит из &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; итераций, в каждой из которых происходит &amp;lt;b&amp;gt;распределение d-мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам&amp;lt;/b&amp;gt;, а также &amp;lt;b&amp;gt;пересчет центров кластеров в d-мерном пространстве&amp;lt;/b&amp;gt;. В шаге &amp;lt;b&amp;gt;распределения d-мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам&amp;lt;/b&amp;gt; расстояния между вектором и центрами кластеров вычисляются независимо (отсутствуют информационные зависимости). &amp;lt;b&amp;gt;Центры масс кластеров&amp;lt;/b&amp;gt; также пересчитываются независимо друг от друга. Таким образом, имеет место [[глоссарий#Массовый параллелизм|&#039;&#039;массовый параллелизм&#039;&#039;]]. Вычислим параллельную сложность &amp;lt;math&amp;gt;\Psi_*&amp;lt;/math&amp;gt; каждого из шагов, а также параллельную сложность всего алгоритма, &amp;lt;math&amp;gt;\Psi_{k-means}&amp;lt;/math&amp;gt;. Будем исходить из предположения, что может быть использовано любое необходимое число потоков.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Распределение d-мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;  &lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Поскольку на данном шаге для каждой пары векторов &amp;lt;math&amp;gt;\mathbf{x}_i, i=\overline{1,n}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mathbf{\mu}_j, j=\overline{1,k}&amp;lt;/math&amp;gt; операции вычисления расстояния не зависят друг от друга, они могут выполняться параллельно. Тогда, разделив все вычисление расстояний на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; потоков, получим, что в каждом потоке будет выполняться только одна операция вычисления расстояния между векторами размерности d. При этом каждому вычислительному потоку передаются координаты центров всех кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ..., \mathbf{\mu}_k&amp;lt;/math&amp;gt;. Таким образом, параллельная сложность данного шага определяется &amp;lt;i&amp;gt; сложностью параллельной операции вычисления расстояния между d-мерными векторами&amp;lt;/i&amp;gt;, &amp;lt;math&amp;gt;\Psi_{distance}^d&amp;lt;/math&amp;gt;  и &amp;lt;i&amp;gt;сложностью определения наиболее близкого кластера&amp;lt;/i&amp;gt; (паралельное взятие минимума по расстояниям), &amp;lt;math&amp;gt;\Psi_{min}^k&amp;lt;/math&amp;gt;. Для оценки &amp;lt;math&amp;gt;\Psi_{distance}^d&amp;lt;/math&amp;gt; воспользуемся  [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения частичной суммы элементов массива путем сдваивания]]. Аналогично, &amp;lt;math&amp;gt;\Psi_{min}^k = log(k)&amp;lt;/math&amp;gt;. В результате, &amp;lt;math&amp;gt;\Psi_{distance}^d = O(log(d))&amp;lt;/math&amp;gt;.   Таким образом: &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Psi_{distribute}^{k, d}  = \Psi_{distance}^d + \Psi_{min}^k = O(log(d))+O(log(k)) = O(log(k*d))&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Пересчет центров кластеров в d-мерном пространстве&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Поскольку на данном шаге для каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров центр масс может быть вычислен независимо, данные операции могут быть выполнены в отдельных потоках. Таким образом, параллельная сложность данного шага, &amp;lt;math&amp;gt;\Psi_{recenter}^{k, d}&amp;lt;/math&amp;gt;,  будет определяться &amp;lt;i&amp;gt;параллельной сложностью вычисления одного центра масс кластера размера &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&amp;lt;/i&amp;gt;, &amp;lt;math&amp;gt;\Psi_{recenter}^{k, d}&amp;lt;/math&amp;gt;,  а так как &amp;lt;math&amp;gt;m \le n \Rightarrow \Psi_{recenter}^{d, m} \le \Psi_{recenter}^{d, n}&amp;lt;/math&amp;gt;. Сложность вычисления центра масс кластера d-мерных векторов размера n аналогично предыдущим вычислениям равна &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;. Тогда:  &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Psi_{recenter}^{k, d}  \le \Psi_{recenter}^{d, n} = O(log(n))&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Общая параллельная сложность алгоритма&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
На каждой итерации необходимо обновление центров кластеров, которые будут использованы на следующей итерации. Таким образом, итерационный процесс выполняется последовательно&amp;lt;ref&amp;gt;Zhao, Weizhong, Huifang Ma, and Qing He. &amp;quot;Parallel k-means clustering based on mapreduce.&amp;quot; IEEE International Conference on Cloud Computing. Springer Berlin Heidelberg, 2009.&amp;lt;/ref&amp;gt;. Тогда, поскольку сложность каждой итерации  определяется &amp;lt;math&amp;gt;\Psi_{distribute}^{k, d}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Psi_{recenter}&amp;lt;/math&amp;gt;, сложность всего алгоритма, &amp;lt;math&amp;gt;\Psi_{k-means}&amp;lt;/math&amp;gt; в предположении, что было сделано &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; операций определяется выражением&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Psi_{k-means} \approx \Tau * (\Psi_{distribute}^{k, d} + \Psi_{recenter}^{k, d}) \le \Tau * O(log(k*d*n))&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Входные данные&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Матрица из &amp;lt;math&amp;gt;n*d&amp;lt;/math&amp;gt; элементов &amp;lt;math&amp;gt;x_{i, j} \in \mathbb{R}, i=\overline{1, n}, j=\overline{1, d}&amp;lt;/math&amp;gt;&amp;amp;ndash; координат векторов (наблюдений).&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Целое положительное число &amp;lt;math&amp;gt;k, k \le n&amp;lt;/math&amp;gt; &amp;amp;ndash; количество кластеров.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Объем входных данных&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; целое число + &amp;lt;math&amp;gt;n*d&amp;lt;/math&amp;gt; вещественных чисел (при условии, что координаты &amp;amp;ndash; вещественные числа).&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Выходные данные&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; целых положительных чисел &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt;&amp;amp;ndash; номера кластеров, соотвествующие каждому вектору (при условии, что нумерация кластеров начинается с &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;).&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Объем выходных данных&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; целых положительных чисел.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; равна &amp;lt;math&amp;gt;\frac{k*d*n*\Tau}{n*d} = k*\Tau &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; &amp;amp;ndash; число кластеров, &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; &amp;amp;ndash; число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Детерминированность |&#039;&#039;Детерминированность&#039;&#039;]] и [[глоссарий#Устойчивость |&#039;&#039;Устойчивость&#039;&#039;]] &amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Алгоритм &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; является итерационным. Количество итераций алгоритма в общем случае не фиксируется и зависит от начального расположения объектов в пространстве, параметра &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, а также от начального приближения центров кластеров, &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;. В результате этого может варьироваться результат работы алгоритма. При неудачном выборе начальных параметров итерационный процесс может сойтись к локальному оптимуму&amp;lt;ref&amp;gt;Von Luxburg, Ulrike. Clustering Stability. Now Publishers Inc, 2010.&amp;lt;/ref&amp;gt;. По этим причинам алгоритм не является ни &amp;lt;b&amp;gt;детермирированным&amp;lt;/b&amp;gt;, ни &amp;lt;b&amp;gt;устойчивым&amp;lt;/b&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Соотношение последовательной и параллельной сложности алгоритма&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\Theta_{k-means}}{\Psi_{k-means}} = \frac{O(\Tau*k*d*n)}{O(\Tau*log(k*d*n))}&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;:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Сравнительно высокая эффективность при простоте реализации&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Высокое качество кластеризации&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Возможность распараллеливания&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Существование множества модификаций&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Недостатки алгоритма&amp;lt;/b&amp;gt;&amp;lt;ref&amp;gt;Ortega, Joaquín Pérez, Ma Del Rocío Boone Rojas, and María J. Somodevilla. &amp;quot;Research issues on K-means Algorithm: An Experimental Trial Using Matlab.&amp;quot;&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Количество кластеров является параметром алгоритма&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Чувствительность к начальным условиям&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Инициализация центров кластеров в значительной степени влияет на результат кластеризации.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Чувствительность к выбросам и шумам&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Выбросы, далекие от центров настоящих кластеров, все равно учитываются при вычислении их центров.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Возможность сходимости к локальному оптимуму&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Итеративный подход не дает гарантии сходимости к оптимальному решению.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Использование понятия &amp;quot;среднего&amp;quot;&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Алгоритм неприменим к данным, для которых не определено понятие &amp;quot;среднего&amp;quot;, например, категориальным данным.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&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;
Исследование масштабируемости параллельной реализации алгоритма k means проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета]. Алгоритм реализован на языке C с использованием средств MPI.&lt;br /&gt;
Для исследования масштабируемости проводилось множество запусков программы с разным значением параметра (количество векторов для кластеризации), а также с различным числом процессоров. Фиксировались результаты запусков &amp;amp;ndash; время работы &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; и количество произведенных итераций алгоритма &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параметры запусков для экспериментальной оценки:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значения &amp;lt;b&amp;gt;количества векторов&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: 20&#039;000, 30&#039;000, 50&#039;000, 100&#039;000, 200&#039;000, 300&#039;000, 500&#039;000, 700&#039;000, 1&#039;000&#039;000, 1&#039;500&#039;000, 2&#039;000&#039;000.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значения &amp;lt;b&amp;gt;количества процессоров&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;: 1, 8, 16, 32, 64, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 480, 512.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значение &amp;lt;b&amp;gt;количества кластеров&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;: 100.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значение &amp;lt;b&amp;gt;размерности векторов&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;: 10.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для проведения экспериментов были сгенерированы нормально распределенные псевдослучайные данные (с использованием Python библиотеки [http://scikit-learn.org/ scikit-learn]):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from sklearn.datasets import make_classification&lt;br /&gt;
X1, Y1 = make_classification(n_features=10, n_redundant=2, n_informative=8,&lt;br /&gt;
                             n_clusters_per_class=1, n_classes=100,&lt;br /&gt;
                             n_samples=2000000)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для заданной конфигурации эксперимента (&amp;lt;math&amp;gt;n, d, p, k&amp;lt;/math&amp;gt;) и полученных результатов (&amp;lt;math&amp;gt;t, \Tau&amp;lt;/math&amp;gt;)  [[Глоссарий#Производительность|производительность]]  и эффективность реализации расчитывались по формулам:&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&amp;lt;math&amp;gt;Performance = \frac{N_{k-means}^{d, n}}{t}\ \ (FLOPS),&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;N_{k-means}^{d, n}&amp;lt;/math&amp;gt; &amp;amp;ndash; точное число операций с плавающей точкой (операции с памятью, а также целочисленные операции не учитывались), вычисленное в соответствие с разделом [[#Последовательная сложность алгоритма| &amp;quot;Последовательная сложность алгоритма&amp;quot;]];&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&amp;lt;math&amp;gt;Efficiency = \frac{100 * Performance}{Performance_{Peak}^{p}}\ \ (%),&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;Performance_{Peak}^{p}&amp;lt;/math&amp;gt; &amp;amp;ndash; пиковая производительность суперкомпьютера при &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; процессорах, вычисленная согласно спецификациям Intel&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; XEON&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; X5670&amp;lt;ref&amp;gt;&amp;quot;http://ark.intel.com/ru/products/47920/Intel-Xeon-Processor-X5670-12M-Cache-2_93-GHz-6_40-GTs-Intel-QPI&amp;quot;&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Графики зависимости производительности и эффективности параллельной реализации k means от числа векторов для кластеризации (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) и числа процессоров (&amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;) представлены на рисунках 4 и 5 соответственно.&lt;br /&gt;
&lt;br /&gt;
[[file:Flops.png|thumb|center|800px|Рис. 4. Параллельная реализация k means. График зависимости производительности реализации алгоритма от числа векторов для кластеризации (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) и числа процессоров (&amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;).]]&lt;br /&gt;
[[file:Efficiency.png|thumb|center|800px|Рис. 5. Параллельная реализация k means. График зависимости эффективности реализации алгоритма от числа векторов для кластеризации (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) и числа процессоров (&amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;).]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;В результате&amp;lt;/b&amp;gt; экспериментальной оценки были получены следующие оценки эффективности реализации:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Минимальное&amp;lt;/b&amp;gt; значение: 0.000409 % достигается при &amp;lt;math&amp;gt;n=20&#039;000, p=480&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Максимальное&amp;lt;/b&amp;gt; значение: 0.741119 % достигается при &amp;lt;math&amp;gt;n=300&#039;000, p=1&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Оценки масштабируемости реализации алгоритма k means:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;По числу процессоров:&amp;lt;/b&amp;gt; -0.002683 &amp;amp;ndash; эффективность убывает с ростом числа процессоров. Данный результат вызван ростом накладных расходов для обеспечения параллельного выполнения алгоритма.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;По размеру задачи:&amp;lt;/b&amp;gt; 0.002779 &amp;amp;ndash; эффективность растет с ростом числа векторов. Данный результат вызван тем, что при увеличении размера задачи, количество вычислений растет по сравнению с временем, затрачиваемым на пересылку данных.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Общая оценка:&amp;lt;/b&amp;gt; -0.000058 &amp;amp;ndash; можно сделать вывод, что в целом эффективность реализации незначительно уменьшается с ростом размера задачи и числа процессоров.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://github.com/serban/kmeans Использованная параллельная реализация алгоритма k means]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
==== Открытое программное обеспечение ====&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.icpsr.umich.edu/CrimeStat/ CrimeStat]&amp;lt;/li&amp;gt; Программное обеспечение, созданное для операционных систем Windows, предоставляющее инструменты статистического  и пространственного анализа для решения задачи картирования преступности.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://juliastats.github.io Julia]&amp;lt;/li&amp;gt; Высокоуровневый высокопроизводительный свободный язык программирования с динамической типизацией, созданный для математических вычислений, содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://mahout.apache.org Mahout]&amp;lt;/li&amp;gt; Apache Mahout - Java библиотека для работы с алгоритмами машинного обучения с использованием MapReduce. Содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.gnu.org/software/octave/ Octave]&amp;lt;/li&amp;gt; Написанная на C++  свободная система для математических вычислений, использующая совместимый с MATLAB язык высокого уровня, содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://spark.apache.org/docs/latest/mllib-clustering.html Spark]&amp;lt;/li&amp;gt; Распределенная реализация k-means содержится в библиотеке Mlib для работы с алгоритмами машинного обучения, взаимодействующая с Python библиотекой NumPy и библиотека R.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://torch.ch Torch]&amp;lt;/li&amp;gt;  MATLAB-подобная библиотека для языка программирования Lua с открытым исходным кодом, предоставляет большое количество алгоритмов для глубинного обучения и научных расчётов. Ядро написано на Си, прикладная часть выполняется на LuaJIT, поддерживается распараллеливание вычислений средствами CUDA и OpenMP. Существуют реализации k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://www.cs.waikato.ac.nz/ml/weka/ Weka]&amp;lt;/li&amp;gt;  Cвободное программное обеспечение для анализа данных, написанное на Java. Содержит k-means и x-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://accord-framework.net/docs/html/T_Accord_MachineLearning_KMeans.htm Accord.NET]&amp;lt;/li&amp;gt; C# реализация алгоритмов k-means, k-means++, k-modes.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_ml/py_kmeans/py_kmeans_opencv/py_kmeans_opencv.html OpenCV]&amp;lt;/li&amp;gt; Написанная на С++ библиотека, направленная в основном на решение задач компьютерного зрения. Содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://mlpack.org/ MLPACK]&amp;lt;/li&amp;gt; Масштабируемая С++ библиотека для работы с алгоритмами машинного обучения, содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.scipy.org/ SciPy]&amp;lt;/li&amp;gt; Библиотека Python, содержит множество реализаций k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://scikit-learn.org/ scikit-learn]&amp;lt;/li&amp;gt; Библиотека Python, содержит множество реализаций k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.r-bloggers.com/k-means-clustering-in-r/ R]&amp;lt;/li&amp;gt; Язык программирования для статистической обработки данных и работы с графикой, а также свободная программная среда вычислений с открытым исходным кодом в рамках проекта GNU, содержит три реализации k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://elki.dbs.ifi.lmu.de ELKI]&amp;lt;/li&amp;gt; Java фреймворк, содержащий реализацию k-means, а также множество других алгоритмов кластеризации.&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
==== Проприетарное программное обеспечение  ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.ayasdi.com/blog/bigdata/topological-data-analysis-of-oil-and-gas-petrophysical-data/ Ayasdi]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://www.stata.com/features/cluster-analysis/ Stata]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://mathworld.wolfram.com/K-MeansClusteringAlgorithm.html Mathematica]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://www.mathworks.com/help/stats/kmeans.html?requestedDomain=www.mathworks.com MATLAB]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://support.sas.com/rnd/app/stat/procedures/fastclus.html SAS]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://docs.rapidminer.com/studio/operators/modeling/segmentation/k_means.html RapidMiner]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://blogs.sap.com/2013/03/28/sap-hana-pal-k-means-algorithm-or-how-to-do-customer-segmentation-for-the-telecommunications-industry/ SAP HANA]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18990</id>
		<title>Участник:Илья Егоров/Алгоритм k-средних</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18990"/>
		<updated>2016-11-23T07:18:34Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Zhum}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k-средних (k-means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n*k*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n*d&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;
Страница создана группой &amp;quot;[[U:Илья Егоров|Илья Егоров]] — [[U:Богомазов Евгений|Евгений Богомазов]]&amp;quot;.&lt;br /&gt;
&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;
Алгоритм кластеризации k-средних впервые был предложен в 1950-х годах математиками Гуго Штейнгаузом и Стюартом Ллойдом независимо друг от друга. Наибольшую популярность он получил после работы Маккуина.&lt;br /&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;
В общем случае выбор числа &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; может базироваться на любых значимых факторах, в том числе на результатах предшествующих исследований, теоретических соображениях или интуиции. &lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Исходные данные:&#039;&#039;&#039;&lt;br /&gt;
* Совокупность &amp;lt;b&amp;gt;n&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;-мерных векторов &amp;lt;math&amp;gt; X = \{x_1 \dots x_n\} , &amp;lt;/math&amp;gt; где  &amp;lt;math&amp;gt; x_i = \{x_{i1} \dots x_{id}\} &amp;lt;/math&amp;gt;&lt;br /&gt;
* Предполагаемое количество кластеров &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
* Разбиение X на множество &amp;lt;math&amp;gt; S = \{S_1 \dots S_k \}, \bigcup S_i = X, S_i \cap S_j = \emptyset, i \neq j &amp;lt;/math&amp;gt;&lt;br /&gt;
* k центров кластеров &amp;lt;math&amp;gt; \Mu = \{\mu_1 \dots \mu_k \} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \mu_i = \{\mu_{i1} \dots \mu_{id} \} &amp;lt;/math&amp;gt; такие, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \begin{cases}\tilde{\mu_i} = \underset{y}{argmin } \sum_{x \in S_i} ||x-y||^2_E \\ \Mu = \underset{\tilde{\Mu}}{argmin } \sum_{i \in k} \sum_{x \in S_i} ||x-\tilde{\mu_i}||^2_E \end{cases} &amp;lt;/math&amp;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; \mu_{i} = random &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) &amp;lt;math&amp;gt; S_i = \{x \in S~|~\underset{j}{argmin } ||x-\mu_j||_E = i\} i = 1 \dots k &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3) &amp;lt;math&amp;gt; \tilde{\mu_{i}} = E_{x \in S_i}(x) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4) Если для &amp;lt;math&amp;gt; \forall i:  \mu_i = \tilde{\mu_i} &amp;lt;/math&amp;gt; то алгоритм завершен, иначе &amp;lt;math&amp;gt; \mu_i = \tilde{\mu_i} &amp;lt;/math&amp;gt; и перейти на пункт 2)&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительным ядром алгоритма является второй этап, а точнее нахождение матрицы расстояний между &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Mu&amp;lt;/math&amp;gt;. Для &#039;&#039;&#039;d&#039;&#039;&#039;-мерного вектора &amp;lt;math&amp;gt;a:&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;||a||=\sqrt{\sum_{i=1\dots d} a_i^2}&amp;lt;/math&amp;gt;, поэтому заполнение одной ячейки такой матрицы потребует &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения, &amp;lt;math&amp;gt;d-1&amp;lt;/math&amp;gt; операций сложения и одну операция вычисления квадратного корня. Но так как эти расстояния используются только для сравнения, а &#039;&#039;sqrt&#039;&#039; является монотонно возрастающей функцией, то ее можно не вычислять. Поэтому нахождения матрицы расстояний потребует всего &amp;lt;math&amp;gt;n*k*d&amp;lt;/math&amp;gt; операций умножений и &amp;lt;math&amp;gt;n*k*(d-1)&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;
*&#039;&#039;&#039;Суммирование векторов&#039;&#039;&#039;,&lt;br /&gt;
*&#039;&#039;&#039;Поиск минимума в векторе&#039;&#039;&#039;,&lt;br /&gt;
*&#039;&#039;&#039;Сравнение векторов&#039;&#039;&#039;,&lt;br /&gt;
*&#039;&#039;&#039;Деление вектора на скаляр&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1)&#039;&#039;&#039; На шаге инициализации центроидов в общем случае (случайный выбор элементов) макроопераций не производится. Но так как правило определения количества кластеров &#039;&#039;&#039;k&#039;&#039;&#039; неоднозначно, то для некоторых вариантов макрооперации могут понадобиться. К примеру, существует класс стратегий поиска &#039;&#039;&#039;k&#039;&#039;&#039;, которые базируются на определении центра масс всех объектов, и для которых будут дополнительно выполняться макрооперации &#039;&#039;&#039;&amp;quot;Суммирование векторов&amp;quot;&#039;&#039;&#039; и &#039;&#039;&#039;&amp;quot;Деление вектора на скаляр&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2a)&#039;&#039;&#039; При составлении матрицы расстояний &#039;&#039;&#039; k &amp;lt;math&amp;gt; \cdot &amp;lt;/math&amp;gt;  n&#039;&#039;&#039; раз производится макрооперация &#039;&#039;&#039;&amp;quot;Вычисление расстояния между векторами&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2b)&#039;&#039;&#039; При нахождение вектора распределения объектов по кластерам &#039;&#039;&#039;n&#039;&#039;&#039; раз производится макрооперация &#039;&#039;&#039;&amp;quot;Поиск минимума в векторе&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3)&#039;&#039;&#039; При пересчете центроидов &#039;&#039;&#039;n - k&#039;&#039;&#039; раз производится макрооперация &#039;&#039;&#039;&amp;quot;Суммирование векторов&amp;quot;&#039;&#039;&#039; и &#039;&#039;&#039;k&#039;&#039;&#039; раз — &#039;&#039;&#039;&amp;quot;Деление вектора на скаляр&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4)&#039;&#039;&#039; Для проверки критерия останова требуется &#039;&#039;&#039;k&#039;&#039;&#039; раз произвести макрооперацию &#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;
    &#039;&#039;&#039;1) Инициализация центроидов &amp;lt;math&amp;gt;\Mu&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; iter = 1 &amp;lt;/math&amp;gt;, задание максимального количество итераций &amp;lt;math&amp;gt;maxiter&amp;lt;/math&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    &#039;&#039;&#039;2a) Нахождение матрицы расстояний &#039;&#039;&#039;&amp;lt;math&amp;gt;dist:&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
        &amp;lt;math&amp;gt;dist_{ij} = \sum_{l = 1\dots d} (x_{il}-\mu_{jl})^2&amp;lt;/math&amp;gt;&lt;br /&gt;
        &lt;br /&gt;
    &#039;&#039;&#039;2b) Нахождение вектора распределения объектов по кластерам &#039;&#039;&#039;&amp;lt;math&amp;gt;index: &amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
        &amp;lt;math&amp;gt;index_{i} =   \underset{j}{argmin }~dist_{ij} &amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;3) Пересчет центроидов &#039;&#039;&#039;&amp;lt;math&amp;gt;\tilde{\Mu}:&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
        &amp;lt;math&amp;gt;\tilde{\mu_{ij}} = \sum_{l \in S_i} \dfrac{x_{lj}}{|S_i|} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S_i = \{l~|~l \in 1\dots n, index_l = i\}  &amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;4) Проверка критерия останова:&#039;&#039;&#039;&lt;br /&gt;
    &lt;br /&gt;
        &#039;&#039;&#039;Если&#039;&#039;&#039; &amp;lt;math&amp;gt; \exists i: \tilde{\mu_i} \neq \mu_i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; iter &amp;lt; maxiter &amp;lt;/math&amp;gt;,&lt;br /&gt;
    &lt;br /&gt;
        &#039;&#039;&#039;то&#039;&#039;&#039; &amp;lt;math&amp;gt; inc(iter), \Mu=\tilde{\Mu},&amp;lt;/math&amp;gt; &#039;&#039;&#039;GOTO 2.а.&#039;&#039;&#039;&lt;br /&gt;
&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;2a)&#039;&#039;&#039; Вычисление матрицы расстояний требует &amp;lt;math&amp;gt; n*k*d &amp;lt;/math&amp;gt; операций умножения и &amp;lt;math&amp;gt; n*k*(d-1) &amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;2b)&#039;&#039;&#039; Нахождение вектора распределения требует &amp;lt;math&amp;gt; n*(k-1) &amp;lt;/math&amp;gt; операций сравнения&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;3)&#039;&#039;&#039; Для вычисления &amp;lt;math&amp;gt; \tilde{\Mu} &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt; (n - k + 1) * d &amp;lt;/math&amp;gt; операций сложения и &amp;lt;math&amp;gt; k * d &amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;4)&#039;&#039;&#039; Для критерия останова требуется &amp;lt;math&amp;gt; n*d &amp;lt;/math&amp;gt; сравнений&lt;br /&gt;
&lt;br /&gt;
Итого: так как максимальное количество итераций задается в алгоритме заранее и не зависит от входных данных, то количество итераций ограничено константой. Тогда сложность алгоритма:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt; O(n*k*d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt; O(n*k*d)&amp;lt;/math&amp;gt; операций умножения, &amp;lt;math&amp;gt; O(k*d) &amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
Рассмотрим вычислительные этапы алгоритма k-means. Это распределение объектов по кластерам и перерасчет центроидов.&lt;br /&gt;
&lt;br /&gt;
Распределение объектов по кластерам состоит из нахождения матрицы расстояний между &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Mu&amp;lt;/math&amp;gt; (этапа &#039;&#039;&#039;2a)&#039;&#039;&#039;) и нахождения индекса минимального элемента в каждой строке (этап &#039;&#039;&#039;2b)&#039;&#039;&#039;). Ниже приведен информационный граф данного этапа.&lt;br /&gt;
&lt;br /&gt;
[[Файл:EgorovBogomazovInf.png |800px|thumb|center|Нахождение вектора индексов распределения по кластерам]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассмотрим этап вычисления ячейки &amp;lt;math&amp;gt; dist_{ij} &amp;lt;/math&amp;gt; матрицы более подробно. Он представляет из себя скалярное произведение d-мерных векторов &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \mu_j &amp;lt;/math&amp;gt;. Ниже приведена граф для данной операции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:EgorovBogomazovDist.png|600px|thumb|center|Операция вычисления расстояния]]&lt;br /&gt;
&lt;br /&gt;
На этапе &#039;&#039;&#039;3)&#039;&#039;&#039; происходит суммирование всех &amp;lt;math&amp;gt; x_l &amp;lt;/math&amp;gt; с одинаковым значением индекса в промежуточные суммы. Одновременно происходит подсчет количество элементов с фиксированным индексом. Затем происходит деление промежуточных сумм на соответствующее количество элементов в новом кластере, благодаря чему удается получить обновленные центроиды &amp;lt;math&amp;gt; \tilde{\mu}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:EgorovBogomazovNewCluster.png|800px|thumb|center|Обновление центроидов]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Для получения общей картины достаточно определить, в том числе и с помощью информационного графа, каким ресурсом параллелизма обладает каждый из этапов алгоритма:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2a)&#039;&#039;&#039; Нахождение каждой ячейки матрицы &amp;lt;math&amp;gt; dist &amp;lt;/math&amp;gt; может происходить полностью независимо, так как они находятся на одном ярусе. При рассмотрении операции вычисления значения конкретной ячейки &amp;lt;math&amp;gt; dist_{ij} &amp;lt;/math&amp;gt; можно заметить, что все операции умножения также независимы между собой, поэтому понадобится всего одно умножение. Конечный результат получается в результате суммирования всех слагаемых и достигается параллельно за &amp;lt;math&amp;gt; \log(d) &amp;lt;/math&amp;gt; бинарных операций сложения при помощи стандартного метода &#039;&#039;reduction&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2b)&#039;&#039;&#039; Нахождение вектора распределений заключается в нахождении минимального элемента в каждой строке матрицы &amp;lt;math&amp;gt; dist &amp;lt;/math&amp;gt;. При использовании &#039;&#039;reduction&#039;&#039; эту операцию можно выполнить за &amp;lt;math&amp;gt; \log(k) &amp;lt;/math&amp;gt; бинарных операций сравнения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3)&#039;&#039;&#039; В случае, если существует методология быстрого разделения &#039;&#039;&#039;n&#039;&#039;&#039; объектов на &#039;&#039;&#039;k&#039;&#039;&#039; множеств при помощи вектора индексации (косвенной адресации), все &amp;lt;math&amp;gt; \tilde{S_i} &amp;lt;/math&amp;gt; могут быть обработаны параллельно. В рамках каждого отдельного &amp;lt;math&amp;gt; \tilde{S_i} &amp;lt;/math&amp;gt; можно проводить распараллеливание дальше, так как &amp;lt;math&amp;gt; x_{lj} &amp;lt;/math&amp;gt; также не зависят друг от друга. Таким образом, вычисление всех слагаемых для всех &amp;lt;math&amp;gt; \tilde{\mu_{ij}} &amp;lt;/math&amp;gt; может быть выполнено за одно параллельное деление, а нахождение суммы всех этих слагаемых для каждого &amp;lt;math&amp;gt; \tilde{\mu_{ij}} &amp;lt;/math&amp;gt; может быть выполнено за &amp;lt;math&amp;gt; O(log(n)) &amp;lt;/math&amp;gt; в худшем случае (при вхождении почти всех объектов в один кластер).&lt;br /&gt;
&lt;br /&gt;
Если же такой методологии не существует, то узким местом становится процесс распределения на множества согласно вектору &amp;lt;math&amp;gt;index&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;
&#039;&#039;&#039;4)&#039;&#039;&#039; Все сравнения для критерия останова также независимы между собой и могут быть выполнены за одну операцию сравнения, а финальный результат может быть получен за одну операцию сложения при работе над общей памятью (к примеру, прибавление единицы при несовпадении в общую память и сравнение ее с 0 по завершению) или за &amp;lt;math&amp;gt;\log(k*d)&amp;lt;/math&amp;gt; сложений иначе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так как количество итераций ограничено константой, то на сложность оно не влияет. В итоге, параллельная сложность алгоритма k-means такова:&lt;br /&gt;
* &amp;lt;math&amp;gt; O(\log(n*d)) &amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt; O(\log(k)) &amp;lt;/math&amp;gt; операций сравнения&lt;br /&gt;
* &amp;lt;math&amp;gt; O(1) &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;b&amp;gt;k&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;n&amp;lt;/b&amp;gt; кластеризуемых элементов&lt;br /&gt;
&lt;br /&gt;
Дополнительные ограничения:&lt;br /&gt;
* &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt; — положительное число, т. е. &amp;lt;b&amp;gt;k &amp;gt; 0&amp;lt;/b&amp;gt;.&lt;br /&gt;
* Для кластеризуемых элементов определена метрика (расстояние между объектами)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;b&amp;gt;n &amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt; d + 1&amp;lt;/b&amp;gt; (кластеризуемые объекты в виде векторов и число &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: Массив, в который записаны принадлежности каждого элемента кластеру (допустим вывод в другой эквивалентной более удобной структуре).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;:  Размер массива равен &amp;lt;b&amp;gt;2 &amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt; n&amp;lt;/b&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;n\cdot k\cdot d&amp;lt;/math&amp;gt; умножений&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;n \cdot d + 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность &amp;lt;math&amp;gt; = \dfrac{n \cdot k \cdot d}{n\cdot d + 1} = k&amp;lt;/math&amp;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;
&#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;
&#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;
Проведём исследование масштабируемости параллельной реализации алгоритма k-средних.  Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета]. Параллельная реализация была написана самостоятельно на языке C, [http://git.algowiki-project.org/iaegorov/Egorov-Bogomazov-k-means/tree/master ссылка на реализацию]. Так как на каждой итерации число действий на единицу данных не велико и данные должны быть собраны вместе при перерасчете центроидов, было решено для ускорения вычислений &#039;&#039;&#039;воспользоваться только OpenMP без MPI&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 16] с увеличением в 2 раза;&lt;br /&gt;
*	размер данных [100000 : 1600000] с увеличением в 2 раза.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов были получены следующие данные:&lt;br /&gt;
&lt;br /&gt;
*	Максимальная эффективность в точке достигается при переходе от 1 потока на 8 при минимальном размере данных, она равна &amp;lt;math&amp;gt;87,5%&amp;lt;/math&amp;gt;.&lt;br /&gt;
*	Усредненная максимальная эффективность достигается при переходе с одного потока на два. Среднее время вычислений на всех рассмотренных потока снижается с 16,33 до 11.87 секунд, поэтому формально эффективность &amp;lt;math&amp;gt;= 16.33 / 11.87 / 2 \approx 68,4\%&amp;lt;/math&amp;gt;&lt;br /&gt;
*	Минимальная эффективность в точке достигается при переходе от 1 потока на 16 при размере данных 800000, она равна &amp;lt;math&amp;gt;11,1\%&amp;lt;/math&amp;gt;.&lt;br /&gt;
*	Усредненная минимальная эффективность наблюдается при переходе с одного на максимальное рассматриваемое в эксперименте число потоков, равное 16. Время вычисления изменяется с 16,33 до 7,6 секунд, поэтому формально эффективность &amp;lt;math&amp;gt; = 16.33 / 7.6 / 16 \approx 14,9\%&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ниже приведены графики зависимости вычислительного времени алгоритма и его эффективности от изменяемых параметров запуска — размера данных и числа процессоров:&lt;br /&gt;
&lt;br /&gt;
[[file:Egorov_Bogomazov_Time.jpg|thumb|center|700px|Параллельная реализация алгоритма k-средних. Изменение вычислительного времени алгоритма в зависимости от числа процессоров и размера исходных данных.]]&lt;br /&gt;
&lt;br /&gt;
Здесь видно, что время выполнения операций алгоритма плавно убывает по каждому из параметров, причем скорость убывания по параметру числа процессоров выше, чем в зависимоти от размера данных.&lt;br /&gt;
&lt;br /&gt;
[[file:Egorov_Bogomazov_Efficiency.jpg|thumb|center|700px|Параллельная реализация алгоритма k-средних. Изменение эффективности алгоритма в зависимости от числа процессоров и размера исходных данных.]]&lt;br /&gt;
&lt;br /&gt;
Здесь построена эффективность перехода от последовательной реализации к параллельной. Рассчитывается она по формуле &#039;&#039;Время вычисления на 1 потоке / Время вычисления на T потоках / T&#039;&#039;, где T — это число потоков. При вычислении на 1 процессоре она равна 100 % в силу используемой формулы, что и отражено на графике.&lt;br /&gt;
&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;&#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;
&#039;&#039;&#039;Десктопные программы&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/ml/weka/ Weka]&lt;br /&gt;
* [http://docs.orange.biolab.si/2/reference/rst/Orange.clustering.kmeans.html Orange]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фреймворки для языков&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Python:&#039;&#039;&#039;&lt;br /&gt;
** [http://scikit-learn.org/ scikit-learn]&lt;br /&gt;
** [https://www.scipy.org/ SciPy]&lt;br /&gt;
* &#039;&#039;&#039;C++:&#039;&#039;&#039;&lt;br /&gt;
** [http://mlpack.org/ MLPACK]&lt;br /&gt;
** [http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_ml/py_kmeans/py_kmeans_opencv/py_kmeans_opencv.html OpenCV]&lt;br /&gt;
** [https://www.gnu.org/software/octave/ Octave] &lt;br /&gt;
* &#039;&#039;&#039;C#:&#039;&#039;&#039;  &lt;br /&gt;
** [http://accord-framework.net/docs/html/T_Accord_MachineLearning_KMeans.htm Accord.NET]&lt;br /&gt;
* &#039;&#039;&#039;Java:&#039;&#039;&#039;&lt;br /&gt;
** [http://elki.dbs.ifi.lmu.de ELKI]&lt;br /&gt;
** [https://mahout.apache.org Mahout]&lt;br /&gt;
* &#039;&#039;&#039;Lua&#039;&#039;&#039;&lt;br /&gt;
** [http://torch.ch Torch]&lt;br /&gt;
&lt;br /&gt;
Языки [https://www.r-bloggers.com/k-means-clustering-in-r/ R] и [http://juliastats.github.io Julia] содержат алгоритм k-means в базовой реализации.&lt;br /&gt;
&lt;br /&gt;
=== Платный доступ/лицензия  ===&lt;br /&gt;
&lt;br /&gt;
Существует целый перечень мощных статистических и математических пакетов для разных ОС:&lt;br /&gt;
* [http://www.mathworks.com/help/stats/kmeans.html?requestedDomain=www.mathworks.com MATLAB]&lt;br /&gt;
* [http://mathworld.wolfram.com/K-MeansClusteringAlgorithm.html Mathematica]&lt;br /&gt;
* [http://www.stata.com/features/cluster-analysis/ Stata]&lt;br /&gt;
* [https://support.sas.com/rnd/app/stat/procedures/fastclus.html SAS]&lt;br /&gt;
* [https://blogs.sap.com/2013/03/28/sap-hana-pal-k-means-algorithm-or-how-to-do-customer-segmentation-for-the-telecommunications-industry/ SAP HANA]&lt;br /&gt;
* [http://docs.rapidminer.com/studio/operators/modeling/segmentation/k_means.html RapidMiner]&lt;br /&gt;
* ...&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] https://ru.wikipedia.org/wiki/K-means&lt;br /&gt;
&lt;br /&gt;
[[en:Description of algorithm properties and structure]]&lt;/div&gt;</summary>
		<author><name>Zhum</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:Avasilenko/Partitioning_Around_Medoids_(PAM)&amp;diff=17547</id>
		<title>Участник:Avasilenko/Partitioning Around Medoids (PAM)</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:Avasilenko/Partitioning_Around_Medoids_(PAM)&amp;diff=17547"/>
		<updated>2016-11-16T08:28:45Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Partitioning Around Medoids&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(T*kn^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n*(n-1)+2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;k+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(T*kn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(k(n-k))&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Авторы&#039;&#039;&#039;:&lt;br /&gt;
* [[Участник:Avasilenko|А.Э.Василенко]] (отвечает за программистскую составляющую);&lt;br /&gt;
* [[Участник:Anastasia|А.В.Тузикова]] (отвечает за математическую составляющую).&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;Rousseeuw P. J., Kaufman L. Finding Groups in Data. – Wiley Online Library, 1990.&amp;lt;/ref&amp;gt;. Более того - это важная часть процесса познания мира, в котором мы живем. Мы учим детей различать кошек и собак, воду и песок, мужчин и женщин путем постоянного развития и улучшения подсознательных схем классификации. Примеры есть и во взрослой жизни – в химии необходимо классифицировать соединения, в археологии – объединять находки по периодам и т.д.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Задача кластеризации&#039;&#039;&#039; - разделение множества объектов на заданное число множеств с минимизацией некоторого функционала - является популярной задачей, относится к разделу машинного обучения и находит применение в таких областях, как анализ текста, биоинформатика, интеллектуальные транспортные системы и др. &amp;lt;ref&amp;gt; Речкалов Т.В. Параллельный алгоритм кластеризации для многоядерного сопроцессора Intel Xeon Phi // Суперкомпьютерные дни в России: Труды vеждународной конференции. – М.: Изд-во МГУ, 2015, c. 532.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует несколько видов кластеризации&amp;lt;ref&amp;gt;Маннинг К.Д., Введение в информационный поиск: пер. с англ. / К.Д. Маннинг, П. Рагхаван, Х.Шютце - М. : Вильямс, 2011. - 528.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;плоская кластеризация&#039;&#039; (&#039;&#039;flat clustering&#039;&#039;) - порождает совокупность кластеров, не имеющих явных взаимосвязей;&lt;br /&gt;
* &#039;&#039;иерархическая кластеризация&#039;&#039; (&#039;&#039;hierarchical clustering&#039;&#039;) - создаёт иерархию кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PAM&#039;&#039;&#039; (&#039;&#039;&#039;Partitioning Around Medoids&#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;
&lt;br /&gt;
&lt;br /&gt;
Модель &amp;lt;ref&amp;gt;Kaufman, L. and Rousseeuw, P.J., Clustering by means of Medoids. In: Y. Dodge and North-Holland, editor. Statistical Data Analysis Based on the L1-Norm and Related Methods. Springer US; 1987, p. 405–416.&amp;lt;/ref&amp;gt;, лежащая в основе алгоритма PAM, основана на выборе &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; объектов, являющихся характерными точками соответствующего кластера. Таким образом, кластерам сопоставляются принадлежащие им объекты, называемые &#039;&#039;&#039;медоидами&#039;&#039;&#039;, на основании которых распределяются остальные объекты по принципу наибольшего сходства. Cама модель называется моделью &#039;&#039;&#039;k-medoids&#039;&#039;&#039; (не путать с моделью &#039;&#039;k-median&#039;&#039;).&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
PAM состоит из двух фаз: &#039;&#039;&#039;BUILD&#039;&#039;&#039; и &#039;&#039;&#039;SWAP&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
*   на фазе BUILD выполняется первичная кластеризация, в ходе которой последовательно выбираются &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; объектов в качестве медоидов;&lt;br /&gt;
*   фаза SWAP представляет собой итерационный процесс, в ходе которого производятся попытки улучшить множество медоидов. Алгоритм выполняет поиск пары объектов (медоид, не-медоид), минимизирующих целевую функцию при замене, после чего обновляет множество медоидов.&lt;br /&gt;
&lt;br /&gt;
На каждой итерации алгоритма выбирается пара (медоид, не-медоид) такая, что замена медоида &amp;lt;math&amp;gt;x_{m_l}&amp;lt;/math&amp;gt; на не-медоид &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt; дает лучшую кластеризацию из возможных. Оценка кластеризации выполняется с помощью целевой функции, вычисляемой как сумма расстояний от каждого объекта до ближайшего медоида:&lt;br /&gt;
 &lt;br /&gt;
:&amp;lt;math&amp;gt;E = \sum_{j = 1}^n \min_{1 \leq l \leq k} \rho ( x_{m_l},x_{o_h} )&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;:&lt;br /&gt;
 &lt;br /&gt;
*   множество кластеризуемых объектов &amp;lt;math&amp;gt;X = \{ x_1, x_2, ... , x_n \} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; - кортеж, состоящий из &amp;lt;math&amp;gt; w &amp;lt;/math&amp;gt; вещественных чисел;&lt;br /&gt;
*   симметрическая матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, элементы матрицы &amp;lt;math&amp;gt;d_{ij}=d(i,j)&amp;lt;/math&amp;gt; - вычисленные расстояния между объектами &amp;lt;math&amp;gt;x_i, x_j&amp;lt;/math&amp;gt;;&lt;br /&gt;
*   количество кластеров &amp;lt;math&amp;gt;k \leq n.&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Обозначения&#039;&#039;:&lt;br /&gt;
*   &amp;lt;math&amp;gt;M = \{ x_{m_1}, x_{m_2}, ... , x_{m_k} \} &amp;lt;/math&amp;gt; - множество медоидов, &amp;lt;math&amp;gt; M \subseteq X ;&amp;lt;/math&amp;gt;&lt;br /&gt;
*   &amp;lt;math&amp;gt;O = \{ x_{o_1}, x_{o_2}, ... , x_{o_r} \} &amp;lt;/math&amp;gt; - множество не-медоидов, &amp;lt;math&amp;gt; O \subseteq X, &amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;r=n-k;&amp;lt;/math&amp;gt;&lt;br /&gt;
*   &amp;lt;math&amp;gt;d_{ij}=\rho (x_i, x_j )&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \rho  : X \times X \rightarrow R &amp;lt;/math&amp;gt; — метрика расстояния;&lt;br /&gt;
*   &amp;lt;math&amp;gt;F_{M,O} = \sum_{j = 1}^{n} \min_{ x_{m_l} \in M} \rho ( x_{m_l},x_{o_j} )&amp;lt;/math&amp;gt; - значение целевой функции для заданных множеств &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
*   &amp;lt;math&amp;gt;T^i&amp;lt;/math&amp;gt; - изменение целевой функции на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге фазы SWAP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;M = \{ x_{m_1}, x_{m_2}, ... , x_{m_k} \}&amp;lt;/math&amp;gt; - множество медоидов;&lt;br /&gt;
* &amp;lt;math&amp;gt;K_{m_i} = \{ x_{o_h} \in O \| x_{m_i} = \arg \min_{x_{m_s} \in M} \rho (x_{o_h}, x_{m_s}) \}, 1 \leq i \leq k&amp;lt;/math&amp;gt; - искомые кластеры.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычислительные формулы метода&#039;&#039;:&lt;br /&gt;
 &lt;br /&gt;
*   &#039;&#039;фаза BUILD&#039;&#039; (состоит из &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;
**   &amp;lt;math&amp;gt;O_0 = X, M_0 = \varnothing ;&amp;lt;/math&amp;gt;&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_1} = \arg \min_{x_{o_h} \in O_0} \sum_{j = 1}^n \rho \left ( x_{o_h},x_j \right ), O_1 = O_0 \backslash \{x_{m_1}\}, M_1 = M_1 \cup \{x_{m_1}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_2} = \arg \min_{x_{o_h} \in O_1} \sum_{j = 1}^{n} \min ( \rho ( x_{m_1},x_j ), \rho ( x_{o_h},x_j ) ), O_2 = O_1 \backslash \{x_{m_2}\}, M_2 = M_1 \cup \{x_{m_2}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_3} = \arg \min_{x_{o_h} \in O_2} \sum_{j = 1}^{n} \min_{1 \leq l \leq 2} ( \rho ( x_{m_l},x_j ) , \rho ( x_{o_h},x_j ) ) , O_3 = O_2 \backslash \{x_{m_3}\}, M_3 = M_3 \cup \{x_{m_3}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
** ...&lt;br /&gt;
**   &amp;lt;math&amp;gt;x_{m_k} = \arg \min_{x_{o_h} \in O_{k-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{k-1}} ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j ) ) , O_k = O_{k-1} \backslash \{x_{m_k}\}, M_k = M_{k-1} \cup \{x_{m_k}\} ; &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;фаза SWAP&#039;&#039; (итерационный процесс):&lt;br /&gt;
** начальное приближение: &amp;lt;math&amp;gt;O_0 = O_k^{BUILD}, M_0 = M_k^{BUILD};&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й шаг итерации:&lt;br /&gt;
*** вычисление целевой функции при оптимальной замене (замене, при которой достигается лучшая кластеризация из возможных на текущей итерации): &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;F^i = \min_{x_{m_s} \in M_{i-1} , x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j) ); &amp;lt;/math&amp;gt;&lt;br /&gt;
*** определение пары (медоид, не-медоид), на которой достигается оптимальная замена: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt; (x_{m_s}, x_{o_h}) = \arg F^i; &amp;lt;/math&amp;gt;&lt;br /&gt;
*** проверка критерия останова;&lt;br /&gt;
*** изменение множеств в соответствии с выбранной парой &amp;lt;math&amp;gt; (x_{m_s}, x_{o_h}):&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;M_i = M_{i-1} \backslash \{x_{m_s}\} \cup \{x_{o_h}\} , O_i = O_{i-1} \cup \{x_{m_s}\} \backslash \{x_{o_h}\} ;&amp;lt;/math&amp;gt;&lt;br /&gt;
** критерий останова:&lt;br /&gt;
*** &amp;lt;math&amp;gt;T^i = F^{i-1} - F^{i} \leq 0 .&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычислительное ядро алгоритма PAM на этапе BUILD&#039;&#039;&#039; состоит из многочисленных вычислений значений целевой функции при различных вариантах выбора дополнительного медоида (шаг алгоритма &amp;lt;math&amp;gt;1 \leq i \leq k&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{i-1}} ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j ) ) = \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \overline{MIN}_{i, x_{o_h}}^{(j)},&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где:&lt;br /&gt;
* &amp;lt;math&amp;gt;\overline{MIN}_{1, x_{o_h} \in O_0} = \{ \rho (x_{o_h}, x_j ) \| 1 \leq j \leq n \};&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\overline{MIN}_{i, x_{o_h} \in O_{i-1}} = \{ \min ( \overline{MIN}_{i-1, x_{m_{i-1}}}^{(j)}, \rho (x_{o_h}, x_j) ) \| 1 \leq j \leq n \}, \forall i: 1 &amp;lt; i \leq k.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С каждой итерацией количество вариантов выбора дополнительных медоидов уменьшается на единицу, таким образом, на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге целевую функцию нужно вычислить лишь &amp;lt;math&amp;gt;|O_{i-1}| = n-i+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;
* таким образом, на каждой последующей итерации можно использовать величину, вычисленную на предыдущем шаге, что позволяет сократить число вычислений минимумов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычислительное ядро алгоритма PAM на этапе SWAP&#039;&#039;&#039; состоит из многочисленных вычислений значений целевой функции при различных вариантах замены некоторого существующего медоида на элемент из числа не-медоидов. &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-я итерация фазы SWAP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{m_s} \in M_{i-1} , x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j ), \rho ( x_{o_h},x_j) ) =&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;= \min_{x_{m_s} \in M_{i-1} } \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min ( \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )) , \rho ( x_{o_h},x_j) )&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;x_{o_h}&amp;lt;/math&amp;gt;, для которых вычисляется &amp;lt;math&amp;gt;\sum_{j = 1}^{n} \min (a, b)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; есть &amp;lt;math&amp;gt; \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )),&amp;lt;/math&amp;gt; и является инвариантом по отношению к &amp;lt;math&amp;gt;\min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min ( \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )) , \rho ( x_{o_h},x_j) ),&amp;lt;/math&amp;gt; что позволяет предвычислить элемент &amp;lt;math&amp;gt;a.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Опишем макрооперации алгоритма&#039;&#039;:&lt;br /&gt;
* в случае если алгоритму PAM на вход подаётся набор точек с их координатами в пространстве &amp;lt;math&amp;gt;R^w,&amp;lt;/math&amp;gt; то необходимо вычислить матрицу расстояний. Для этого может быть использована, например, евклидова метрика:&lt;br /&gt;
*: &amp;lt;math&amp;gt;\rho (p,q)=\sqrt{(p_1-q_1)^2+(p_2-q_2)^2+\dots+(p_w-q_w)^2} = \sqrt{\sum_{j=1}^w (p_j-q_j)^2} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;p, q \in R^w;&amp;lt;/math&amp;gt;&lt;br /&gt;
* нахождение минимального среди значений, принимаемых некоторой функцией &amp;lt;math&amp;gt;value,&amp;lt;/math&amp;gt; определённой на дискретном множестве &amp;lt;math&amp;gt;Y=\{y_i\}, \, i = \overline{(1,r)}:&amp;lt;/math&amp;gt;&lt;br /&gt;
*: &amp;lt;math&amp;gt;minimum_{y \in Y } (value (y)) = \min \Bigg(value(y_1), \min \bigg(value(y_2), \min \Big(\dots , \min(value(y_{r-1}), value(y_{r})) ... \Big) \bigg)\Bigg);&amp;lt;/math&amp;gt;&lt;br /&gt;
* вычисление вектора инвариантов, используемых при оптимизации фазы SWAP (см. вычислительное ядро алгоритма):&lt;br /&gt;
*: &amp;lt;math&amp;gt;\overline{a} = \{ minimum_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l}, x_j ) ) \| 1 \leq j \leq n \},&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;i - &amp;lt;/math&amp;gt;номер итерации;&lt;br /&gt;
* вычисление вектора расстояний от каждой точки заданного множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; до ближайшего медоида из множества &amp;lt;math&amp;gt;M_{i-1} \backslash \{x_{m_s}\},&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;i - &amp;lt;/math&amp;gt;номер итерации фазы SWAP: &lt;br /&gt;
*: &amp;lt;math&amp;gt;\overline{b} = \{ \min (\overline{a}^{(j)}, \rho ( x_{o_p}, x_j )) \| 1 \leq j \leq n \};&amp;lt;/math&amp;gt;&lt;br /&gt;
* сложение компонент вектора: &amp;lt;math&amp;gt;S = \sum_{j = 1}^{n} \overline{f}^{(j)}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Макроструктура вычислительного ядра алгоритма PAM&#039;&#039; может быть выражена через макрооперации следующим образом:&lt;br /&gt;
* для ядра фазы BUILD:&lt;br /&gt;
*::: &amp;lt;math&amp;gt;F^i = minimum_{ x_{o_h} \in O_{i-1} } ( S \, ( \overline{f} (x_{o_h}) )),&amp;lt;/math&amp;gt;&lt;br /&gt;
*::: &amp;lt;math&amp;gt;\overline{f} = \{ \min \,( \overline{MIN}_{i, x_{o_h}}^{(j)}, \rho (x_{o_h}, x_j)) \| 1 \leq j \leq n \}, &amp;lt;/math&amp;gt; где&lt;br /&gt;
*# &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;n-i+1&amp;lt;/math&amp;gt; раз;&lt;br /&gt;
*# &amp;lt;math&amp;gt;minimum&amp;lt;/math&amp;gt; находится &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз при вычислении наилучшего значения целевой функции на текущей итерации.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* для ядра фазы SWAP:&lt;br /&gt;
*::: &amp;lt;math&amp;gt;F^i = minimum_{ x_{m_s} \in M_{i-1} , x_{o_h} \in O_{i-1} } ( S \, ( \overline{b} \, ( x_{o_h}, \overline{a} \, (x_{m_s}) ) )),&amp;lt;/math&amp;gt; где:&lt;br /&gt;
*# &amp;lt;math&amp;gt;minimum&amp;lt;/math&amp;gt; находится &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз  в процессе вычисления макрооперации &amp;lt;math&amp;gt;\overline {a};&amp;lt;/math&amp;gt;&lt;br /&gt;
*# &amp;lt;math&amp;gt;\overline {a}&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз и использует вычисленные на шаге 1 минимальные значения;&lt;br /&gt;
*# &amp;lt;math&amp;gt;\overline {b}&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; раз и использует вычисленные на шаге 2 значения &amp;lt;math&amp;gt;\overline {a};&amp;lt;/math&amp;gt;&lt;br /&gt;
*# &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; вычисляется &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; раз и использует вычисленные на шаге 3 значения &amp;lt;math&amp;gt;\overline {b};&amp;lt;/math&amp;gt;&lt;br /&gt;
*# &amp;lt;math&amp;gt;minimum&amp;lt;/math&amp;gt; находится &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
 &#039;&#039;&#039;Вход&#039;&#039;&#039;  : Множество объектов &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; или матрица расстояний &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
 &#039;&#039;&#039;Выход&#039;&#039;&#039; : Множество медоидов &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;&lt;br /&gt;
 0  Если на вход было подано множество объектов, необходимо предварительно вычислить матрицу расстояний;&lt;br /&gt;
 1  Инициализация множеств &amp;lt;math&amp;gt;M := \varnothing, O := X;&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;amp;nbsp;&lt;br /&gt;
    // фаза BUILD&lt;br /&gt;
 2  &#039;&#039;&#039;for (int i := 1; i &amp;lt;= k; i++)&#039;&#039;&#039; {&lt;br /&gt;
        // Найти &amp;lt;math&amp;gt;x_{m_i}:&amp;lt;/math&amp;gt;&lt;br /&gt;
        {&lt;br /&gt;
 3          Для всех &amp;lt;math&amp;gt;x_{o_h} \in O&amp;lt;/math&amp;gt; вычислить &amp;lt;math&amp;gt;F_{M \cup \{x_{o_h}\}, O \backslash \{x_{o_h}\}};&amp;lt;/math&amp;gt;&lt;br /&gt;
 4          Положить &amp;lt;math&amp;gt;x_{m_i}&amp;lt;/math&amp;gt; равным &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt;, соответствующему минимальному из вычисленных &amp;lt;math&amp;gt;F;&amp;lt;/math&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
 5      Обновить множества &amp;lt;math&amp;gt;M := M \cup \{x_{m_i}\}, O := O \backslash \{x_{m_i}\};&amp;lt;/math&amp;gt;&lt;br /&gt;
    }&lt;br /&gt;
    // фаза SWAP&lt;br /&gt;
 6  &#039;&#039;&#039;while (1)&#039;&#039;&#039; {&lt;br /&gt;
        // Найти пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h}),&amp;lt;/math&amp;gt; на которой достигается оптимальная замена:&lt;br /&gt;
        {&lt;br /&gt;
 7          Для всех &amp;lt;math&amp;gt;x_{m_i} \in M, x_{o_j} \in O&amp;lt;/math&amp;gt; вычислить &amp;lt;math&amp;gt;F^i;&amp;lt;/math&amp;gt; // вычисление проводить с учётом приведённой ранее оптимизации (см. 1.3 [[#Вычислительное ядро алгоритма|Вычислительное ядро алгоритма]]);&lt;br /&gt;
 8          Положить пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; равной паре &amp;lt;math&amp;gt;(x_{m_i}, x_{o_j})&amp;lt;/math&amp;gt;, соответствующей минимальному из вычисленных &amp;lt;math&amp;gt;F;&amp;lt;/math&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
 9      Вычислить &amp;lt;math&amp;gt;T^i;&amp;lt;/math&amp;gt; // критерий останова (см. 1.2 [[#Математическое описание алгоритма|Математическое описание алгоритма]])&lt;br /&gt;
 10     &#039;&#039;&#039;if&#039;&#039;&#039; &amp;lt;math&amp;gt;(T^i &amp;gt; 0)&amp;lt;/math&amp;gt; {&lt;br /&gt;
 11         Обновить множества &amp;lt;math&amp;gt;M := M \backslash \{x_{m_s}\} \cup \{x_{o_h}\}, O := O \cup \{x_{m_s}\} \backslash \{x_{o_h}\};&amp;lt;/math&amp;gt;&lt;br /&gt;
 12     } &#039;&#039;&#039;else&#039;&#039;&#039; {&lt;br /&gt;
 13         &#039;&#039;&#039;break&#039;&#039;&#039;;&lt;br /&gt;
        }&lt;br /&gt;
    };&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;
* &#039;&#039;&#039;Минимизация кеш-промахов&#039;&#039;&#039;. Матрица расстояний, к которой постоянно выполняется доступ на чтение для вычисления минимумов и их суммирования, должна храниться в памяти как матрица размера &amp;lt;math&amp;gt;n*n&amp;lt;/math&amp;gt;, несмотря на то, что она симметрическая и достаточно хранить лишь половину (элементы над или под главной диагональю). Данная оптимизация потребует использования в 2 раза большего количества памяти для хранения расстояний (а это является главным расходом памяти программы, так как все остальные используемые данные являются временными (локальными для каждой операции) и представляют из себя несколько векторов длинной &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов). Однако так как в данном алгоритме доступ к матрице осуществляется построчно (в  [[#Вычислительное ядро алгоритма|вычислительном ядре алгоритма]] поэлементно ищутся  минимумы 2-х векторов, после чего все они складываются), то в этом случае из матрицы можно будет читать данные по строкам подряд, что снижает количество кеш-промахов. Использование только половины матрицы приводит к частым промахам в кеше в связи с необходимостью загрузки части векторов по строкам, а части - по столбцам.&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;\frac{n*(n-1)}{2}&amp;lt;/math&amp;gt; операций вычисления расстояний;&lt;br /&gt;
* сложность каждой операции вычисления расстояния (для случая использования Евклидовой метрики) равна &amp;lt;math&amp;gt;2*w-1&amp;lt;/math&amp;gt; операций сложения и вычитания, &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; операций возведения в квадрат (умножения) и &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; операции извлечения квадратного корня.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На основании формулы из описания &#039;&#039;&#039;вычислительного ядра фазы BUILD&#039;&#039;&#039; определим сложность выполнения всего этапа.&lt;br /&gt;
&lt;br /&gt;
Обозначим:&lt;br /&gt;
*&amp;lt;math&amp;gt;summs&amp;lt;/math&amp;gt; - операция сложения двух вещественных чисел;&lt;br /&gt;
*&amp;lt;math&amp;gt;mins&amp;lt;/math&amp;gt; - операция нахождения минимума для двух вещественных чисел.&lt;br /&gt;
&lt;br /&gt;
Тогда, количество операций равно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = ( ( (n-1) summs ) * (n-1+1) + (n-1+1-1)mins) + \sum_{i = 2}^{k} ( ( (1 mins)*(n-1) + (n-1) summs ) * (n-i+1) + (n-i+1-1)mins) \Rightarrow&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\Rightarrow S =&lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    S(mins) = ( ( 0 ) * (n-1+1) + (n-1+1-1)mins) + \sum_{i = 2}^{k} ( ( (1 mins)*(n-1)) * (n-i+1) + (n-i+1-1)mins ) &amp;amp; \quad\\&lt;br /&gt;
    S(summs) = \sum_{i = 1}^{k} ( ( (n-1) summs ) * (n-i+1) ) &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
=&lt;br /&gt;
&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;&lt;br /&gt;
=&lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    S(mins) = n^2(k-1) - \frac{1}{2}(nk^2 - nk) + n - k &amp;amp; \quad\\&lt;br /&gt;
    S(summs) = n^2k - \frac{1}{2}(nk^2 + nk - k^2 + k ) &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, сложность фазы BUILD есть &amp;lt;math&amp;gt;O(kn^2)&amp;lt;/math&amp;gt; как по количеству найденных минимумов, так и по количеству операций сложения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На основании формулы из описания &#039;&#039;&#039;вычислительного ядра фазы SWAP&#039;&#039;&#039; определим сложность вычисления целевой функции &amp;lt;math&amp;gt;F^i&amp;lt;/math&amp;gt; с использованием оптимизации процесса нахождения минимума (см. раздел [[#Вычислительное ядро алгоритма|Вычислительное ядро алгоритма]]). Количество операций равно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = ( ( (1 mins)*(n-1) + (n-1) summs )*(n-k) + n*((k-1-1) mins) )*k \Rightarrow&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\Rightarrow S =&lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    S(mins) = ( ((1 mins)*(n-1) )*(n-k) + n*((k-1-1) mins) )*k = kn^2 - 3nk + k^2 &amp;amp; \quad\\&lt;br /&gt;
    S(summs) = ( ( (n-1) summs )*(n-k) )*k = (n-1)(n-k)k &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, сложность одной итерации фазы SWAP есть &amp;lt;math&amp;gt;O(kn^2)&amp;lt;/math&amp;gt; как по количеству найденных минимумов, так и по количеству операция сложения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Полученные оценки позволяют говорить о том, что PAM относится к алгоритмам &#039;&#039;&#039;с квадратичной сложностью&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Основная вычислительная сложность алгоритма PAM приходится на фазу SWAP, т.к. сложность одной её итерации сопоставима со сложностью всей фазы BUILD, однако SWAP может содержать в себе огромное число итераций.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
==== Информационный граф фазы BUILD ====&lt;br /&gt;
 &lt;br /&gt;
Опишем граф фазы BUILD как аналитически, так и в виде рисунка.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Граф состоит из шести групп вершин&#039;&#039;:&lt;br /&gt;
 &lt;br /&gt;
# Операция M, соответствующая &#039;&#039;&#039;первой&#039;&#039;&#039; группе вершин, осуществляет изменение множества медоидов:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M_{It} = M_{It-1} \cup \overline{x}_{m_{It}}, &amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;\overline{x}_{m_{It}} = \overline{x}_{o_{It}}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; (номер итерации), соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{x}_{o_{It}}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из пятой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M_{It-1}&amp;lt;/math&amp;gt; - множество медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - входное данное алгоритма;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It \leq k &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из первой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; k &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = k &amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы BUILD.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция O, соответствующая &#039;&#039;&#039;второй&#039;&#039;&#039; группе вершин, осуществляет изменение множества не-медоидов:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;O_{It} = O_{It-1} \backslash \overline{x}_{o_{It}}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt;, соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{x}_{o_{It}}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из пятой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;O_{It-1}&amp;lt;/math&amp;gt; - множество не-медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - входное данное алгоритма;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It \leq k &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; k &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = k &amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы BUILD.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция, соответствующая &#039;&#039;&#039;третьей&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{c}=\overline{c}_{x_{o_p} } =  \begin{cases} \{ \rho (x_{o_p}, x_j ) \| 1 \leq j \leq n \}, It=1 &amp;amp; \quad\\ \{ \min ( \overline{MIN}^{(j)}, \rho (x_{o_p}, x_j) ) \| 1 \leq j \leq n \}, 1&amp;lt;It \leq k &amp;amp; \quad\\  \end{cases} .&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-It+1],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = 1:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:** &amp;lt;math&amp;gt;\overline{x}_{o_p}&amp;lt;/math&amp;gt; - входное данное алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;1 &amp;lt; It \leq k:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:**&amp;lt;math&amp;gt;\overline{x}_{o_p}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1;&amp;lt;/math&amp;gt;&lt;br /&gt;
#:**&amp;lt;math&amp;gt;\overline{MIN}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из шестой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция S, соответствующая &#039;&#039;&#039;четвертой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;S = \sum_{j = 1}^{n} \overline {c}^{(j)}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-It+1],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{c}_{ x_{o_p} }&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из третьей группы с координатами &amp;lt;math&amp;gt; It, p.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция M1, соответствующая &#039;&#039;&#039;пятой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M1 = (F^{It}; x_{o_{It}}),&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt; \begin{cases}&lt;br /&gt;
F^{It} = \min_{x_{o_p} \in O_{It-1}} S &amp;amp; \quad\\&lt;br /&gt;
x_{o_{It}} = \arg F^{It} &amp;amp; \quad\\&lt;br /&gt;
\end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; - результаты срабатывания операций, соответствующих вершинам из четвертой группы с первой координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;n-It+1&amp;lt;/math&amp;gt; величина.&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; k &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = k &amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы BUILD.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция MIN, соответствующая &#039;&#039;&#039;шестой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{MIN}=\overline{MIN}_{It,x_{m_{It}} } =  \begin{cases}&lt;br /&gt;
\{ \rho (x_{m_{It}            }, x_j ) \| 1 \leq j \leq n \}, It=1 &amp;amp; \quad\\&lt;br /&gt;
\{ \min ( \overline{MIN}^{(j)}, \rho (x_{m_{It}}, x_j) ) \| 1 \leq j \leq n \}, 1&amp;lt;It \leq k &amp;amp; \quad\\&lt;br /&gt;
 \end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k-1],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{MIN}_{It-1,x_{m_{It-1}}} &amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из шестой группы с координатой &amp;lt;math&amp;gt;It-1;&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;x_{m_{It}} =  x_{o_{It}} &amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из пятой группы с координатой &amp;lt;math&amp;gt;It.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный граф в общем случае представлен на Рис.1, отображен шаг с номером &amp;lt;math&amp;gt;It, 1&amp;lt;It&amp;lt;k&amp;lt;/math&amp;gt; фазы BUILD.&lt;br /&gt;
Обозначения:&lt;br /&gt;
# вершины первой группы обозначены фиолетовым цветом и подписаны как &amp;lt;math&amp;gt;M;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины  второй группы обозначены синим цветом и подписаны как &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины третьей группы обозначены жёлтым цветом и подписаны как &amp;lt;math&amp;gt;\overline{c}_{x_{o_p} };&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины четвертой группы обозначены серым цветом и подписаны как &amp;lt;math&amp;gt;S;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины пятой группы обозначены зеленым цветом и подписаны как &amp;lt;math&amp;gt;M1;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины шестой группы обозначены красным цветом и подписаны как &amp;lt;math&amp;gt;MIN;&amp;lt;/math&amp;gt;&lt;br /&gt;
# синим дугам сопоставлены метки, отражающие данные, используемые в соответствующих жёлтых вершинах.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:Pam build 1.png|thumb|center|800px|Рисунок 1. Граф фазы BUILD алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На Рис.2 представлен граф фазы BUILD для случая &amp;lt;math&amp;gt;n=6, k=3.&amp;lt;/math&amp;gt; Вершины, соответствующие операциям, получающим входные данные и производящим выходные данные, увеличены и выделены толстыми границами.&lt;br /&gt;
&lt;br /&gt;
[[file:Pam build 2.png|thumb|center|1100px|Рисунок 2. Граф фазы BUILD алгоритма для n = 6, k = 3. ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Информационный граф фазы SWAP ====&lt;br /&gt;
 &lt;br /&gt;
Опишем граф фазы SWAP как аналитически, так и в виде рисунка.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Граф состоит из семи групп вершин&#039;&#039;:&lt;br /&gt;
 &lt;br /&gt;
# Операция M, соответствующая &#039;&#039;&#039;первой&#039;&#039;&#039; группе вершин, осуществляет изменение множества медоидов.&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; (номер итерации), соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; T),&amp;lt;/math&amp;gt; принимая все целочисленные значения, где:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;T\in \N&amp;lt;/math&amp;gt; - количество выполнений фазы SWAP во время работы алгоритма PAM.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* результат срабатывания операции, соответствующей вершине из седьмой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M_{It-1}&amp;lt;/math&amp;gt; - множество медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It &amp;lt; T &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из первой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; T -1 &amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = T - 1&amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы SWAP.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция O, соответствующая &#039;&#039;&#039;второй&#039;&#039;&#039; группе вершин, осуществляет изменение множества не-медоидов.&lt;br /&gt;
#: Единственная координата &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt;, соответствующая каждой из вершин, меняется в диапазоне &amp;lt;math&amp;gt;[1; T),&amp;lt;/math&amp;gt; принимая все целочисленные значения, где:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;T\in \N&amp;lt;/math&amp;gt; - количество выполнений фазы SWAP во время работы алгоритма PAM.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* результат срабатывания операции, соответствующей вершине из седьмой группы с координатой &amp;lt;math&amp;gt;It,&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;O_{It-1}&amp;lt;/math&amp;gt; - множество не-медоидов:&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1 &amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;1&amp;lt; It &amp;lt; T &amp;lt;/math&amp;gt; -  результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция, соответствующая &#039;&#039;&#039;третьей&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{a}=\overline{a}_{x_{m_i} } = \{ \min_{ x_{m_l} \in M  \backslash \{x_{m_i}\} }  \rho ( x_{m_l},x_j ) \| 1 \leq j \leq n \}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументом операции является &amp;lt;math&amp;gt;\overline{x}_{m_i}:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = 1&amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из первой группы с координатой &amp;lt;math&amp;gt;It-1.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция, соответствующая &#039;&#039;&#039;четвертой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;\overline{b}=\overline{b}_{x_{m_i} x_{o_p} } =  \{ \min (\overline{a}_{ x_{m_i}}^{(j)}, \rho ( x_{o_p},x_j )) \| 1 \leq j \leq n \}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{x}_{o_p}:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1&amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из второй группы с координатой &amp;lt;math&amp;gt;It-1&amp;lt;/math&amp;gt;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{a}_{x_{m_i}}&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из третьей группы с координатами &amp;lt;math&amp;gt;It, i.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция S, соответствующая &#039;&#039;&#039;пятой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;S = \sum_{j = 1}^{n} \overline {b}^{(j)}.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; n-k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;\overline{b}_{x_{m_i} x_{o_p} }&amp;lt;/math&amp;gt; - результат срабатывания операции, соответствующей вершине из четвертой группы с координатами &amp;lt;math&amp;gt;It, i, p.&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция M1, соответствующая &#039;&#039;&#039;шестой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M1 = (F_{x_{o_p}}^{It}; x_{o_p}),&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt; \begin{cases}&lt;br /&gt;
F_{x_{o_p}}^{It} = \min_{x_{o_p} \in O} S &amp;amp; \quad\\&lt;br /&gt;
x_{o_p} = \arg F_{x_{o_p}}^{It} &amp;amp; \quad\\&lt;br /&gt;
\end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; k],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; - результаты срабатывания операций, соответствующих вершинам из пятой группы с первыми двумя координатами &amp;lt;math&amp;gt;It, i,&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;n-k&amp;lt;/math&amp;gt; величин.&lt;br /&gt;
#: Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
#:&lt;br /&gt;
#: &amp;lt;br&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
# Операция M2, соответствующая &#039;&#039;&#039;седьмой&#039;&#039;&#039; группе вершин, выглядит следующим образом:&lt;br /&gt;
#:: &amp;lt;math&amp;gt;M2 = (F_{x_{o_p}, x_{m_i}}^{It}; x_{m_i}; x_{o_p}),&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;\begin{cases}&lt;br /&gt;
F_{x_{o_p}, x_{m_i}}^{It} = \min (\min_{x_{m_i} \in M} (F_{x_{o_p}}^{It}), M2_{It-1}) &amp;amp; \quad\\&lt;br /&gt;
x_{m_i} = \arg \min_{x_{m_i} \in M} M1 &amp;amp; \quad\\&lt;br /&gt;
\end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Координаты вершин:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; - меняется в диапазоне &amp;lt;math&amp;gt;[1; T],&amp;lt;/math&amp;gt; принимая все целочисленные значения.&lt;br /&gt;
#: Аргументами операции являются:&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M1 = (F_{x_{o_p}}^{It}; x_{o_p})&amp;lt;/math&amp;gt; - результаты срабатывания операций, соответствующих вершинам из шестой группы с координатами &amp;lt;math&amp;gt;It, i,&amp;lt;/math&amp;gt; всего &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; величин;&lt;br /&gt;
#:* &amp;lt;math&amp;gt;M2_{It-1}:&amp;lt;/math&amp;gt;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It = 1&amp;lt;/math&amp;gt; - результат выполнения фазы BUILD;&lt;br /&gt;
#:** при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt; - результаты срабатывания операции, соответствующей вершине из седьмой группы с координатой &amp;lt;math&amp;gt;It-1;&amp;lt;/math&amp;gt;&lt;br /&gt;
#: Результат срабатывания операции является:&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It &amp;lt; T&amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
#:* при &amp;lt;math&amp;gt;It = T&amp;lt;/math&amp;gt; - &#039;&#039;выходным данным&#039;&#039; фазы SWAP.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный граф в общем случае представлен на Рис.3, отображена одна итерация фазы SWAP.&lt;br /&gt;
Обозначения:&lt;br /&gt;
# вершины первой группы обозначены фиолетовым цветом и подписаны как &amp;lt;math&amp;gt;M;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины второй группы обозначены синим цветом и подписаны как &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины третьей группы обозначены желтым цветом и подписаны как &amp;lt;math&amp;gt;\overline{a}_{x_{m_i} };&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины четвертой группы обозначены оранжевым цветом и подписаны как &amp;lt;math&amp;gt;\overline{b}_{x_{m_i o_p} };&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины пятой группы обозначены серым цветом и подписаны как &amp;lt;math&amp;gt;S;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины шестой группы обозначены зеленым цветом и подписаны как &amp;lt;math&amp;gt;M1;&amp;lt;/math&amp;gt;&lt;br /&gt;
# вершины седьмой группы обозначены красным цветом и подписаны как &amp;lt;math&amp;gt;M2;&amp;lt;/math&amp;gt;&lt;br /&gt;
# синим дугам сопоставлены метки, отражающие данные, используемые в соответствующих оранжевых вершинах;&lt;br /&gt;
# фиолетовым дугам сопоставлены метки, отражающие данные, используемые в соответствующих жёлтых вершинах.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:Pam swap 1.png|thumb|center|1100px|Рисунок 3. Граф фазы SWAP алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На Рис.4 представлен граф фазы SWAP для случая &amp;lt;math&amp;gt;n=6, k=2, T=3.&amp;lt;/math&amp;gt; Вершины, соответствующие операциям, получающим входные данные и производящим выходные данные, увеличены и выделены толстыми границами.&lt;br /&gt;
&lt;br /&gt;
[[file:Pam swap 2.png|thumb|center|1100px|Рисунок 4. Граф фазы SWAP алгоритма для n = 6, k = 2, T = 3.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При параллельной реализации алгоритма PAM будем рассматривать параллельные варианты фазы BUILD и SWAP ([[#Возможная параллельная реализация алгоритма PAM|возможная параллельная реализация]]). Покажем, что сложность фазы BUILD сопоставима со сложностью одной итерации фазы SWAP.&lt;br /&gt;
&lt;br /&gt;
Оценки последовательной сложности фаз BUILD и SWAP приведены в параграфе &amp;quot;[[#Последовательная сложность алгоритма|Последовательная сложность алгоритма]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Ресурс параллелизма фазы BUILD ====&lt;br /&gt;
&lt;br /&gt;
Для решения задачи кластеризации методом PAM в параллельном варианте фазы BUILD в предположении доступности неограниченного числа процессов для одной итерации необходимо выполнить:&lt;br /&gt;
*  1 ярус с нахождением &amp;lt;math&amp;gt;\overline{c}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It &amp;gt; 1&amp;lt;/math&amp;gt;;&lt;br /&gt;
* 1 ярус с нахождением суммы &amp;lt;math&amp;gt;S:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; операцию сложения;&lt;br /&gt;
* 1 ярус с нахождением значений элемента &amp;lt;math&amp;gt;M1:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n-It&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением минимального элемента &amp;lt;math&amp;gt;\overline{MIN}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It&amp;gt;1;&amp;lt;/math&amp;gt;&lt;br /&gt;
* 1 ярус с изменением множеств медоидов и не-медоидов (не выполняются операции рассматриваемых типов).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таким образом, параллельная сложность одного шага фазы BUILD равна: &lt;br /&gt;
* &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; операция сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;n-It&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It=1;&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;3n-It&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов при &amp;lt;math&amp;gt;It&amp;gt;1.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность фазы BUILD при числе кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; равна: &lt;br /&gt;
* &amp;lt;math&amp;gt;kn-k&amp;lt;/math&amp;gt; операция сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;3kn - 2n - \frac{1}{2}k^2 - \frac{1}{2}k&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В параллельном варианте, по сравнению с последовательным, при &amp;lt;math&amp;gt;k, n \to \infty&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;O(kn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(kn^2).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Соотношение количества сложений и взятий минимума для фазы BUILD является постоянным и стремится к &amp;lt;math&amp;gt;3,&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;k, n \to \infty.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Максимальная ширина яруса достигается при вычислении &amp;lt;math&amp;gt;\overline {c}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; и равна &amp;lt;math&amp;gt;n-It+1&amp;lt;/math&amp;gt; для шага &amp;lt;math&amp;gt;It&amp;lt;/math&amp;gt; фазы BUILD. Максимальная ширина яруса при рассмотрении всей фазы есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; и достигается на первом шаге.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Ресурс параллелизма фазы SWAP ====&lt;br /&gt;
&lt;br /&gt;
Для решения задачи кластеризации методом PAM в параллельном варианте фазы SWAP в предположении доступности неограниченного числа процессов для одной итерации необходимо выполнить:&lt;br /&gt;
* 1 ярус с нахождением &amp;lt;math&amp;gt;\overline {a}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n(k-1)&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением &amp;lt;math&amp;gt;\overline {b}:&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; операций нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением суммы &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;:&lt;br /&gt;
** &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; операцию сложения;&lt;br /&gt;
* 1 ярус с нахождением минимального элемента &#039;первого&#039; вида &amp;lt;math&amp;gt;(M1 = \min_{x_{o_p} \in O} S):&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;n-k-1&amp;lt;/math&amp;gt; операцию нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с нахождением минимального элемента &#039;второго&#039; вида &amp;lt;math&amp;gt;(M2 = \min_{x_{m_i} \in M} M1):&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; операцию нахождения минимального из двух элементов;&lt;br /&gt;
* 1 ярус с изменением множеств медоидов и не-медоидов (не выполняются операции рассматриваемых типов).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таким образом, параллельная сложность одной итерации фазы SWAP равна:&lt;br /&gt;
* &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; - по количеству операций сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;kn + n - 2&amp;lt;/math&amp;gt; - по количеству операций нахождений минимума для двух чисел.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В параллельном варианте, по сравнению с последовательным, при &amp;lt;math&amp;gt;k, n \to \infty&amp;lt;/math&amp;gt; количество операций на один порядок меньше по параметру &amp;lt;math&amp;gt;n:&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(kn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(kn^2).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Соотношение количества сложений и взятий минимума для одной итерации фазы SWAP является постоянным и равным &amp;lt;math&amp;gt;k,&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;n \to \infty&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Максимальная ширина яруса достигается при вычислении &amp;lt;math&amp;gt;\overline {b}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; и равна &amp;lt;math&amp;gt;k(n-k).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При использовании выбранной модели, в предположении доступности неограниченного числа вычислителей, фаза BUILD при &amp;lt;math&amp;gt;n \to \infty&amp;lt;/math&amp;gt; требует большего количества операций суммирования на порядок &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, по сравнению с одной итерацией фазы SWAP. В свою очередь, количество операций подсчёта минимума ассимптотически эквивалентны.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
* число &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек упорядоченного множества &amp;lt;math&amp;gt;X;&amp;lt;/math&amp;gt;&lt;br /&gt;
* число &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - количество требуемых кластеров;&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, состоящих из &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; вещественных чисел, - попарных расстояний между элементами из множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; за исключением расстояний вида &amp;lt;math&amp;gt;\rho (x, x)=0, x \in X;&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;Объём входных данных&#039;&#039;: &amp;lt;math&amp;gt;n(n-1)&amp;lt;/math&amp;gt; вещественное число и &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; целых числа.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Структура хранения данных&#039;&#039;:&lt;br /&gt;
* сразу после считывания входных данных составляется квадратная симметрическая матрица &amp;lt;math&amp;gt;D^{n * n}&amp;lt;/math&amp;gt;, у которой каждый элемент &amp;lt;math&amp;gt;d_{ij}&amp;lt;/math&amp;gt; является вещественным числом - расстоянием &amp;lt;math&amp;gt;\rho (x_i, x_j);&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; целых чисел - индексов принадлежащих ему элементов из множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;, аналогичный массив &amp;lt;math&amp;gt;n-k&amp;lt;/math&amp;gt; целых чисел создаётся для множества не-медоидов &amp;lt;math&amp;gt;O;&amp;lt;/math&amp;gt;&lt;br /&gt;
* для пересылки данных, как правило, используется тройка &amp;lt;math&amp;gt;(F_{x_{m_s}, x_{o_h}}^{i_j}; x_{m_s}; x_{o_h})&amp;lt;/math&amp;gt; или пара &amp;lt;math&amp;gt;(F_{x_{o_h}}^{i_j}; x_{o_h}),&amp;lt;/math&amp;gt; где:&lt;br /&gt;
** &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; - целое число, обозначающее порядковый номер элемента множества &amp;lt;math&amp;gt;X;&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt; - целое число, обозначающее порядковый номер элемента множества &amp;lt;math&amp;gt;X;&amp;lt;/math&amp;gt;&lt;br /&gt;
** &amp;lt;math&amp;gt;F_{x_{m_s}, x_{o_h}}^{i_j}&amp;lt;/math&amp;gt; - вещественное число, равное минимальному значению целевой функции, вычисленному в текущем контексте.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&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;1&amp;lt;/math&amp;gt; вещественное число, являющееся значением целевой функции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Объём выходных данных&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;  целых чисел и &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычислительная мощность&#039;&#039; последовательного алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, равна &amp;lt;math&amp;gt;O(T*k).&amp;lt;/math&amp;gt;&lt;br /&gt;
Вычислительная мощность параллельного алгоритма равна &amp;lt;math&amp;gt;O(T*\frac{k}{n}).&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм PAM является &#039;&#039;менее чувствительным&#039;&#039; к выбросам данных, чем алгоритм k-средних. Это связано с использованием медоидов, менее подверженных влиянию таких данных.&amp;lt;ref&amp;gt; Егоров А. В., Куприянова Н. И. Особенности методов кластеризации данных // Известия Южного федерального университета. Технические науки. –2011. T. 124, № 11. –С. 174-177.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Фиксированный способ выбора начальных приближений позволяет говорить о воспроизводимости полученных результатов для последовательной реализации. Для параллельной работы алгоритма подобная воспроизводимость отсутствует, так как из-за &#039;&#039;недетерминированности&#039;&#039; выбора элементов, на которых достигается минимальное значение целевой функции для каждой итерации, итоговое множество медоидов и, следовательно, значение целевой функции может меняться.&lt;br /&gt;
&lt;br /&gt;
Алгоритм PAM &#039;&#039;конечен&#039;&#039;, так как:&lt;br /&gt;
* количество операций фазы BUILD конечно;&lt;br /&gt;
* значение целевой фукнции неотрицательно и на каждой итерации фазы SWAP строго уменьшается.&lt;br /&gt;
&lt;br /&gt;
Одно из преимуществ алгоритма PAM заключается в том, что он может работать с любой матрицей различий. В данном случае принципиальное отличие от матрицы расстояний заключается в том, что для матрицы различий может не выполняться неравенство треугольника, так как в основе её построения не обязательно используется метрика.&lt;br /&gt;
&lt;br /&gt;
Основные операция алгоритма PAM: сложение и нахождение минимума. Операция взятия минимума не подвержена процессу накопления ошибки. На каждой итерации алгоритма сложение производится над значениями, взятыми непосредственно из исходных данных. Таким образом, алгоритм PAM не накапливает ошибок в процессе своей работы и является &#039;&#039;устойчивым&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;math&amp;gt;k(n-k) \, \bmod \, p \neq 0&amp;lt;/math&amp;gt; отсутствует возможность равномерно распределить задачи рассчёта &amp;lt;math&amp;gt;\overline{b}&amp;lt;/math&amp;gt; между процессами. Кроме того, если &amp;lt;math&amp;gt;k \, \bmod \, p \neq 0&amp;lt;/math&amp;gt;, то после их распределения для некоторых процессов окажется необходимым вычислить несколько соседних значений &amp;lt;math&amp;gt;\overline{a}.&amp;lt;/math&amp;gt; Таким образом, &#039;&#039;равномерная&#039;&#039; загрузка процессов &#039;&#039;не всегда возможна&#039;&#039;. Помимо этого, агрегирование данных с целью вычисления глобального минимума приводит к простою незадействованных процессов.&lt;br /&gt;
&lt;br /&gt;
Как и большинство алгоритмов кластеризации, PAM может сходиться к некоторому &#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;
==== Возможная параллельная реализация алгоритма PAM ====&lt;br /&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;M, O&amp;lt;/math&amp;gt; - упорядоченны.&lt;br /&gt;
&lt;br /&gt;
Приведённые далее возможные схемы для параллельных реализаций фазы BUILD и фазы SWAP основаны на максимально равномерном распределении задач между процессами.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Возможная параллельная реализация фазы BUILD =====&lt;br /&gt;
&lt;br /&gt;
Главная задача заключается в распараллеливании многочисленных вычислений целевой функции на каждой итерации фазы BUILD:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{o_h} \in O_{i-1}} F_{x_{o_h}}^i,&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;F_{x_{o_h}}^i = \sum_{j = 1}^{n} \overline{MIN}_{i, x_{o_h}}^{(j)},&amp;lt;/math&amp;gt; где&lt;br /&gt;
: &amp;lt;math&amp;gt;\overline{MIN}_{1, x_{o_h} \in O_0} = \{ \rho (x_{o_h}, x_j ) \| 1 \leq j \leq n \};&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;\overline{MIN}_{i, x_{o_h} \in O_{i-1}} = \{ \min ( \overline{MIN}_{i-1, x_{m_{i-1}}}^{(j)}, \rho (x_{o_h}, x_j) ) \| 1 \leq j \leq n \}, \forall i: 1 &amp;lt; i \leq k.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Схема реализации&#039;&#039;: &amp;lt;br&amp;gt;(далее &amp;lt;math&amp;gt;0 &amp;lt; i \leq k &amp;lt;/math&amp;gt; - номер текущей итерации)&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## положим:&lt;br /&gt;
##: &amp;lt;math&amp;gt;pos = p-(n-i+1) \, \bmod \, p;&amp;lt;/math&amp;gt;&lt;br /&gt;
## на каждой итерации процессу необходимо вычислить &amp;lt;math&amp;gt;r_j&amp;lt;/math&amp;gt; значений целевой функции для соответствующих ему элементов &amp;lt;math&amp;gt;x_{o_h}, ..., x_{o_{h+(r_j-1)}}:&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt; r_j = \begin{cases}&lt;br /&gt;
    \lfloor \frac{n-i+1}{p} \rfloor , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
    \lceil \frac{n-i+1}{p} \rceil , pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt;h = \begin{cases}&lt;br /&gt;
    r_j*j , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
    (r_j-1)*pos + (j-pos)*r_j , pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&amp;lt;/math&amp;gt; - порядковый номер &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# каждому процессу с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## найти минимум среди значений целевой функции &amp;lt;math&amp;gt;F_{x_{o_h}}^i&amp;lt;/math&amp;gt; для каждого из значений параметра &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt; &lt;br /&gt;
##* в данном случае может быть использована оптимизация, описанная в [[#Вычислительное ядро алгоритма|вычислительном ядре алгоритма]];&lt;br /&gt;
## для процессов с номером &amp;lt;math&amp;gt;j \neq 0&amp;lt;/math&amp;gt; переслать процессу с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; пару &amp;lt;math&amp;gt;(x_{o_h}, F_{x_{o_h}}^i)&amp;lt;/math&amp;gt; для всех обработанных &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# для процесса с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;:&lt;br /&gt;
## принять пары от процессов &amp;lt;math&amp;gt;(0, p-1],&amp;lt;/math&amp;gt; в ответ на шаг 2.2;&lt;br /&gt;
## найти &amp;lt;math&amp;gt;x_{o_h},&amp;lt;/math&amp;gt; для которого целевая функция принимает минимальное значение, и положить &amp;lt;math&amp;gt;x_{m_i}:=x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
## вычислить &amp;lt;math&amp;gt;\overline{MIN}_{i, x_{m_i}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
## разослать пару &amp;lt;math&amp;gt;(x_{m_i}, \overline{MIN}_{i, x_{m_i}})&amp;lt;/math&amp;gt; при помощи широковещательной рассылки;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## принять пару &amp;lt;math&amp;gt;(x_{m_i}, \overline{MIN}_{i, x_{m_i}})&amp;lt;/math&amp;gt; в ответ на шаг 3.4;&lt;br /&gt;
## скорректировать локальные копии множеств &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt;, изменив принадлежность вектора &amp;lt;math&amp;gt;x_{m_i}&amp;lt;/math&amp;gt;, а также обновить хранимое значение вектора &amp;lt;math&amp;gt;\overline{MIN}&amp;lt;/math&amp;gt; для подготовки к следующей итерации;&lt;br /&gt;
## перейти к шагу 2.1.&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
После завершения &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; шагов фазы BUILD, множества &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt; (одинаковые для каждого процесса согласно приведенной схеме), а так же значение целевой функции являются выходными данными фазы BUILD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Возможная параллельная реализация фазы SWAP =====&lt;br /&gt;
&lt;br /&gt;
Главная задача заключается в распараллеливании многократного вычисления целевой функции на каждой итерации фазы SWAP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F^i = \min_{x_{m_s} \in M_{i-1} } \min_{x_{o_h} \in O_{i-1}} \sum_{j = 1}^{n} \min ( \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )) , \rho ( x_{o_h},x_j) )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Схема реализации&#039;&#039;: &amp;lt;br&amp;gt;(далее &amp;lt;math&amp;gt;i \in \N&amp;lt;/math&amp;gt; - номер текущей итерации)&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## положим:&lt;br /&gt;
##: &amp;lt;math&amp;gt;pos = p-(k*(n-k)) \, \bmod \, p;&amp;lt;/math&amp;gt;&lt;br /&gt;
## на каждой итерации процессу необходимо вычислить &amp;lt;math&amp;gt;r_j&amp;lt;/math&amp;gt; значений целевой функции для соответствующих ему пар вида (медоид, не-медоид)   &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h}), (x_{m_s}, x_{o_{h+1}}), ..., (x_{m_s}, x_{o_{n-k}}), (x_{m_{s+1}}, x_{o_0}), (x_{m_{s+1}}, x_{o_1}), . . . \, : &amp;lt;/math&amp;gt;&lt;br /&gt;
##:&amp;lt;math&amp;gt; r_j =   \begin{cases}&lt;br /&gt;
    \lfloor \frac{k*(n-k)}{p} \rfloor , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
    \lceil \frac{k*(n-k)}{p} \rceil , pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt;s = \begin{cases}&lt;br /&gt;
    (r_j*j) \mbox{ div } (n-k) , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
     ((r_j-1)*pos + r_j*(j-pos)) \mbox{ div } (n-k), pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&amp;lt;/math&amp;gt; - порядковый номер &amp;lt;math&amp;gt;x_{m_s};&amp;lt;/math&amp;gt;&lt;br /&gt;
##: &amp;lt;math&amp;gt;h = \begin{cases}&lt;br /&gt;
    (r_j*j) \, \bmod \,  (n-k) , 0 \leq j &amp;lt; pos &amp;amp; \quad\\&lt;br /&gt;
     ((r_j-1)*pos + r_j*(j-pos)) \, \bmod \,  (n-k), pos  \leq j &amp;amp; \quad\\&lt;br /&gt;
  \end{cases}&amp;lt;/math&amp;gt; - порядковый номер &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# каждому процессу с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## найти минимум среди вычисленных значений целевой функции для каждого из значений первого параметра &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; по соответствующим ему &amp;lt;math&amp;gt;x_{o_h};&amp;lt;/math&amp;gt;&lt;br /&gt;
##* в данном случае может быть использована оптимизация, описанная в [[#Вычислительное ядро алгоритма|вычислительном ядре алгоритма]];&lt;br /&gt;
## переслать процессу с номером &amp;lt;math&amp;gt;N = s \, \bmod \, p&amp;lt;/math&amp;gt; (если &amp;lt;math&amp;gt;N \neq j&amp;lt;/math&amp;gt;) тройку &amp;lt;math&amp;gt;(F_{x_{m_s}, x_{o_h}}^{i_j}, x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; для всех обработанных &amp;lt;math&amp;gt;x_{m_s};&amp;lt;/math&amp;gt;&lt;br /&gt;
# для процессов с номерами &amp;lt;math&amp;gt;j&amp;lt;\min (p, k):&amp;lt;/math&amp;gt;&lt;br /&gt;
## принять описанные выше тройки от процессов, обработавших элементы &amp;lt;math&amp;gt;x_{m_{z \, \bmod \, p = j}}, z = \overline {0, k-1},&amp;lt;/math&amp;gt; в ответ на шаг 2.2;&lt;br /&gt;
## вычислить минимальное среди полученных значений целевой функции;&lt;br /&gt;
## запомнить &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt;, на которых минимальное значение было достигнуто;&lt;br /&gt;
## при &amp;lt;math&amp;gt;j \neq 0 &amp;lt;/math&amp;gt; отправить тройку &amp;lt;math&amp;gt;(F_{x_{m_s}, x_{o_h}}^{i_j}, x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt;, соответствующую выбранной паре, процессу с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для процесса с номером &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;:&lt;br /&gt;
## принять тройки от процессов &amp;lt;math&amp;gt;(0, min(k-1, p-1)],&amp;lt;/math&amp;gt; в ответ на шаг 3.4;&lt;br /&gt;
## найти пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt;, для которой целевая функция принимает минимальное значение;&lt;br /&gt;
## сравнить найденное минимальное значение с вычисленным значением целевой функции на предыдущей итерации фазы SWAP;&lt;br /&gt;
### (&#039;&#039; &#039;&#039;&#039;условие останова&#039;&#039;&#039; &#039;&#039;) если значение целевой функции не уменьшилось, остановить вычисление;&lt;br /&gt;
### иначе, разослать выявленную пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; при помощи широковещательной рассылки;&lt;br /&gt;
# для каждого процесса с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(0 \leq j &amp;lt; p):&amp;lt;/math&amp;gt;&lt;br /&gt;
## принять пару &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; в ответ на шаг 4.3.2;&lt;br /&gt;
## скорректировать локальные копии множеств &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt;, заменяя элемент &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt; на элемент &amp;lt;math&amp;gt;x_{o_h}&amp;lt;/math&amp;gt; и наоборот;&lt;br /&gt;
## перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Ограничения и недостатки =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Ограничения&#039;&#039;:&lt;br /&gt;
* предполагается, что матрица расстояний &amp;lt;math&amp;gt;D^{n * n}&amp;lt;/math&amp;gt; полностью помещается в памяти каждого процесса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Недостатки&#039;&#039;:&lt;br /&gt;
* при распределении обсчитываемых целевых функций, каждому из процессов ставится в соответствие некоторая совокупность пар &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt;, для которых нужно рассчитать значения целевой функции. В случае, если количество процессов не кратно количеству обрабатываемых кластеров, различные процессы получат пары у которых совпадают &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt;, что приведёт к совершению одинаковых вычислений на различных процессах: &amp;lt;math&amp;gt;   \min_{ x_{m_l} \in M_{i-1} \backslash \{x_{m_s}\} } ( \rho ( x_{m_l},x_j )), 1 \leq j \leq n;&amp;lt;/math&amp;gt;&lt;br /&gt;
* вычисление фазы SWAP успешно распараллеливается между &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; процессами, однако после этого происходит агрегация данных в 2 этапа относительно &amp;lt;math&amp;gt;(x_{m_s}, x_{o_h})&amp;lt;/math&amp;gt; для вычисления глобального минимума. На первом этапе данные собираются на первых &amp;lt;math&amp;gt;\min (p,k)&amp;lt;/math&amp;gt; процессах, где происходят локальные вычисления минимумов среди принятых данных (для каждого &amp;lt;math&amp;gt;x_{m_s}&amp;lt;/math&amp;gt;), после чего полученные минимумы и связанная с ними информация агрегируются на мастер-процессе, который вычисляет глобальный минимум и извещает о нём все остальные процессы. &amp;lt;br&amp;gt;Во время описанных вычислений все незадействованные процессы простаивают.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Дополнительные возможности&#039;&#039;:&lt;br /&gt;
* помимо описанной схемы, также есть возможность распараллелить операцию вычисления матрицы расстояний &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, однако это является отдельной алгоритмической задачей и не даёт существенного прироста производительности алгоритма.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Существующие параллельные реализации алгоритма PAM ====&lt;br /&gt;
&lt;br /&gt;
Примером другой параллельной реализации алгоритма PAM может служить реализация в пакете SPRINT для языка R (подробнее см. [[#Существующие реализации алгоритма|существующие реализации алгоритма]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Структура графа алгоритма (см. 1.7 [[#Информационный граф| Информационный граф алгоритма]]) такова, что позволяет надеяться на получение хорошей масштабируемости.&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма Partitioning Around Medoids проводилось на суперкомпьютере BlueGene/P &amp;lt;ref&amp;gt;IBM BlueGene/P  - [http://hpc.cmc.msu.ru http://hpc.cmc.msu.ru]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
Отметим, что для данному суперкомпьютеру свойственна стабильность по времени вычисления одной и той же задачи при различных запусках.&lt;br /&gt;
Во время вычислений каждому MPI процессу соответствовало 1 ядро процессора PowerPC 450, 850 MHz с пиковой производительностью 3.4 GFlop/sec per core.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма:&lt;br /&gt;
* число процессов (p):&lt;br /&gt;
** [1; 4 : 512] с шагом 4 процесса;&lt;br /&gt;
* размер кластеризуемого множества (n):&lt;br /&gt;
** [4; 16; 32; 64];&lt;br /&gt;
** [128 : 3200] с шагом 128 элементов;&lt;br /&gt;
* каждому размеру кластеризуемого множества соответствует количество кластеров для разбиения (k):&lt;br /&gt;
** [2; 4; 4; 8];&lt;br /&gt;
** [2 : 50] с шагом 2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Особенности проведения эксперимента:&lt;br /&gt;
* тестовые данные являются псевдослучайными;&lt;br /&gt;
* для всех экспериментов использовался одинаковый набор данных;&lt;br /&gt;
* в соответствии с описанными параметрами запуска, для множеств размерности [128 : 3200], количество кластеров (k) прямо пропорционально размеру задачи (n).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен широкий диапазон [[Глоссарий#Эффективность распараллеливания|эффективности реализации]] алгоритма:&lt;br /&gt;
* минимальная эффективность реализации: &amp;lt;math&amp;gt;3.4 * 10^{-9} \, %&amp;lt;/math&amp;gt; достигается при использовании 512 процессов для множества из 4 элементов и 2-х кластеров (результат вызван избыточностью количества процессов по отношению к размеру задачи);&lt;br /&gt;
* максимальная эффективность реализации: &amp;lt;math&amp;gt;4.1 * 10^{-4} \, %&amp;lt;/math&amp;gt; достигается при использовании 4 процессов для множества из 128 элементов и 2 кластеров.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Абсолютная величина [[Глоссарий#Производительность|производительности алгоритма]] (GFlops) определялась как количество операций с плавающей точкой за секунду. Точное количество операций было вычислено на основании [[#Последовательная сложность алгоритма| последовательной сложности алгоритма]], целочисленные операции и операции работы с памятью не учитывались.&lt;br /&gt;
&lt;br /&gt;
Таким образом, производительность обусловлена как самим алгоритмом (его внутренняя структура определяет соотношение между операциями работы с памятью, операциями над целочисленными значениями и значениями с плавающей точкой), так и используемой реализацией.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики производительности и эффективности выбранной реализации PAM в зависимости от изменяемых параметров запуска:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|style=&amp;quot;margin: 0 auto;&amp;quot;&lt;br /&gt;
 | [[file:PAM-gflops.png|thumb|none|600px|Рисунок 5а. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества.]]&lt;br /&gt;
 | [[file:PAM-gflops-top-view.png|thumb|none|600px|Рисунок 5б. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества. Вид сверху.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
[[file:PAM-gflops.png|thumb|center|800px|Рисунок 5а. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества.]]&lt;br /&gt;
[[file:PAM-gflops-top-view.png|thumb|center|800px|Рисунок 5б. Параллельная реализация алгоритма PAM. Изменение производительности в зависимости от числа процессов и размера множества. Вид сверху.]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
[[file:PAM-efficiency.png|thumb|center|800px|Рисунок 6. Параллельная реализация алгоритма PAM. Изменение эффективности в зависимости от числа процессов и размера множества.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На рисунке 5б, видно, что пики организованы в прямые. Это обусловлено инвариантностью величины &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; в соответствии с описанной в [[#Вычислительное ядро алгоритма | вычислительном ядре фазы SWAP]] оптимизацией. На рисунке 3 для [[#Информационный граф фазы SWAP| информационного графа фазы SWAP]] наглядно видно, что при построчном распределении вычислений &amp;lt;math&amp;gt;\overline{b}_{x_{m_s, o_h}}&amp;lt;/math&amp;gt; возможно возникновение следующих ситуаций (далее при &#039;&#039; &#039;&#039;&#039;k * (n-k) mod p == 0&#039;&#039;&#039; &#039;&#039; обозначим &#039;&#039; &#039;&#039;&#039;r = k * (n-k) / p&#039;&#039;&#039; &#039;&#039;):&lt;br /&gt;
# при &#039;&#039; &#039;&#039;&#039;r &amp;lt; (n-k)&#039;&#039;&#039; &#039;&#039;, если &#039;&#039; &#039;&#039;&#039;(n-k) mod r == 0&#039;&#039;&#039; &#039;&#039;, то каждому процессу необходимо вычислить ровно одну величину &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}};&amp;lt;/math&amp;gt;&lt;br /&gt;
# при &#039;&#039; &#039;&#039;&#039;r &amp;amp;ge; (n-k)&#039;&#039;&#039; &#039;&#039;, если &#039;&#039; &#039;&#039;&#039;k mod p == 0&#039;&#039;&#039; &#039;&#039;, то каждому процессу необходимо вычислить несколько соответствующих ему величин &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}},&amp;lt;/math&amp;gt; при этом каждая величина будет вычислена ровно в одном процессе;&lt;br /&gt;
# в остальных случаях вычисления величин &amp;lt;math&amp;gt;\overline{b}_{x_{m_s, o_h}}&amp;lt;/math&amp;gt; распределяются между процессами таким образом, что часть процессов обрабатывает больше величин &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}}&amp;lt;/math&amp;gt; нежели остальные (ситуация возникает при обработке процессом последних элементов одной строки и первых элементов следующей). Подобная ситуация приводит к дополнительным вычислениям на некоторых процессах и, как следствие, к простою остальных, в связи с чем происходит падение производительности.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Первые два случая приводят к равномерному распределению нагрузки между процессами, и им соответствуют видимые пики на приведённых рисунках 5a, 5б, 6. Для 3-го случая из-за неравномерной загрузки процессов достигаемая производительность падает.&lt;br /&gt;
&lt;br /&gt;
Второй случай соответствует размеру задачи меньшему, чем количество процессов и на практике маловероятен.&lt;br /&gt;
Таким образом, для повышения производительности следует выбирать количество процессов так, чтобы &#039;&#039; &#039;&#039;&#039;k * (n-k)&#039;&#039;&#039; &#039;&#039; и &#039;&#039; &#039;&#039;&#039;(n-k)&#039;&#039;&#039; &#039;&#039; были кратны &#039;&#039; &#039;&#039;&#039;p&#039;&#039;&#039; &#039;&#039; и &#039;&#039; &#039;&#039;&#039;k * (n-k) div p&#039;&#039;&#039; &#039;&#039; соответственно.&lt;br /&gt;
&lt;br /&gt;
Для исследуемой реализации, вычисление величин &amp;lt;math&amp;gt;\overline{a}_{x_{m_s}}&amp;lt;/math&amp;gt; может быть оптимизировано (за счёт распределения между процессами и последующих пересылок). Однако принципиальный вид графиков останется тем же: пики будут наблюдаться для случаев равномерного распределения вычислительных задач между процессами.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Построим оценки масштабируемости выбранной реализации алгоритма PAM:&lt;br /&gt;
* По числу процессов: &amp;lt;math&amp;gt;- \, 1.6 * 10^{-8}.&amp;lt;/math&amp;gt; С ростом числа процессов эффективность на рассмотренной области изменений параметров запуска уменьшается. Этот результат вызван ростом времени, затрачиваемого на пересылку данных, то есть ростом накладных расходов на организацию параллельного выполнения.&lt;br /&gt;
* По размеру задачи: &amp;lt;math&amp;gt;3.9 * 10^{-7}&amp;lt;/math&amp;gt;. С ростом размера задачи эффективность на рассмотренной области изменений параметров запуска увеличивается. Этот результат вызван увеличением количества вычислений относительно объема пересылок данных.&lt;br /&gt;
* Общая оценка масштабируемости: &amp;lt;math&amp;gt;- \, 1.2 * 10^{-9}&amp;lt;/math&amp;gt;. При рассмотрении области параметров запуска в целом можно говорить о том, что с ростом размера задачи и числа процессов эффективность незначительно падает.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://github.com/phonexicum/pam-mpi-cmc Исследованная параллельная реализация на языке C++]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Последовательная реализация алгоритма PAM в пакете ClusterR на языке R:&lt;br /&gt;
#* Лицензия: MIT&lt;br /&gt;
#* [https://cran.r-project.org/package=ClusterR веб-страница фреймворка (исходный код, системные требования и справочное руководство)]&lt;br /&gt;
#* Алгоритм PAM реализован в функции &#039;Cluster_Medoids&#039;. Отличительная особенность реализации &amp;lt;ref&amp;gt;Lampros Mouselimis [https://cran.r-project.org/web/packages/cluster/cluster.pdf Package &#039;ClusterR&#039;]. October 8, 2016&amp;lt;/ref&amp;gt;:&lt;br /&gt;
#** для задания метрики между элементами кластеризуемого множества передаётся строка, задающая одну из реализованных в алгоритме метрик. Данный подход теряет одно из преимуществ алгоритма PAM, позволяющее в качестве матрицы расстояний использовать произвольную матрицу различий.&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# Последовательная реализация алгоритма PAM в пакете Cluster на языке R:&lt;br /&gt;
#* Лицензия: GPL (≥2)&lt;br /&gt;
#* [https://cran.r-project.org/package=cluster веб-страница фреймворка (исходный код, системные требования и справочное руководство)]&lt;br /&gt;
#* Алгоритм PAM реализован в функции &#039;pam()&#039;. Данная реализация имеет следующие особенности &amp;lt;ref&amp;gt;Martin Maechler., etc. [https://cran.r-project.org/web/packages/cluster/cluster.pdf Package &#039;cluster&#039;]. October 8, 2016&amp;lt;/ref&amp;gt;:&lt;br /&gt;
#** функция может принимать на вход не множество точек, а матрицу расстояний между ними;&lt;br /&gt;
#** реализация более гибкая, так как принимает на вход матрицу различий элементов, а не использует какую-либо метрику;&lt;br /&gt;
#** может быть предоставлено графическое отображение.&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# Параллельная реализация алгоритма PAM в пакете SPRINT (Simple Parallel R INTerface - параллельный фреймворк) на языке R:&lt;br /&gt;
#* Лицензия: GPL (≥3)&lt;br /&gt;
#* [https://cran.r-project.org/package=sprint веб-страница фреймворка (исходный код, системные требования и справочное руководство)]&lt;br /&gt;
#* Алгоритм PAM реализован в фукции &#039;ppam()&#039;. Входные параметры данной функции схожи с параметрами, задаваемыми в линейной реализации &#039;pam()&#039; пакета Cluster языка R. Отличительной особенностью является подача на вход предвычисленной матрицы расстояний (например, с помощью функции &#039;pcor()&#039; из этой же библиотеки).&amp;lt;ref&amp;gt;University of Edinburgh SPRINT Team. [https://cran.r-project.org/web/packages/sprint/sprint.pdf Package &#039;sprint&#039;]. February 20, 2015&amp;lt;/ref&amp;gt;&lt;br /&gt;
#:&lt;br /&gt;
#:&lt;br /&gt;
# Параллельная реализация алгоритма PAM на основе операций MapReduce на Hadoop кластере.&lt;br /&gt;
#* В рамках исследования по ускорению анализа информации о геноме были созданы реализации алгоритма PAM с использованием технологии MPI и технологии MapReduce. Реализация в открытом досупе отсутствует. &amp;lt;ref&amp;gt;Dobrzelecki B. et al. Managing and analysing Genomic Data using HPC and Clouds //Grid and Cloud Database Management. – Springer Berlin Heidelberg, 2011. – С. 261-277.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
MKL, ScaLAPACK, PETSc, FFTW, ATLAS, Magma и другие подобные библиотеки.&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;
гугл-запрос: &amp;quot;partitioning around medoids&amp;quot; &amp;quot;PAM&amp;quot; BUILD SWAP &amp;quot;MPI&amp;quot; &amp;quot;program&amp;quot; &amp;quot;package&amp;quot; -worldwidescience&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:Avasilenko&amp;diff=17546</id>
		<title>Обсуждение участника:Avasilenko</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:Avasilenko&amp;diff=17546"/>
		<updated>2016-11-16T08:28:35Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* не указано, какая реализация алгоритма использовалась - собственная или библиотечная&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 11:28, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2&amp;diff=17543</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2&amp;diff=17543"/>
		<updated>2016-11-16T08:17:37Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет текста программы&lt;br /&gt;
* нет указания на реализацию алгоритма (своя или сторонняя)&lt;br /&gt;
* нет пояснений к графикам&lt;br /&gt;
* либо заполните п. 2.4.1, либо уберите подпункты.&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 11:17, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2&amp;diff=17542</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2&amp;diff=17542"/>
		<updated>2016-11-16T08:16:14Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет текста программы&lt;br /&gt;
* нет указания на реализацию алгоритма (своя или сторонняя)&lt;br /&gt;
* нет пояснений к графикам&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 11:16, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=17541</id>
		<title>Участник:Илья Егоров/Алгоритм k-средних</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%98%D0%BB%D1%8C%D1%8F_%D0%95%D0%B3%D0%BE%D1%80%D0%BE%D0%B2/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=17541"/>
		<updated>2016-11-16T08:16:07Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k-средних (k-means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n*k*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n*d&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;
Страница создана группой &amp;quot;[[U:Илья Егоров|Илья Егоров]] — [[U:Богомазов Евгений|Евгений Богомазов]]&amp;quot;.&lt;br /&gt;
&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;
Алгоритм кластеризации k-средних впервые был предложен в 1950-х годах математиками Гуго Штейнгаузом и Стюартом Ллойдом независимо друг от друга. Наибольшую популярность он получил после работы Маккуина.&lt;br /&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;
В общем случае выбор числа &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; может базироваться на любых значимых факторах, в том числе на результатах предшествующих исследований, теоретических соображениях или интуиции. &lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Исходные данные:&#039;&#039;&#039;&lt;br /&gt;
* Совокупность &amp;lt;b&amp;gt;n&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;-мерных векторов &amp;lt;math&amp;gt; X = \{x_1 \dots x_n\} , &amp;lt;/math&amp;gt; где  &amp;lt;math&amp;gt; x_i = \{x_{i1} \dots x_{id}\} &amp;lt;/math&amp;gt;&lt;br /&gt;
* Предполагаемое количество кластеров &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
* Разбиение X на множество &amp;lt;math&amp;gt; S = \{S_1 \dots S_k \}, \bigcup S_i = X, S_i \cap S_j = \emptyset, i \neq j &amp;lt;/math&amp;gt;&lt;br /&gt;
* k центров кластеров &amp;lt;math&amp;gt; \Mu = \{\mu_1 \dots \mu_k \} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \mu_i = \{\mu_{i1} \dots \mu_{id} \} &amp;lt;/math&amp;gt; такие, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \begin{cases}\tilde{\mu_i} = \underset{y}{argmin } \sum_{x \in S_i} ||x-y||^2_E \\ \Mu = \underset{\tilde{\Mu}}{argmin } \sum_{i \in k} \sum_{x \in S_i} ||x-\tilde{\mu_i}||^2_E \end{cases} &amp;lt;/math&amp;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; \mu_{i} = random &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) &amp;lt;math&amp;gt; S_i = \{x \in S~|~\underset{j}{argmin } ||x-\mu_j||_E = i\} i = 1 \dots k &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3) &amp;lt;math&amp;gt; \tilde{\mu_{i}} = E_{x \in S_i}(x) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4) Если для &amp;lt;math&amp;gt; \forall i:  \mu_i = \tilde{\mu_i} &amp;lt;/math&amp;gt; то алгоритм завершен, иначе &amp;lt;math&amp;gt; \mu_i = \tilde{\mu_i} &amp;lt;/math&amp;gt; и перейти на пункт 2)&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительным ядром алгоритма является второй этап, а точнее нахождение матрицы расстояний между &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Mu&amp;lt;/math&amp;gt;. Для &#039;&#039;&#039;d&#039;&#039;&#039;-мерного вектора &amp;lt;math&amp;gt;a:&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;||a||=\sqrt{\sum_{i=1\dots d} a_i^2}&amp;lt;/math&amp;gt;, поэтому заполнение одной ячейки такой матрицы потребует &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения, &amp;lt;math&amp;gt;d-1&amp;lt;/math&amp;gt; операций сложения и одну операция вычисления квадратного корня. Но так как эти расстояния используются только для сравнения, а &#039;&#039;sqrt&#039;&#039; является монотонно возрастающей функцией, то ее можно не вычислять. Поэтому нахождения матрицы расстояний потребует всего &amp;lt;math&amp;gt;n*k*d&amp;lt;/math&amp;gt; операций умножений и &amp;lt;math&amp;gt;n*k*(d-1)&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;
*&#039;&#039;&#039;Суммирование векторов&#039;&#039;&#039;,&lt;br /&gt;
*&#039;&#039;&#039;Поиск минимума в векторе&#039;&#039;&#039;,&lt;br /&gt;
*&#039;&#039;&#039;Сравнение векторов&#039;&#039;&#039;,&lt;br /&gt;
*&#039;&#039;&#039;Деление вектора на скаляр&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1)&#039;&#039;&#039; На шаге инициализации центроидов в общем случае (случайный выбор элементов) макроопераций не производится. Но так как правило определения количества кластеров &#039;&#039;&#039;k&#039;&#039;&#039; неоднозначно, то для некоторых вариантов макрооперации могут понадобиться. К примеру, существует класс стратегий поиска &#039;&#039;&#039;k&#039;&#039;&#039;, которые базируются на определении центра масс всех объектов, и для которых будут дополнительно выполняться макрооперации &#039;&#039;&#039;&amp;quot;Суммирование векторов&amp;quot;&#039;&#039;&#039; и &#039;&#039;&#039;&amp;quot;Деление вектора на скаляр&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2a)&#039;&#039;&#039; При составлении матрицы расстояний &#039;&#039;&#039; k &amp;lt;math&amp;gt; \cdot &amp;lt;/math&amp;gt;  n&#039;&#039;&#039; раз производится макрооперация &#039;&#039;&#039;&amp;quot;Вычисление расстояния между векторами&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2b)&#039;&#039;&#039; При нахождение вектора распределения объектов по кластерам &#039;&#039;&#039;n&#039;&#039;&#039; раз производится макрооперация &#039;&#039;&#039;&amp;quot;Поиск минимума в векторе&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3)&#039;&#039;&#039; При пересчете центроидов &#039;&#039;&#039;n - k&#039;&#039;&#039; раз производится макрооперация &#039;&#039;&#039;&amp;quot;Суммирование векторов&amp;quot;&#039;&#039;&#039; и &#039;&#039;&#039;k&#039;&#039;&#039; раз - &#039;&#039;&#039;&amp;quot;Деление вектора на скаляр&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4)&#039;&#039;&#039; Для проверки критерия останова требуется &#039;&#039;&#039;k&#039;&#039;&#039; раз произвести макрооперацию &#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;
    &#039;&#039;&#039;1) Инициализация центроидов &amp;lt;math&amp;gt;\Mu&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; iter = 1 &amp;lt;/math&amp;gt;, задание максимального количество итераций &amp;lt;math&amp;gt;maxiter&amp;lt;/math&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    &#039;&#039;&#039;2a) Нахождение матрицы расстояний &#039;&#039;&#039;&amp;lt;math&amp;gt;dist:&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
        &amp;lt;math&amp;gt;dist_{ij} = \sum_{l = 1\dots d} (x_{il}-\mu_{jl})^2&amp;lt;/math&amp;gt;&lt;br /&gt;
        &lt;br /&gt;
    &#039;&#039;&#039;2b) Нахождение вектора распределения объектов по кластерам &#039;&#039;&#039;&amp;lt;math&amp;gt;index: &amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
        &amp;lt;math&amp;gt;index_{i} =   \underset{j}{argmin }~dist_{ij} &amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;3) Пересчет центроидов &#039;&#039;&#039;&amp;lt;math&amp;gt;\tilde{\Mu}:&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
        &amp;lt;math&amp;gt;\tilde{\mu_{ij}} = \sum_{l \in S_i} \dfrac{x_{lj}}{|S_i|} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S_i = \{l~|~l \in 1\dots n, index_l = i\}  &amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;4) Проверка критерия останова:&#039;&#039;&#039;&lt;br /&gt;
    &lt;br /&gt;
        &#039;&#039;&#039;Если&#039;&#039;&#039; &amp;lt;math&amp;gt; \exists i: \tilde{\mu_i} \neq \mu_i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; iter &amp;lt; maxiter &amp;lt;/math&amp;gt;,&lt;br /&gt;
    &lt;br /&gt;
        &#039;&#039;&#039;то&#039;&#039;&#039; &amp;lt;math&amp;gt; inc(iter), \Mu=\tilde{\Mu},&amp;lt;/math&amp;gt; &#039;&#039;&#039;GOTO 2.а.&#039;&#039;&#039;&lt;br /&gt;
&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;2a)&#039;&#039;&#039; Вычисление матрицы расстояний требует &amp;lt;math&amp;gt; n*k*d &amp;lt;/math&amp;gt; операций умножения и &amp;lt;math&amp;gt; n*k*(d-1) &amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;2b)&#039;&#039;&#039; Нахождение вектора распределения требует &amp;lt;math&amp;gt; n*(k-1) &amp;lt;/math&amp;gt; операций сравнения&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;3)&#039;&#039;&#039; Для вычисления &amp;lt;math&amp;gt; \tilde{\Mu} &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt; (n - k + 1) * d &amp;lt;/math&amp;gt; операций сложения и &amp;lt;math&amp;gt; k * d &amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;4)&#039;&#039;&#039; Для критерия останова требуется &amp;lt;math&amp;gt; n*d &amp;lt;/math&amp;gt; сравнений&lt;br /&gt;
&lt;br /&gt;
Итого: так как максимальное количество итераций задается в алгоритме заранее и не зависит от входных данных, то количество итераций ограничено константой. Тогда сложность алгоритма:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt; O(n*k*d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt; O(n*k*d)&amp;lt;/math&amp;gt; операций умножения, &amp;lt;math&amp;gt; O(k*d) &amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
Рассмотрим вычислительные этапы алгоритма k-means. Это распределение объектов по кластерам и перерасчет центроидов.&lt;br /&gt;
&lt;br /&gt;
Распределение объектов по кластерам состоит из нахождения матрицы расстояний между &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Mu&amp;lt;/math&amp;gt; (этапа &#039;&#039;&#039;2a)&#039;&#039;&#039;) и нахождения индекса минимального элемента в каждой строке (этап &#039;&#039;&#039;2b)&#039;&#039;&#039;). Ниже приведен информационный граф данного этапа.&lt;br /&gt;
&lt;br /&gt;
[[Файл:EgorovBogomazovInf.png |800px|thumb|center|Нахождение вектора индексов распределения по кластерам]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассмотрим этап вычисления ячейки &amp;lt;math&amp;gt; dist_{ij} &amp;lt;/math&amp;gt; матрицы более подробно. Он представляет из себя скалярное произведение d-мерных векторов &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \mu_j &amp;lt;/math&amp;gt;. Ниже приведена граф для данной операции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:EgorovBogomazovDist.png|600px|thumb|center|Операция вычисления расстояния]]&lt;br /&gt;
&lt;br /&gt;
На этапе &#039;&#039;&#039;3)&#039;&#039;&#039; происходит суммирование всех &amp;lt;math&amp;gt; x_l &amp;lt;/math&amp;gt; с одинаковым значением индекса в промежуточные суммы. Одновременно происходит подсчет количество элементов с фиксированным индексом. Затем происходит деление промежуточных сумм на соответствующее количество элементов в новом кластере, благодаря чему удается получить обновленные центроиды &amp;lt;math&amp;gt; \tilde{\mu}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:EgorovBogomazovNewCluster.png|800px|thumb|center|Обновление центроидов]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Для получения общей картины достаточно определить, в том числе и с помощью информационного графа, каким ресурсом параллелизма обладает каждый из этапов алгоритма:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2a)&#039;&#039;&#039; Нахождение каждой ячейки матрицы &amp;lt;math&amp;gt; dist &amp;lt;/math&amp;gt; может происходить полностью независимо, так как они находятся на одном ярусе. При рассмотрении операции вычисления значения конкретной ячейки &amp;lt;math&amp;gt; dist_{ij} &amp;lt;/math&amp;gt; можно заметить, что все операции умножения также независимы между собой, поэтому понадобится всего одно умножение. Конечный результат получается в результате суммирования всех слагаемых и достигается параллельно за &amp;lt;math&amp;gt; \log(d) &amp;lt;/math&amp;gt; бинарных операций сложения при помощи стандартного метода &#039;&#039;reduction&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2b)&#039;&#039;&#039; Нахождение вектора распределений заключается в нахождении минимального элемента в каждой строке матрицы &amp;lt;math&amp;gt; dist &amp;lt;/math&amp;gt;. При использовании &#039;&#039;reduction&#039;&#039; эту операцию можно выполнить за &amp;lt;math&amp;gt; \log(k) &amp;lt;/math&amp;gt; бинарных операций сравнения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3)&#039;&#039;&#039; В случае, если существует методология быстрого разделения &#039;&#039;&#039;n&#039;&#039;&#039; объектов на &#039;&#039;&#039;k&#039;&#039;&#039; множеств при помощи вектора индексации (косвенной адресации), все &amp;lt;math&amp;gt; \tilde{S_i} &amp;lt;/math&amp;gt; могут быть обработаны параллельно. В рамках каждого отдельного &amp;lt;math&amp;gt; \tilde{S_i} &amp;lt;/math&amp;gt; можно проводить распараллеливание дальше, так как &amp;lt;math&amp;gt; x_{lj} &amp;lt;/math&amp;gt; также не зависят друг от друга. Таким образом, вычисление всех слагаемых для всех &amp;lt;math&amp;gt; \tilde{\mu_{ij}} &amp;lt;/math&amp;gt; может быть выполнено за одно параллельное деление, а нахождение суммы всех этих слагаемых для каждого &amp;lt;math&amp;gt; \tilde{\mu_{ij}} &amp;lt;/math&amp;gt; может быть выполнено за &amp;lt;math&amp;gt; O(log(n)) &amp;lt;/math&amp;gt; в худшем случае (при вхождении почти всех объектов в один кластер).&lt;br /&gt;
&lt;br /&gt;
Если же такой методологии не существует, то узким местом становится процесс распределения на множества согласно вектору &amp;lt;math&amp;gt;index&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;
&#039;&#039;&#039;4)&#039;&#039;&#039; Все сравнения для критерия останова также независимы между собой и могут быть выполнены за одну операцию сравнения, а финальный результат может быть получен за одну операцию сложения при работе над общей памятью (к примеру, прибавление единицы при несовпадении в общую память и сравнение ее с 0 по завершению) или за &amp;lt;math&amp;gt;\log(k*d)&amp;lt;/math&amp;gt; сложений иначе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так как количество итераций ограничено константой, то на сложность оно не влияет. В итоге, параллельная сложность алгоритма k-means такова:&lt;br /&gt;
* &amp;lt;math&amp;gt; O(\log(n*d)) &amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt; O(\log(k)) &amp;lt;/math&amp;gt; операций сравнения&lt;br /&gt;
* &amp;lt;math&amp;gt; O(1) &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;b&amp;gt;k&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;n&amp;lt;/b&amp;gt; кластеризуемых элементов&lt;br /&gt;
&lt;br /&gt;
Дополнительные ограничения:&lt;br /&gt;
* &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt; — положительное число, т. е. &amp;lt;b&amp;gt;k &amp;gt; 0&amp;lt;/b&amp;gt;.&lt;br /&gt;
* Для кластеризуемых элементов определена метрика (расстояние между объектами)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;b&amp;gt;n &amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt; d + 1&amp;lt;/b&amp;gt; (кластеризуемые объекты в виде векторов и число &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: Массив, в который записаны принадлежности каждого элемента кластеру (допустим вывод в другой эквивалентной более удобной структуре).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;:  Размер массива равен &amp;lt;b&amp;gt;2 &amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt; n&amp;lt;/b&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;n\cdot k\cdot d&amp;lt;/math&amp;gt; умножений&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;n \cdot d + 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность &amp;lt;math&amp;gt; = \dfrac{n \cdot k \cdot d}{n\cdot d + 1} = k&amp;lt;/math&amp;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;
&#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;
&#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;
=== Масштабируемость реализации ===&lt;br /&gt;
&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма k-средних. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 16] с увеличением в 2 раза;&lt;br /&gt;
*	размер данных [100000 : 1600000] с увеличением в 2 раза.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов были получены следующие данные:&lt;br /&gt;
&lt;br /&gt;
*	Максимальная эффективность в точке достигается при переходе от 1 потока на 8 при минимальном размере данных, она равна &amp;lt;math&amp;gt;87,5%&amp;lt;/math&amp;gt;.&lt;br /&gt;
*	Усредненная максимальная эффективность достигается при переходе с одного потока на два. Среднее время вычислений на всех рассмотренных потока снижается с 16,33 до 11.87 секунд, поэтому формально эффективность &amp;lt;math&amp;gt;= 16.33 / 11.87 / 2 \approx 68,4\%&amp;lt;/math&amp;gt;&lt;br /&gt;
*	Минимальная эффективность в точке достигается при переходе от 1 потока на 16 при размере данных 800000, она равна &amp;lt;math&amp;gt;11,1\%&amp;lt;/math&amp;gt;.&lt;br /&gt;
*	Усредненная минимальная эффективность наблюдается при переходе с одного на максимальное рассматриваемое в эксперименте число потоков, равное 16. Время вычисления изменяется с 16,33 до 7,6 секунд, поэтому формально эффективность &amp;lt;math&amp;gt; = 16.33 / 7.6 / 16 \approx 14,9\%&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ниже приведены графики зависимости вычислительного времени алгоритма и его эффективности от изменяемых параметров запуска -- размера данных и числа процессоров:&lt;br /&gt;
&lt;br /&gt;
[[file:Egorov_Bogomazov_Time.jpg|thumb|center|700px|Параллельная реализация алгоритма k-средних. Изменение вычислительного времени алгоритма в зависимости от числа процессоров и размера исходных данных.]]&lt;br /&gt;
&lt;br /&gt;
[[file:Egorov_Bogomazov_Efficiency.jpg|thumb|center|700px|Параллельная реализация алгоритма k-средних. Изменение эффективности алгоритма в зависимости от числа процессоров и размера исходных данных.]]&lt;br /&gt;
&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;&#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;
&#039;&#039;&#039;Десктопные программы&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/ml/weka/ Weka]&lt;br /&gt;
* [http://docs.orange.biolab.si/2/reference/rst/Orange.clustering.kmeans.html Orange]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фреймворки для языков&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Python:&#039;&#039;&#039;&lt;br /&gt;
** [http://scikit-learn.org/ scikit-learn]&lt;br /&gt;
** [https://www.scipy.org/ SciPy]&lt;br /&gt;
* &#039;&#039;&#039;C++:&#039;&#039;&#039;&lt;br /&gt;
** [http://mlpack.org/ MLPACK]&lt;br /&gt;
** [http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_ml/py_kmeans/py_kmeans_opencv/py_kmeans_opencv.html OpenCV]&lt;br /&gt;
** [https://www.gnu.org/software/octave/ Octave] &lt;br /&gt;
* &#039;&#039;&#039;C#:&#039;&#039;&#039;  &lt;br /&gt;
** [http://accord-framework.net/docs/html/T_Accord_MachineLearning_KMeans.htm Accord.NET]&lt;br /&gt;
* &#039;&#039;&#039;Java:&#039;&#039;&#039;&lt;br /&gt;
** [http://elki.dbs.ifi.lmu.de ELKI]&lt;br /&gt;
** [https://mahout.apache.org Mahout]&lt;br /&gt;
* &#039;&#039;&#039;Lua&#039;&#039;&#039;&lt;br /&gt;
** [http://torch.ch Torch]&lt;br /&gt;
&lt;br /&gt;
Языки [https://www.r-bloggers.com/k-means-clustering-in-r/ R] и [http://juliastats.github.io Julia] содержат алгоритм k-means в базовой реализации.&lt;br /&gt;
&lt;br /&gt;
=== Платный доступ/лицензия  ===&lt;br /&gt;
&lt;br /&gt;
Существует целый перечень мощных статистических и математических пакетов для разных ОС:&lt;br /&gt;
* [http://www.mathworks.com/help/stats/kmeans.html?requestedDomain=www.mathworks.com MATLAB]&lt;br /&gt;
* [http://mathworld.wolfram.com/K-MeansClusteringAlgorithm.html Mathematica]&lt;br /&gt;
* [http://www.stata.com/features/cluster-analysis/ Stata]&lt;br /&gt;
* [https://support.sas.com/rnd/app/stat/procedures/fastclus.html SAS]&lt;br /&gt;
* [https://blogs.sap.com/2013/03/28/sap-hana-pal-k-means-algorithm-or-how-to-do-customer-segmentation-for-the-telecommunications-industry/ SAP HANA]&lt;br /&gt;
* [http://docs.rapidminer.com/studio/operators/modeling/segmentation/k_means.html RapidMiner]&lt;br /&gt;
* ...&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] https://ru.wikipedia.org/wiki/K-means&lt;br /&gt;
&lt;br /&gt;
[[en:Description of algorithm properties and structure]]&lt;/div&gt;</summary>
		<author><name>Zhum</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%91%D1%80%D0%BE%D1%82%D0%B8%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%94%D0%B0%D0%BD%D1%83%D1%82%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-means&amp;diff=17540</id>
		<title>Участник:Бротиковская Данута/Алгоритм k-means</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%91%D1%80%D0%BE%D1%82%D0%B8%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%94%D0%B0%D0%BD%D1%83%D1%82%D0%B0/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k-means&amp;diff=17540"/>
		<updated>2016-11-16T08:11:56Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних (k means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(\Tau*k*d*n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; d*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;
Авторы страницы &amp;lt;b&amp;gt;[[Участник:Бротиковская Данута|Данута Бротиковская]]&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;[[Участник:DennZo1993|Денис Зобнин]]&amp;lt;/b&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;b&amp;gt;&amp;lt;i&amp;gt;k средних&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; (&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt;) &amp;amp;ndash; неиерархический&amp;lt;ref&amp;gt;&amp;quot;https://ru.wikipedia.org/wiki/Иерархическая_кластеризация&amp;quot;&amp;lt;/ref&amp;gt;, итерационный метод кластеризации&amp;lt;ref&amp;gt;&amp;quot;https://ru.wikipedia.org/wiki/Кластерный_анализ&amp;quot;&amp;lt;/ref&amp;gt;, получивший большую популярность благодаря своей простоте, наглядности реализации и достаточно высокому качеству работы. Был изобретен в 1950-х годах математиком &amp;lt;i&amp;gt;Гуго Штейнгаузом&amp;lt;/i&amp;gt;&amp;lt;ref&amp;gt;Steinhaus, Hugo. &amp;quot;Sur la division des corp materiels en parties.&amp;quot; Bull. Acad. Polon. Sci 1.804 (1956): 801.&amp;lt;/ref&amp;gt; и почти одновременно &amp;lt;i&amp;gt;Стюартом Ллойдом&amp;lt;/i&amp;gt;&amp;lt;ref&amp;gt;Lloyd, S. P. &amp;quot;Least square quantization in PCM. Bell Telephone Laboratories Paper. Published in journal much later: Lloyd, SP: Least squares quantization in PCM.&amp;quot; IEEE Trans. Inform. Theor.(1957/1982).&amp;lt;/ref&amp;gt;. Особую популярность приобрел после публикации работы &amp;lt;i&amp;gt;МакКуина&amp;lt;/i&amp;gt;&amp;lt;ref&amp;gt;MacQueen, James. &amp;quot;Some methods for classification and analysis of multivariate observations.&amp;quot; Proceedings of the fifth Berkeley symposium on mathematical statistics and probability. Vol. 1. No. 14. 1967.&amp;lt;/ref&amp;gt; в 1967.&lt;br /&gt;
&lt;br /&gt;
Алгоритм представляет собой версию EM-алгоритма&amp;lt;ref&amp;gt;&amp;quot;https://ru.wikipedia.org/wiki/EM-алгоритм&amp;quot;&amp;lt;/ref&amp;gt;, применяемого также для разделения смеси гауссиан. Основная идея &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;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; кластеров таким образом, чтобы каждое наблюдение принадлежало ровно одному кластеру, расположенному на наименьшем расстоянии от наблюдения.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Дано:&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
* набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; наблюдений &amp;lt;math&amp;gt;X=\{\mathbf{x}_1, \mathbf{x}_2, ..., \mathbf{x}_n\}, \mathbf{x}_i \in \mathbb{R}^d, i=\overline{1, 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 \mathbb{N}, k \leq n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Требуется:&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&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;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;:&lt;br /&gt;
*   &amp;lt;math&amp;gt;S_i \cap S_j= \varnothing, \quad i \ne j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*   &amp;lt;math&amp;gt;\bigcup_{i=1}^{k} S_i = X&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Действие алгоритма:&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм &amp;lt;i&amp;gt;k means&amp;lt;/i&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;S_1, S_2, ..., S_k,&amp;lt;/math&amp;gt; таким образом, чтобы минимизировать сумму квадратов расстояний от каждой точки кластера до его центра (центр масс кластера). Введем обозначение, &amp;lt;math&amp;gt;S=\{S_1, S_2, ..., S_k\}&amp;lt;/math&amp;gt;. Тогда действие алгоритма &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; равносильно поиску:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;table style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;center&amp;quot; width=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\arg\min_{S} \sum\limits_{i=1}^k \sum\limits_{\mathbf{x} \in S_i} \rho(\mathbf{x}, \mathbf{\mu}_i )^2,&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;&amp;lt;math&amp;gt;(1)&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\mathbf{\mu}_i&amp;lt;/math&amp;gt; &amp;amp;ndash; центры кластеров, &amp;lt;math&amp;gt;i=\overline{1,k}, \quad \rho(\mathbf{x}, \mathbf{\mu}_i)&amp;lt;/math&amp;gt; &amp;amp;ndash; функция расстояния между &amp;lt;math&amp;gt;\mathbf{x}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;u&amp;gt;Шаги алгоритма: &amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Начальный шаг: инициализация кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Выбирается произвольное множество точек &amp;lt;math&amp;gt;\mu_i, i=\overline{1,k}&amp;lt;/math&amp;gt;, рассматриваемых как начальные центры кластеров: &amp;lt;math&amp;gt;\mu_i^{(0)} = \mu_i, \quad i=\overline{1, k}&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Шаг&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;t: \forall \mathbf{x}_i \in X, i=\overline{1,n}: \mathbf{x}_i \in S_j \iff  j=\arg\min_{k}\rho(\mathbf{x}_i,\mathbf{\mu}_k^{(t-1)})^2&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Пересчет центров кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Шаг &amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;t: \forall i=\overline{1,k}:  \mu_i^{(t)} = \cfrac{1}{|S_i|}\sum_{\mathbf{x}\in S_i}\mathbf{x}&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Проверка условия останова:&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &#039;&#039;&#039;if&#039;&#039;&#039; &amp;lt;math&amp;gt;\exist i\in \overline{1,k}: \mu_i^{(t)} \ne \mu_i^{(t-1)}&amp;lt;/math&amp;gt; &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
        t = t + 1;&lt;br /&gt;
        goto 2;&lt;br /&gt;
    &#039;&#039;&#039;else&#039;&#039;&#039;&lt;br /&gt;
        &#039;&#039;&#039;stop&#039;&#039;&#039;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительным ядром являются шаги 2 и 3 приведенного выше алгоритма: &amp;lt;b&amp;gt;&amp;lt;i&amp;gt;распределение векторов по кластерам&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;&amp;lt;i&amp;gt;пересчет центров кластеров&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;Распределение векторов&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; по кластерам предполагает вычисление расстояний между каждым вектором &amp;lt;math&amp;gt;\mathbf{x}_i \in X, i= \overline{1,n}&amp;lt;/math&amp;gt; и центрами кластера &amp;lt;math&amp;gt;\mathbf{\mu}_j, j= \overline{1,k}&amp;lt;/math&amp;gt;. Таким образом, данный шаг предполагает &amp;lt;math&amp;gt;k*n&amp;lt;/math&amp;gt; вычислений расстояний между d-мерными векторами. &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;Пересчет центров кластеров&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt; предполагает &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; вычислений центров масс &amp;lt;math&amp;gt;\mathbf{\mu}_i&amp;lt;/math&amp;gt; множеств &amp;lt;math&amp;gt;S_i, i=\overline{1,k}&amp;lt;/math&amp;gt; представленных выражением в шаге 3 представленного выше алгоритма.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Инициализация центров масс &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Наиболее распространенными являются следующие стратегии:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Метод Forgy&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;В качестве начальных значений &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt; берутся случайно выбранные векторы.&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Метод случайно разделения (Random Partitioning)&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;Для каждого вектора &amp;lt;math&amp;gt;\mathbf{x}_i \in X, i=\overline{1, n}&amp;lt;/math&amp;gt; выбирается случайным образом кластер &amp;lt;math&amp;gt;S_1, ..., S_k&amp;lt;/math&amp;gt;, после чего для каждого полученного кластера вычисляются значения &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&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;\mathbf{x}_i \in X, i=\overline{1, n}&amp;lt;/math&amp;gt; и центрами кластеров &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt; вычисляются &amp;lt;b&amp;gt;расстояния&amp;lt;/b&amp;gt; по формуле (как правило, используется Евлидово расстояние):&lt;br /&gt;
&amp;lt;table style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;center&amp;quot; width=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;math&amp;gt; \mathbf{v}_1, \mathbf{v}_2 \in \mathbb{R}^d,  \quad \rho(\mathbf{v}_1, \mathbf{v}_2) = \lVert \mathbf{v}_1- \mathbf{v}_2 \rVert= \sqrt{\sum_{i=1}^{d}(\mathbf{v}_{1,i} - \mathbf{v}_{2,i})^2}&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;&amp;lt;math&amp;gt;(2)&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&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;b&amp;gt;формуле вычисления центра масс&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;table style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;center&amp;quot; width=&amp;quot;90%&amp;quot;&amp;gt;&amp;lt;math&amp;gt; \mu = \cfrac{1}{|S|}\sum_{\mathbf{x}\in S}\mathbf{x}&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;&amp;lt;math&amp;gt;(3)&amp;lt;/math&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;algorithm&#039;&#039;&#039; k-means &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
     &#039;&#039;&#039;1.&#039;&#039;&#039; Инициализировать центры кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_i^{(1)}, i=\overline{1,k}&amp;lt;/math&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;2.&#039;&#039;&#039; &amp;lt;math&amp;gt;t \leftarrow 1&amp;lt;/math&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;3.&#039;&#039;&#039; &#039;&#039;&#039;Распределение по кластерам&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
        &amp;lt;math&amp;gt;S_i^{(t)}=\{\mathbf{x}_p: \lVert\mathbf{x}_p-\mathbf{\mu}_i^{(t)}\rVert^2 \leq \lVert\mathbf{x}_p-\mathbf{\mu}_j^{(t)}\rVert^2 \quad \forall j=\overline{1,k}\},&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        где каждый вектор &amp;lt;math&amp;gt;\mathbf{x}_p&amp;lt;/math&amp;gt; соотносится единственному кластеру &amp;lt;math&amp;gt;S^{(t)}&amp;lt;/math&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;4.&#039;&#039;&#039; &#039;&#039;&#039;Обновление центров кластеров&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
        &amp;lt;math&amp;gt;\mathbf{\mu}_i^{(t+1)} = \frac{1}{|S^{(t)}_i|} \sum_{\mathbf{x}_j \in S^{(t)}_i} \mathbf{x}_j &amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
     &#039;&#039;&#039;5.&#039;&#039;&#039; &#039;&#039;&#039;if&#039;&#039;&#039; &amp;lt;math&amp;gt;\exist i \in \overline{1,k}: \mathbf{\mu}_i^{(t+1)} \ne \mathbf{\mu}_i^{(t)}&amp;lt;/math&amp;gt; &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
            t = t + 1;&lt;br /&gt;
            goto &#039;&#039;&#039;3&#039;&#039;&#039;;&lt;br /&gt;
        &#039;&#039;&#039;else&#039;&#039;&#039;&lt;br /&gt;
            &#039;&#039;&#039;stop&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма===&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Обозначим &amp;lt;math&amp;gt;\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; временную сложность вычисления центорида кластера, число элементов которого равна &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;,  в d-мерном пространстве.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Аналогично  &amp;lt;math&amp;gt;\Theta_{distance}^d&amp;lt;/math&amp;gt; &amp;amp;ndash; временная сложность вычисления расстояния между двумя d-мерными векторами.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Сложность шага инициализации &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров мощности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; в d-мерном пространстве &amp;amp;ndash; &amp;lt;math&amp;gt;\Theta_{init}^{k, d, m}&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Стратерия Forgy&amp;lt;/i&amp;gt;: вычисления не требуются, &amp;lt;math&amp;gt;\Theta_{init}^{k, d, m} = 0&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Стратегия случайного разбиения&amp;lt;/i&amp;gt;: вычисление центров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров,  &amp;lt;math&amp;gt;\Theta_{init}^{k, d, m} = k*\Theta_{centroid}^{d, m}, m \le n&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Cложность шага распределения d мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам &amp;amp;ndash; &amp;lt;math&amp;gt;\Theta_{distribute}^{k, d}&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
На этом шаге для каждого вектора &amp;lt;math&amp;gt;\mathbf{x}_i \in X, i=\overline{1, n}&amp;lt;/math&amp;gt; вычисляется k расстояний до центров кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ...\mathbf{\mu}_k&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;math&amp;gt;\Theta_{distribute}^{k, d} = n*k*\Theta_{distance}^d&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Сложность шага пересчета центров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров размера &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; в d-мерном пространстве &amp;amp;ndash; &amp;lt;math&amp;gt;\Theta_{recenter}^{k, d, m}&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;На этом шаге вычисляется &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; центров кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ...\mathbf{\mu}_k&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Theta_{recenter}^{k, d, m} = k*\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; &amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Рассчитаем &amp;lt;math&amp;gt;\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; для кластера, число элементов которого равно &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Theta_{centroid}^{d, m}&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;m * d&amp;lt;/math&amp;gt; сложений + &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; делений &amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Рассчитаем &amp;lt;math&amp;gt;\Theta_{distance}^d&amp;lt;/math&amp;gt; в соответствие с формулой &amp;lt;math&amp;gt;(2)&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Theta_{distance}^d&amp;lt;/math&amp;gt; = &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; вычитаний + &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; умножений + &amp;lt;math&amp;gt;(d-1)&amp;lt;/math&amp;gt; сложение&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;Предположим, что алгоритм сошелся за &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; итераций, тогда временная сложность алгоритма &amp;lt;math&amp;gt;\Theta_{k-means}^{d, n}&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt;\Theta_{k-means}^{d, n} \le \Theta_{init}^{k, d, n} + \Tau*(\Theta_{distribute}^{k, d} + \Theta_{recenter}^{k, d, n})&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 5px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Операции сложения/вычитания:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt;\Theta_{k-means}^{d, n} \le k*n*d+ \Tau*(k*n*(2d-1) + k*n*d) = k*n*d+ \Tau*(k*n*(3d-1)) \thicksim O(\Tau*k*d*n)&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Операции умножения/деления:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt;\Theta_{k-means}^{d, n} \le k*d + \Tau*(k*n*d + k*d) = k*d + \Tau * k*d*(n+1) \thicksim O(\Tau*k*d*n) &amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-bottom: 20px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Получаем, что &amp;lt;b&amp;gt;временная сложность&amp;lt;/b&amp;gt; алгоритма &amp;lt;b&amp;gt;k means&amp;lt;/b&amp;gt; кластеризации &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; &amp;lt;b&amp;gt;d-мерных&amp;lt;/b&amp;gt; векторов на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров за &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; итераций:&amp;lt;/p&amp;gt;&lt;br /&gt;
 &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;math&amp;gt; \Theta_{k-means}^{d, n} \thicksim O(\Tau*k*d*n) &amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф алгоритма. Алгоритм &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; начинается с этапа инициализации, после которого следуют итерации, на каждой из которых выполняется два последовательных шага (см. [[#Схема реализации последовательного алгоритма|&amp;quot;Схема реализации последовательного алгоритма&amp;quot;]]): &lt;br /&gt;
* распределение векторов по кластерам&lt;br /&gt;
* перерасчет центров кластеров&lt;br /&gt;
&lt;br /&gt;
Поскольку основная часть вычислений приходится на шаги итераций, распишем информационные графы данных шагов.&lt;br /&gt;
&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Информационный граф шага распределения векторов по кластерам представлен на &#039;&#039;рисунке 1&#039;&#039;. Исходами данного графа является исходные векторы &amp;lt;math&amp;gt;\mathbf{x}_1, ..., \mathbf{x}_n&amp;lt;/math&amp;gt;, а также центры кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ... \mathbf{\mu}_k&amp;lt;/math&amp;gt;, вычисленные ранее (на шаге инициализации, если рассматривается первая итерация алгоритма, или на шаге пересчета центров кластеров предыдущей итерации в противном случае). Каждая пара векторов данных &amp;lt;math&amp;gt;\mathbf{x}_i, i=\overline{1,n}&amp;lt;/math&amp;gt; и центров кластера &amp;lt;math&amp;gt;\mathbf{\mu}_j, j=\overline{1, k}&amp;lt;/math&amp;gt; : (&amp;lt;math&amp;gt;\mathbf{x}_i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\mathbf{\mu}_j&amp;lt;/math&amp;gt;) подаются на независимые узлы &amp;lt;i&amp;gt;&amp;quot;d&amp;quot;&amp;lt;/i&amp;gt; вычисления расстояния между векторами (более подробная схема вычисления расстояния представлена далее, &#039;&#039;рисунок 2&#039;&#039;). Далее узлы вычисления расстояния &amp;lt;i&amp;gt;&amp;quot;d&amp;quot;&amp;lt;/i&amp;gt;, соответствующие одному и тому же исходному вектору &amp;lt;math&amp;gt;\mathbf{x}_i&amp;lt;/math&amp;gt; передаются на один узел &amp;lt;i&amp;gt;&amp;quot;m&amp;quot;&amp;lt;/i&amp;gt;, где далее происходит вычисление новой метки кластера для каждого вектора &amp;lt;math&amp;gt;\mathbf{x}_i&amp;lt;/math&amp;gt; (берется кластер с минимальным результатом вычисления расстояния). На выходе графа выдаются метки кластеров , &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt;, такие что &amp;lt;math&amp;gt;\forall \mathbf{x}_i, i=\overline{1, n}, \mathbf{x}_i \in S_j \Leftrightarrow L_i = j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[file:Clusterization.png|thumb|center|800px|Рис. 1. Схема распределения векторов по кластерам. &#039;&#039;d&#039;&#039; &amp;amp;ndash; вычисление расстояния между векторами; &#039;&#039;m&#039;&#039; &amp;amp;ndash; вычисление минимума.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Вычисление расстояния между векторами&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Подробная схема вычисления расстояния между векторами &amp;lt;math&amp;gt;\mathbf{x}_i, \mathbf{\mu}_j&amp;lt;/math&amp;gt; представлена на &#039;&#039;рисунке 2&#039;&#039;. Как показано на графе, узел вычисления расстояния между векторами &amp;lt;i&amp;gt;&amp;quot;d&amp;quot;&amp;lt;/i&amp;gt; состоит из шага взятия разности между векторами (узел &amp;quot;&amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&amp;quot;) и взятия нормы получившегося вектора разности (узел &amp;quot;&amp;lt;math&amp;gt;||\cdot||^2&amp;lt;/math&amp;gt;&amp;quot;). Более подробно, вычисление расстояния между векторами &amp;lt;math&amp;gt;\mathbf{x}_i = {x_{i1,}, ...,{x_{in}}}, \mathbf{\mu}_j = {\mu_{j1}, ...,\mu_{jn}}&amp;lt;/math&amp;gt; может быть представлено как вычисление разности между каждой парой компонент &amp;lt;math&amp;gt;(x_{iz}, \mu_{jz}), z=\overline{1, d}&amp;lt;/math&amp;gt; (узел &amp;quot;&amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&amp;quot;), далее возведение в квадрат для каждого узла &amp;quot;&amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&amp;quot; (узел &amp;quot;&amp;lt;math&amp;gt;()^2&amp;lt;/math&amp;gt;&amp;quot;) и суммирования выходов всех узлов &amp;quot;&amp;lt;math&amp;gt;()^2&amp;lt;/math&amp;gt;&amp;quot; (узел &amp;quot;&amp;lt;math&amp;gt;+&amp;lt;/math&amp;gt;&amp;quot;).  &lt;br /&gt;
&lt;br /&gt;
[[file:dist_calc.png|thumb|center|800px|Рис. 2. Схема вычисления расстояния между вектором и центром кластера.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Пересчет центров кластеров&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Информационный граф шага пересчета центров кластеров представлен на &#039;&#039;рисунке 3&#039;&#039;. Исходами данного графа является исходные векторы &amp;lt;math&amp;gt;\mathbf{x}_1, ..., \mathbf{x}_n&amp;lt;/math&amp;gt;, а также им соответствующие метки кластера, &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt;, такие что &amp;lt;math&amp;gt;\forall x_i, i=\overline{1, n}, \mathbf{x}_i \in S_j \Leftrightarrow L_i = j&amp;lt;/math&amp;gt;, вычисленные на этапе распределения векторов по кластерам. Все векторы &amp;lt;math&amp;gt;\mathbf{x}_1, ..., \mathbf{x}_n&amp;lt;/math&amp;gt; подаются в узлы &amp;lt;math&amp;gt;+_1, ...+_k&amp;lt;/math&amp;gt;, каждый узел &amp;lt;math&amp;gt;+_m, m = \overline{1,k}&amp;lt;/math&amp;gt; соответствует операции сложения векторов кластера с номером &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;. Метки кластера &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt; также совместно передаются на узлы &amp;lt;math&amp;gt;S_m, m = \overline{1,k}&amp;lt;/math&amp;gt;, на каждом из которых вычисляется количество векторов в соответствующем кластере (количество меток с соответствующим значением). Далее каждая пара выходов узлов &amp;lt;math&amp;gt;+_m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;S_m&amp;lt;/math&amp;gt; подается на узел &amp;quot;&amp;lt;math&amp;gt;/&amp;lt;/math&amp;gt;&amp;quot;, где производится деление суммы векторов кластера на количество элементов в нем. Значения, вычисленные на узлах &amp;quot;&amp;lt;math&amp;gt;/&amp;lt;/math&amp;gt;&amp;quot;, присваиваются новым центрам кластеров (выходные значения графа).&lt;br /&gt;
&lt;br /&gt;
[[file:Recluster.png|thumb|center|800px|Рис. 3. Схема пересчета центров кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Работа алгоритма состоит из &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; итераций, в каждой из которых происходит &amp;lt;b&amp;gt;распределение d-мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам&amp;lt;/b&amp;gt;, а также &amp;lt;b&amp;gt;пересчет центров кластеров в d-мерном пространстве&amp;lt;/b&amp;gt;. В шаге &amp;lt;b&amp;gt;распределения d-мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам&amp;lt;/b&amp;gt; расстояния между вектором и центрами кластеров вычисляются независимо (отсутствуют информационные зависимости). &amp;lt;b&amp;gt;Центры масс кластеров&amp;lt;/b&amp;gt; также пересчитываются независимо друг от друга. Таким образом, имеет место [[глоссарий#Массовый параллелизм|&#039;&#039;массовый параллелизм&#039;&#039;]]. Вычислим параллельную сложность &amp;lt;math&amp;gt;\Psi_*&amp;lt;/math&amp;gt; каждого из шагов, а также параллельную сложность всего алгоритма, &amp;lt;math&amp;gt;\Psi_{k-means}&amp;lt;/math&amp;gt;. Будем исходить из предположения, что может быть использовано любое необходимое число потоков.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Распределение d-мерных векторов по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;  &lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Поскольку на данном шаге для каждой пары векторов &amp;lt;math&amp;gt;\mathbf{x}_i, i=\overline{1,n}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mathbf{\mu}_j, j=\overline{1,k}&amp;lt;/math&amp;gt; операции вычисления расстояния не зависят друг от друга, они могут выполняться параллельно. Тогда, разделив все вычисление расстояний на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; потоков, получим, что в каждом потоке будет выполняться только одна операция вычисления расстояния между векторами размерности d. При этом каждому вычислительному потоку передаются координаты центров всех кластеров &amp;lt;math&amp;gt;\mathbf{\mu}_1, ..., \mathbf{\mu}_k&amp;lt;/math&amp;gt;. Таким образом, параллельная сложность данного шага определяется &amp;lt;i&amp;gt; сложностью параллельной операции вычисления расстояния между d-мерными векторами&amp;lt;/i&amp;gt;, &amp;lt;math&amp;gt;\Psi_{distance}^d&amp;lt;/math&amp;gt;  и &amp;lt;i&amp;gt;сложностью определения наиболее близкого кластера&amp;lt;/i&amp;gt; (паралельное взятие минимума по расстояниям), &amp;lt;math&amp;gt;\Psi_{min}^k&amp;lt;/math&amp;gt;. Для оценки &amp;lt;math&amp;gt;\Psi_{distance}^d&amp;lt;/math&amp;gt; воспользуемся  [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения частичной суммы элементов массива путем сдваивания]]. Аналогично, &amp;lt;math&amp;gt;\Psi_{min}^k = log(k)&amp;lt;/math&amp;gt;. В результате, &amp;lt;math&amp;gt;\Psi_{distance}^d = O(log(d))&amp;lt;/math&amp;gt;.   Таким образом: &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Psi_{distribute}^{k, d}  = \Psi_{distance}^d + \Psi_{min}^k = O(log(d))+O(log(k)) = O(log(k*d))&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Пересчет центров кластеров в d-мерном пространстве&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Поскольку на данном шаге для каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров центр масс может быть вычислен независимо, данные операции могут быть выполнены в отдельных потоках. Таким образом, параллельная сложность данного шага, &amp;lt;math&amp;gt;\Psi_{recenter}^{k, d}&amp;lt;/math&amp;gt;,  будет определяться &amp;lt;i&amp;gt;параллельной сложностью вычисления одного центра масс кластера размера &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;&amp;lt;/i&amp;gt;, &amp;lt;math&amp;gt;\Psi_{recenter}^{k, d}&amp;lt;/math&amp;gt;,  а так как &amp;lt;math&amp;gt;m \le n \Rightarrow \Psi_{recenter}^{d, m} \le \Psi_{recenter}^{d, n}&amp;lt;/math&amp;gt;. Сложность вычисления центра масс кластера d-мерных векторов размера n аналогично предыдущим вычислениям равна &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;. Тогда:  &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Psi_{recenter}^{k, d}  \le \Psi_{recenter}^{d, n} = O(log(n))&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Общая параллельная сложность алгоритма&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
На каждой итерации необходимо обновление центров кластеров, которые будут использованы на следующей итерации. Таким образом, итерационный процесс выполняется последовательно&amp;lt;ref&amp;gt;Zhao, Weizhong, Huifang Ma, and Qing He. &amp;quot;Parallel k-means clustering based on mapreduce.&amp;quot; IEEE International Conference on Cloud Computing. Springer Berlin Heidelberg, 2009.&amp;lt;/ref&amp;gt;. Тогда, поскольку сложность каждой итерации  определяется &amp;lt;math&amp;gt;\Psi_{distribute}^{k, d}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Psi_{recenter}&amp;lt;/math&amp;gt;, сложность всего алгоритма, &amp;lt;math&amp;gt;\Psi_{k-means}&amp;lt;/math&amp;gt; в предположении, что было сделано &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; операций определяется выражением&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Psi_{k-means} \approx \Tau * (\Psi_{distribute}^{k, d} + \Psi_{recenter}^{k, d}) \le \Tau * O(log(k*d*n))&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Входные данные&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Матрица из &amp;lt;math&amp;gt;n*d&amp;lt;/math&amp;gt; элементов &amp;lt;math&amp;gt;x_{i, j} \in \mathbb{R}, i=\overline{1, n}, j=\overline{1, d}&amp;lt;/math&amp;gt;&amp;amp;ndash; координат векторов (наблюдений).&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Целое положительное число &amp;lt;math&amp;gt;k, k \le n&amp;lt;/math&amp;gt; &amp;amp;ndash; количество кластеров.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Объем входных данных&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; целое число + &amp;lt;math&amp;gt;n*d&amp;lt;/math&amp;gt; вещественных чисел (при условии, что координаты &amp;amp;ndash; вещественные числа).&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Выходные данные&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; целых положительных чисел &amp;lt;math&amp;gt;L_1, ..., L_n&amp;lt;/math&amp;gt;&amp;amp;ndash; номера кластеров, соотвествующие каждому вектору (при условии, что нумерация кластеров начинается с &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;).&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Объем выходных данных&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; целых положительных чисел.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; равна &amp;lt;math&amp;gt;\frac{k*d*n*\Tau}{n*d} = k*\Tau &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; &amp;amp;ndash; число кластеров, &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; &amp;amp;ndash; число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Детерминированность |&#039;&#039;Детерминированность&#039;&#039;]] и [[глоссарий#Устойчивость |&#039;&#039;Устойчивость&#039;&#039;]] &amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Алгоритм &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; является итерационным. Количество итераций алгоритма в общем случае не фиксируется и зависит от начального расположения объектов в пространстве, параметра &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, а также от начального приближения центров кластеров, &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;. В результате этого может варьироваться результат работы алгоритма. При неудачном выборе начальных параметров итерационный процесс может сойтись к локальному оптимуму&amp;lt;ref&amp;gt;Von Luxburg, Ulrike. Clustering Stability. Now Publishers Inc, 2010.&amp;lt;/ref&amp;gt;. По этим причинам алгоритм не является ни &amp;lt;b&amp;gt;детермирированным&amp;lt;/b&amp;gt;, ни &amp;lt;b&amp;gt;устойчивым&amp;lt;/b&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Соотношение последовательной и параллельной сложности алгоритма&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\Theta_{k-means}}{\Psi_{k-means}} = \frac{O(\Tau*k*d*n)}{O(\Tau*log(k*d*n))}&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;:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Сравнительно высокая эффективность при простоте реализации&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Высокое качество кластеризации&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Возможность распараллеливания&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Существование множества модификаций&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Недостатки алгоритма&amp;lt;/b&amp;gt;&amp;lt;ref&amp;gt;Ortega, Joaquín Pérez, Ma Del Rocío Boone Rojas, and María J. Somodevilla. &amp;quot;Research issues on K-means Algorithm: An Experimental Trial Using Matlab.&amp;quot;&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Количество кластеров является параметром алгоритма&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Чувствительность к начальным условиям&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Инициализация центров кластеров в значительной степени влияет на результат кластеризации.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Чувствительность к выбросам и шумам&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Выбросы, далекие от центров настоящих кластеров, все равно учитываются при вычислении их центров.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Возможность сходимости к локальному оптимуму&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Итеративный подход не дает гарантии сходимости к оптимальному решению.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Использование понятия &amp;quot;среднего&amp;quot;&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Алгоритм неприменим к данным, для которых не определено понятие &amp;quot;среднего&amp;quot;, например, категориальным данным.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&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;
Исследование масштабируемости параллельной реализации алгоритма k means проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета]. Алгоритм реализован на языке C с использованием средств MPI.&lt;br /&gt;
Для исследования масштабируемости проводилось множество запусков программы с разным значением параметра (количество векторов для кластеризации), а также с различным числом процессоров. Фиксировались результаты запусков &amp;amp;ndash; время работы &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; и количество произведенных итераций алгоритма &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параметры запусков для экспериментальной оценки:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значения &amp;lt;b&amp;gt;количества векторов&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: 20&#039;000, 30&#039;000, 50&#039;000, 100&#039;000, 200&#039;000, 300&#039;000, 500&#039;000, 700&#039;000, 1&#039;000&#039;000, 1&#039;500&#039;000, 2&#039;000&#039;000.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значения &amp;lt;b&amp;gt;количества процессоров&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;: 1, 8, 16, 32, 64, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 480, 512.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значение &amp;lt;b&amp;gt;количества кластеров&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;: 100.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;Значение &amp;lt;b&amp;gt;размерности векторов&amp;lt;/b&amp;gt; &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;: 10.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для проведения экспериментов были сгенерированы нормально распределенные псевдослучайные данные (с использованием Python библиотеки [http://scikit-learn.org/ scikit-learn]):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from sklearn.datasets import make_classification&lt;br /&gt;
X1, Y1 = make_classification(n_features=10, n_redundant=2, n_informative=8,&lt;br /&gt;
                             n_clusters_per_class=1, n_classes=100,&lt;br /&gt;
                             n_samples=2000000)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Графики производительности и эффективности реализации представлены на рисунках 4 и 5 соответственно.&lt;br /&gt;
&lt;br /&gt;
[[file:Flops.png|thumb|center|800px|Рис. 4. График производительности реализации алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
[[file:Efficiency.png|thumb|center|800px|Рис. 5. График эффективности реализации алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Для заданной конфигурации эксперимента (&amp;lt;math&amp;gt;n, d, p, k&amp;lt;/math&amp;gt;) и полученных результатов (&amp;lt;math&amp;gt;t, \Tau&amp;lt;/math&amp;gt;) производительность и эффективность реализации расчитывались по формулам:&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&amp;lt;math&amp;gt;Performance = \frac{N_{k-means}^{d, n}}{t}\ \ (FLOPS),&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;N_{k-means}^{d, n}&amp;lt;/math&amp;gt; &amp;amp;ndash; точное число операций с плавающей точкой (операции с памятью, а также целочисленные операции не учитывались), вычисленное в соответствие с разделом [[#Последовательная сложность алгоритма| &amp;quot;Последовательная сложность алгоритма&amp;quot;]];&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&amp;lt;math&amp;gt;Efficiency = \frac{100 * Performance}{Performance_{Peak}^{p}}\ \ (%),&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;Performance_{Peak}^{p}&amp;lt;/math&amp;gt; &amp;amp;ndash; пиковая производительность суперкомпьютера при &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; процессорах, вычисленная согласно спецификациям Intel&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; XEON&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; X5670&amp;lt;ref&amp;gt;&amp;quot;http://ark.intel.com/ru/products/47920/Intel-Xeon-Processor-X5670-12M-Cache-2_93-GHz-6_40-GTs-Intel-QPI&amp;quot;&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;В результате&amp;lt;/b&amp;gt; экспериментальной оценки были получены следующие оценки эффективности реализации:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Минимальное&amp;lt;/b&amp;gt; значение: 0.000409 % достигается при &amp;lt;math&amp;gt;n=20&#039;000, p=480&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Максимальное&amp;lt;/b&amp;gt; значение: 0.741119 % достигается при &amp;lt;math&amp;gt;n=300&#039;000, p=1&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Оценки масштабируемости реализации алгоритма k means:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;По числу процессоров:&amp;lt;/b&amp;gt; -0.002683 &amp;amp;ndash; эффективность убывает с ростом числа процессоров. Данный результат вызван ростом накладных расходов для обеспечения параллельного выполнения алгоритма.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;По размеру задачи:&amp;lt;/b&amp;gt; 0.002779 &amp;amp;ndash; эффективность растет с ростом числа векторов. Данный результат вызван тем, что при увеличении размера задачи, количество вычислений растет по сравнению с временем, затрачиваемым на пересылку данных.&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Общая оценка:&amp;lt;/b&amp;gt; -0.000058 &amp;amp;ndash; можно сделать вывод, что в целом эффективность реализации незначительно уменьшается с ростом размера задачи и числа процессоров.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://github.com/serban/kmeans Использованная реализация алгоритма k means]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
==== Открытое программное обеспечение ====&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.icpsr.umich.edu/CrimeStat/ CrimeStat]&amp;lt;/li&amp;gt; Программное обеспечение, созданное для операционных систем Windows, предоставляющее инструменты статистического  и пространственного анализа для решения задачи картирования преступности.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://juliastats.github.io Julia]&amp;lt;/li&amp;gt; Высокоуровневый высокопроизводительный свободный язык программирования с динамической типизацией, созданный для математических вычислений, содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://mahout.apache.org Mahout]&amp;lt;/li&amp;gt; Apache Mahout - Java библиотека для работы с алгоритмами машинного обучения с использованием MapReduce. Содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.gnu.org/software/octave/ Octave]&amp;lt;/li&amp;gt; Написанная на C++  свободная система для математических вычислений, использующая совместимый с MATLAB язык высокого уровня, содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://spark.apache.org/docs/latest/mllib-clustering.html Spark]&amp;lt;/li&amp;gt; Распределенная реализация k-means содержится в библиотеке Mlib для работы с алгоритмами машинного обучения, взаимодействующая с Python библиотекой NumPy и библиотека R.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://torch.ch Torch]&amp;lt;/li&amp;gt;  MATLAB-подобная библиотека для языка программирования Lua с открытым исходным кодом, предоставляет большое количество алгоритмов для глубинного обучения и научных расчётов. Ядро написано на Си, прикладная часть выполняется на LuaJIT, поддерживается распараллеливание вычислений средствами CUDA и OpenMP. Существуют реализации k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://www.cs.waikato.ac.nz/ml/weka/ Weka]&amp;lt;/li&amp;gt;  Cвободное программное обеспечение для анализа данных, написанное на Java. Содержит k-means и x-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://accord-framework.net/docs/html/T_Accord_MachineLearning_KMeans.htm Accord.NET]&amp;lt;/li&amp;gt; C# реализация алгоритмов k-means, k-means++, k-modes.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_ml/py_kmeans/py_kmeans_opencv/py_kmeans_opencv.html OpenCV]&amp;lt;/li&amp;gt; Написанная на С++ библиотека, направленная в основном на решение задач компьютерного зрения. Содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://mlpack.org/ MLPACK]&amp;lt;/li&amp;gt; Масштабируемая С++ библиотека для работы с алгоритмами машинного обучения, содержит реализацию k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.scipy.org/ SciPy]&amp;lt;/li&amp;gt; Библиотека Python, содержит множество реализаций k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://scikit-learn.org/ scikit-learn]&amp;lt;/li&amp;gt; Библиотека Python, содержит множество реализаций k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.r-bloggers.com/k-means-clustering-in-r/ R]&amp;lt;/li&amp;gt; Язык программирования для статистической обработки данных и работы с графикой, а также свободная программная среда вычислений с открытым исходным кодом в рамках проекта GNU, содержит три реализации k-means.&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://elki.dbs.ifi.lmu.de ELKI]&amp;lt;/li&amp;gt; Java фреймворк, содержащий реализацию k-means, а также множество других алгоритмов кластеризации.&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
==== Проприетарное программное обеспечение  ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://www.ayasdi.com/blog/bigdata/topological-data-analysis-of-oil-and-gas-petrophysical-data/ Ayasdi]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://www.stata.com/features/cluster-analysis/ Stata]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://mathworld.wolfram.com/K-MeansClusteringAlgorithm.html Mathematica]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://www.mathworks.com/help/stats/kmeans.html?requestedDomain=www.mathworks.com MATLAB]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://support.sas.com/rnd/app/stat/procedures/fastclus.html SAS]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://docs.rapidminer.com/studio/operators/modeling/segmentation/k_means.html RapidMiner]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[https://blogs.sap.com/2013/03/28/sap-hana-pal-k-means-algorithm-or-how-to-do-customer-segmentation-for-the-telecommunications-industry/ SAP HANA]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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%91%D1%80%D0%BE%D1%82%D0%B8%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%94%D0%B0%D0%BD%D1%83%D1%82%D0%B0&amp;diff=17539</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%91%D1%80%D0%BE%D1%82%D0%B8%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%94%D0%B0%D0%BD%D1%83%D1%82%D0%B0&amp;diff=17539"/>
		<updated>2016-11-16T08:11:44Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нужны пояснения к рисункам 4 и 5.&lt;/div&gt;</summary>
		<author><name>Zhum</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=17538</id>
		<title>Участник:Parkhomenko/Алгоритм k средних</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:Parkhomenko/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=17538"/>
		<updated>2016-11-16T08:06:13Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм k средних&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd&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(t * \log kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(kdn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Parkhomenko|П.А.Пархоменко]] ([[#Общее описание алгоритма|1.1]], [[#Вычислительное ядро алгоритма|1.3]], [[#Последовательная сложность алгоритма|1.6]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Входные и выходные данные алгоритма|1.9]], [[#Свойства алгоритма|1.10]], [[#Масштабируемость алгоритма и его реализации|2.4]]), [[Участник:Ivan.mashonskiy|И.Д.Машонский]] ([[#Математическое описание алгоритма|1.2]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]], [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм k средних (англ. k-means) - один из алгоритмов машинного обучения, решающий задачу кластеризации. Он был изобретен в середине 1950-х математиком Гуго Штейнгаузом&amp;lt;ref&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/ref&amp;gt; и Стюартом Ллойдом&amp;lt;ref&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/ref&amp;gt;. Алгоритм стал особо популярным после публикации Маккуина&amp;lt;ref&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/ref&amp;gt;, в которой впервые был использован термин “k-means”.&lt;br /&gt;
&lt;br /&gt;
Задача алгоритма кластеризации заключается в разбиении N объектов (d-мерных векторов) на K групп (кластеров). Каждый вектор может принадлежать только одному кластеру. Количество кластеров K фиксировано, оно задается в качестве параметра.&lt;br /&gt;
&lt;br /&gt;
K-means является итерационным алгоритмом, разновидностью EM-алгоритма. Основная идея работы алгоритма заключается в проведении некоторого количества итераций, на каждой из которых происходит пересчет центра масс для каждого кластера, полученного на предыдущей итерации. После этого векторы перераспределяются по кластерам: вектор относится к тому кластеру, расстояние до центра масс которого минимально. Алгоритм завершает работу в том случае, если на очередной итерации центры масс кластеров не изменились.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Исходные данные: множество d-мерных векторов (наблюдений) &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;&amp;amp;nbsp;=&amp;amp;nbsp;&amp;lt;math&amp;gt;\{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&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;k&amp;lt;/math&amp;gt; непересекающихся множеств (кластеров) &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, таких что &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt; и значение &amp;lt;math&amp;gt;\sum_{i=1}^{k} \sum_{\mathbf x \in S_i} \left\| \mathbf x - \mu_i \right\|^2 &amp;lt;/math&amp;gt; — минимально для всех возможных наборов &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
Под &amp;lt;math&amp;gt;\mu_i&amp;lt;/math&amp;gt; здесь подразумевается центр масс векторов из множества &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Первоначальные значения центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt; определяются в начале работы алгоритма случайным образом среди векторов множества &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм заключается в попеременном применении двух шагов &amp;lt;ref&amp;gt;MacKay, David (2003). &amp;quot;Chapter 20. An Example Inference Task: Clustering&amp;quot; (PDF). Information Theory, Inference and Learning Algorithms. Cambridge University Press. pp. 284–292.&amp;lt;/ref&amp;gt;: &#039;&#039;&#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;S_i^{(t)}&amp;lt;/math&amp;gt;: &lt;br /&gt;
::&amp;lt;math&amp;gt;S_i^{(t)} = \big \{ x_p : \big \| x_p - \mu^{(t)}_i \big \|^2 \le \big \| x_p - \mu^{(t)}_j \big \|^2 \ \forall j, 1 \le j \le k \big\},&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс&#039;&#039;&#039;: на данном шаге вычисляются новые центры масс для кластеров, полученных на предыдущем шаге.&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм завершается, когда на очередном шаге не происходит изменения центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательного алгоритма k-means состоит из двух операций, повторяющихся на каждой итерации алгоритма:&lt;br /&gt;
* пересчета центров масс кластеров;&lt;br /&gt;
* соотнесения каждого вектора к одному из кластеров.&lt;br /&gt;
&lt;br /&gt;
Пересчет центра масс кластера заключается в нахождении d-мерного вектора, являющегося средним арифметическим d-мерных векторов, принадлежащих кластеру. Эту операцию необходимо выполнять для каждого из K кластеров.&lt;br /&gt;
&lt;br /&gt;
Для соотнесения вектора к одному из кластеров, необходимо для каждого из N вектора посчитать расстояние до центров масс всех K кластеров. Для нахождения расстояния между двумя d-мерными векторами p и q используется Евклидова метрика: &amp;lt;math&amp;gt;\sqrt{\sum_{k=1}^d (p_k-q_k)^2}&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;&lt;br /&gt;
&lt;br /&gt;
В начале алгоритма осуществляется инициализация центров масс &amp;lt;math&amp;gt;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;, которая может быть реализована двумя возможными способами &amp;lt;ref&amp;gt;Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot; (PDF). Proceedings of the eleventh international conference on Information and knowledge management (CIKM).&amp;lt;/ref&amp;gt;: &lt;br /&gt;
* случайный выбор центров масс среди исходных наблюдений &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* все наблюдения случайным образом распределяются по кластерам &amp;lt;math&amp;gt;S_1, S_2, ..., S_k&amp;lt;/math&amp;gt;, а затем для каждого кластера выполняется операция обновления центра масс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Распределение векторов по кластерам&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге распределения векторов по кластерам производится операция нахождения квадратов расстояний между векторами кластера и центром масс кластера: &lt;br /&gt;
::&amp;lt;math&amp;gt;\rho = \big \| x_p - \mu^{(t)}_i \big \|^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Обновление центров масс кластеров&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На шаге обновления центров масс производится операция суммирования векторов кластера:&lt;br /&gt;
::&amp;lt;math&amp;gt;\mu^{(t+1)}_i = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &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;\mu_1^{(1)}, ..., \mu_k^{(1)}&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;t := 1&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого вектора &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;m = \underset{j \in \{1, 2, ..., k\}} {\operatorname{arg\,min}} \big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt;, &amp;lt;br/&amp;gt;вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; распределяется в кластер &amp;lt;math&amp;gt;S^{(t)}_m&amp;lt;/math&amp;gt;&lt;br /&gt;
# Для каждого &amp;lt;math&amp;gt;j = 1, 2, ..., k&amp;lt;/math&amp;gt; вычисляется &amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt;&lt;br /&gt;
#  Если &amp;lt;math&amp;gt;\mu^{(t)}_j = \mu^{(t+1)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt;, завершить выполнение алгоритма, иначе &amp;lt;math&amp;gt;t := t + 1&amp;lt;/math&amp;gt;, переход на шаг 3.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для вычисления центра массы кластера, содержащего &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; d-мерных векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n_i * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения центров масс всех k кластеров требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
* &amp;lt;math&amp;gt;k * d&amp;lt;/math&amp;gt; операций деления&lt;br /&gt;
&lt;br /&gt;
Для определения расстояния между двумя d-мерными векторами, требуется:&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;d - 1&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Для определения кластера для n d-мерного векторов, необходимо:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * d&amp;lt;/math&amp;gt; операций умножения&lt;br /&gt;
* &amp;lt;math&amp;gt;n * k * (d - 1)&amp;lt;/math&amp;gt; операций сложения&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность одной итерации:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n * k * d)&amp;lt;/math&amp;gt; операций умножения/деления&lt;br /&gt;
&lt;br /&gt;
Сложность всего алгоритма, состоящего из &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; итераций: &amp;lt;math&amp;gt;O(t * n * k * d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе описывается информационный граф алгоритма k-средних при &amp;lt;math&amp;gt;n = 3, k = 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 показана общая структура алгоритма: в качестве входных данных выступает матрица, строками которой являются входные векторы &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt;, на выходе получается массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; чисел, где элемент с номером &amp;lt;math&amp;gt;i = 1, 2, 3&amp;lt;/math&amp;gt; обозначает номер кластера, которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. На шаге &amp;lt;math&amp;gt;init&amp;lt;/math&amp;gt; происходит инициализация значений центров масс кластеров, после этого осуществляется итеративное повторение этапов распределения векторов по кластерам (&amp;lt;math&amp;gt;as&amp;lt;/math&amp;gt;) и обновления значений центров масс кластеров (&amp;lt;math&amp;gt;up&amp;lt;/math&amp;gt;). Когда на очередном шаге обновления центров масс кластеров значения центров масс не изменяются, алгоритм завершает работу, и в качестве выходных данных выступают номера кластеров для каждого из входных векторов.&lt;br /&gt;
[[file:k_means_main.png|thumb|center|400px|Рисунок 1. Общая схема работы алгоритма k-средних]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Распределение векторов по кластерам.&#039;&#039;&#039; На рисунке 2 изображен информационный граф для этапа распределения векторов по кластерам. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; и текущие значения центров масс кластеров &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt; (вычисленные на этапе инициализации или предшествующего этапа обновления центров масс кластеров). В качестве выходных данных выступают числа &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_i, i = 1, 2, 3&amp;lt;/math&amp;gt; - номер кластера, содержащего вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; обозначены операции векторной разности, вершинами &amp;lt;math&amp;gt;sn&amp;lt;/math&amp;gt; - операция вычисления квадрата нормы вектора, вершинами &amp;lt;math&amp;gt;am&amp;lt;/math&amp;gt; - операция &amp;lt;math&amp;gt;\underset{i \in \{1, 2\}} {\operatorname{arg\,min}}  p_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k_means_info_graph_as.png|thumb|center|400px|Рисунок 2. Граф алгоритма для этапа распределения векторов по кластерам]]&lt;br /&gt;
&#039;&#039;&#039;Обновление центров масс кластеров.&#039;&#039;&#039; На рисунке 3 представлен информационный граф для этапа обновления центров масс кластеров. В качестве входных данных выступают строки входной матрицы (входные векторы) &amp;lt;math&amp;gt;x_1, x_2, x_3&amp;lt;/math&amp;gt; с соответствующими номерами кластеров &amp;lt;math&amp;gt;m_1, m_2, m_3&amp;lt;/math&amp;gt;, содержащих эти векторы. В качестве выходных данных здесь выступают обновленные значения центров масс кластеров: &amp;lt;math&amp;gt;\mu_1, \mu_2&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;s_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции суммирования векторов, принадлежащих кластеру &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометками &amp;lt;math&amp;gt;cs_i, i = 1, 2&amp;lt;/math&amp;gt; обозначены операции вычисления выражения &amp;lt;math&amp;gt;1 / |S_i|&amp;lt;/math&amp;gt;, где за &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt; обозначен кластер с номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;. Вершинами с пометкой &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; обозначена операция произведения.&lt;br /&gt;
[[file:k_means_info_graph_up.png|thumb|center|400px|Рисунок 3. Граф алгоритма для этапа обновления центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм кластеризации k средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (распределения векторов по кластерам и обновления центров масс кластеров) лежат циклы, итерации которых являются информационно независимыми.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
В силу особенностей алгоритма k средних порядок выполнения основных этапов существенен, что не позволяет выполнять их параллельно. Таким образом, итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;T = t * (T_{as} + T_{up})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T_{as}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;T_{up}&amp;lt;/math&amp;gt; - параллельная сложность этапа обновления центров масс кластеров, &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - общее число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа распределения векторов по кластерам с учетом доступности неограниченного числа процессоров вычисляется следующим образом: так как вычисление значений &amp;lt;math&amp;gt;\big \| x_i - \mu^{(t)}_j \big \|^2&amp;lt;/math&amp;gt; для каждой пары векторов &amp;lt;math&amp;gt;x_i, i = 1, 2, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^{(t)}_j, j = 1, 2, ..., k&amp;lt;/math&amp;gt; можно выполнять независимо и для вычисления этого выражения требуется &amp;lt;math&amp;gt;O(\log d)&amp;lt;/math&amp;gt; операций в соответствии с [[Нахождение_частных_сумм_элементов_массива_сдваиванием | параллельной реализацией нахождения сумм элементов массива сдваиванием]]. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log k)&amp;lt;/math&amp;gt;. Таким образом, сложность этапа распределения векторов по кластерам &amp;lt;math&amp;gt;O(\log kd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность этапа обновления центров масс кластеров вычисляется следующим образом: так как обновление значений каждого из центров масс кластеров можно выполнять независимо, параллельная сложность определяется сложностью обновления центра масс самого большого кластера. В худшем случае для нахождения значения выражения &amp;lt;math&amp;gt;\mu^{(t+1)}_j = \frac{1}{|S^{(t)}_i|} \sum_{x_j \in S^{(t)}_i} x_j &amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
С учетом вышесказанного, итоговая параллельная сложность алгоритма k средних определяется выражением &amp;lt;math&amp;gt;O(t * \log kdn)&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;k&amp;lt;/math&amp;gt; - количество кластеров;&lt;br /&gt;
* матрица координат векторов &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; размерностью &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных&#039;&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; вещественных чисел (если координаты вещественные числа), &amp;lt;math&amp;gt;1&amp;lt;/math&amp;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;
&#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;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности алгоритма равно &amp;lt;math&amp;gt;\frac{O(tnkd)}{O(t * \log kdn)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Число операций алгоритма k средних оценивается как &amp;lt;math&amp;gt;O(tnkd)&amp;lt;/math&amp;gt;, суммарный объем входных и выходных данных равен &amp;lt;math&amp;gt;n(d+1)&amp;lt;/math&amp;gt;. Следовательно, вычислительная мощность алгоритма линейна по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и линейна по &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить следующие ключевые особенности алгоритма k средних:&lt;br /&gt;
* Алгоритм прост в реализации и имеет высокую скорость работы по сравнению с другими алгоритмами кластеризации (при удачном выборе начальных значений центров масс кластеров)&lt;br /&gt;
* В качестве метрики используется евклидово расстояние, а в качестве меры разброса кластера используется дисперсия&lt;br /&gt;
* Количество кластеров k является входным параметром алгоритма, поэтому неправильный выбор этого параметра может негативно повлиять на качество кластеризации&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;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Исследование масштабируемости алгоритма k-means в зависимости от количества используемых процессов было проведено в статье Кумара&amp;lt;ref&amp;gt;Kumar, J., Mills, R. T., Hoffman, F. M., &amp;amp; Hargrove, W. W. (2011). Parallel k-means clustering for quantitative ecoregion delineation using large data sets. Procedia Computer Science, 4, 1602-1611.&amp;lt;/ref&amp;gt;. Исследование происходило на суперкомпьютере Jaguar - Cray XT5&amp;lt;ref&amp;gt;https://www.top500.org/system/176029&amp;lt;/ref&amp;gt;. На момент экспериментов данный суперкомпьютер имел следующую конфигурацию: 18,688 вычислительных узлов с двумя шестнадцатиядерными процессорами AMD Opteron 2435 (Istanbul) 2.6 GHz, 16 GB of DDR2-800 оперативной памяти, и SeaStar 2+ роутер. Всего он состоял из 224,256 вычислительных ядер, 300 TB памяти, и пиковой производительностью 2.3 petaflops.&lt;br /&gt;
&lt;br /&gt;
Реализация алгоритма была выполнена на языке программирования C с использованием MPI.&lt;br /&gt;
&lt;br /&gt;
Объем данных составлял 84 гигабайта, количество объектов (d-мерных векторов) n равнялось 1,024,767,667, размерность векторов d равнялась 22, количество кластеров k равнялось 1000. &lt;br /&gt;
&lt;br /&gt;
На рисунке 4 показана зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров. Можно отметить, что время, затраченное на чтение данных и запись результатов кластеризации, практически не изменяется с увеличением количества задействованных процессоров. Время же работы самого алгоритма кластеризации уменьшается с увеличением количества процессоров.&lt;br /&gt;
[[Файл:k-means-proc-scalability.png|thumb|center|700px|Рисунок 4. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества используемых процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Ниже представлены результаты исследования времени работы алгоритма k-means в зависимости от количества кластеров k и количества векторов n. Для исследования использовалась реализация k-means на языке программирования C (с использованием OpenMP). Тестирование происходило на 8 процессорах. Затраченное время считалось как среднее арифметическое 5 запусков алгоритма.&lt;br /&gt;
&lt;br /&gt;
На рисунке 5 показана зависимость времени работы алгоритма от количества кластеров k. Количество векторов n равно 100000.&lt;br /&gt;
[[Файл:K-means-k.png|thumb|center|700px|Рисунок 5. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества кластеров k.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 показана зависимость времени работы алгоритма от количества векторов n. Количество кластеров k равно 160.&lt;br /&gt;
[[Файл:K-means-n.png|thumb|center|700px|Рисунок 6. Зависимости времени работы алгоритма кластеризации k-means в зависимости от количества векторов n.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие реализации алгоритма k-средних:&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации, в том числе &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++, содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [https://github.com/JuliaStats/Clustering.jl Julia k-means] реализация алгоритма &#039;&#039;k&#039;&#039;-средних на языке программирования Julia.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма &#039;&#039;k&#039;&#039;-средних на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма &#039;&#039;k&#039;&#039;-средних.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций &#039;&#039;k&#039;&#039;-средних на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация &#039;&#039;k&#039;&#039;-средних на языке R.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Ivan.mashonskiy&amp;diff=17537</id>
		<title>Обсуждение участника:Ivan.mashonskiy</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:Ivan.mashonskiy&amp;diff=17537"/>
		<updated>2016-11-16T08:05:52Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет текста программы&lt;br /&gt;
* нужны пояснения к рисункам 5 и 6.&lt;/div&gt;</summary>
		<author><name>Zhum</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:IanaV&amp;diff=17535</id>
		<title>Обсуждение участника:IanaV</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:IanaV&amp;diff=17535"/>
		<updated>2016-11-16T07:57:16Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет описания или ссылки на аппаратную платформу&lt;br /&gt;
* нет окончательного текста программы&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 10:57, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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:IanaV/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_means&amp;diff=17534</id>
		<title>Участник:IanaV/Алгоритм k means</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:IanaV/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_k_means&amp;diff=17534"/>
		<updated>2016-11-16T07:57:09Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
Авторы страницы: [[Участник:IanaV|Валуйская Я.А.]], [[Участник:GlotovES|Глотов Е.С.]]&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;p&amp;gt;Алгоритм &amp;lt;i&amp;gt;k-means&amp;lt;/i&amp;gt; (&amp;lt;i&amp;gt;k средних&amp;lt;/i&amp;gt;) - один из наиболее популярных алгоритмов кластеризации. Наиболее простой, но в то же время достаточно неточный метод кластеризации в классической реализации.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм был разработан в 1956 году математиком Гуго Штейнгаузом &amp;lt;ref&amp;gt;[http://www.laurent-duval.eu/Documents/Steinhaus_H_1956_j-bull-acad-polon-sci_division_cmp-k-means.pdf Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.]&amp;lt;/ref&amp;gt;, и почти одновременно его изобрел Стюарт Ллойд &amp;lt;ref&amp;gt;[http://www-evasion.imag.fr/people/Franck.Hetroy/Teaching/ProjetsImage/2007/Bib/lloyd-1982.pdf Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.]&amp;lt;/ref&amp;gt;. Особую популярность алгоритм снискал после работы Джеймса Маккуина &amp;lt;ref&amp;gt;[https://pdfs.semanticscholar.org/a718/b85520bea702533ca9a5954c33576fd162b0.pdf MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.]&amp;lt;/ref&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм кластеризации &amp;lt;i&amp;gt;k-means&amp;lt;/i&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;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Алгоритм k-means является итерационным алгоритмом кластеризации. На каждой итерации происходит пересчет центра масс для каждого кластера, полученного на предыдущем шаге, затем исходные векторы заново распределяются по кластерам в соответствии с тем, какой из новых центров оказался ближе по выбранной метрике. Алгоритм завершается, когда на какой-то итерации не происходит изменения кластеров.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Исходные данные:&#039;&#039; &lt;br /&gt;
* множество наблюдений &amp;lt;math&amp;gt;X = \{x_1, x_2, ..., x_n\}&amp;lt;/math&amp;gt;, где каждое наблюдение &amp;lt;math&amp;gt;x_i \in R^d, i = 1, ..., n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* количество кластеров &amp;lt;math&amp;gt;k \in N, k \leq n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Обозначения:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;S = \{S_1, S_2, ..., S_k \} &amp;lt;/math&amp;gt; - множество кластеров, которые удовлетворяют следующим условиям:&lt;br /&gt;
** &amp;lt;math&amp;gt;S_i \bigcap S_j = \emptyset, i \neq j&amp;lt;/math&amp;gt;; &lt;br /&gt;
** &amp;lt;math&amp;gt;X = {\bigcup \limits _{i = 1}^k S_i} &amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;\mu_i \in R^d, i = 1, ..., k&amp;lt;/math&amp;gt; - центр масс кластера &amp;lt;math&amp;gt;S_i&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;d(u, v)&amp;lt;/math&amp;gt; - расстояние между векторами &amp;lt;math&amp;gt;u \in R^m, v \in R^m&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;L = (l_1, l_2, ..., l_n)&amp;lt;/math&amp;gt; - набор меток, где метка &amp;lt;math&amp;gt;l_i \in N_{[1, k]}&amp;lt;/math&amp;gt; - является порядковым номером кластера, к которому принадлежит вектор &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt; x_i \in S_{l_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;Цель алгоритма k-means&#039;&#039; - распределить наблюдения из входного множества &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;S = \{S_1, S_2, ..., S_k \} &amp;lt;/math&amp;gt; таким образом, чтобы сумма квадратов расстояний от каждой точки кластера до его центра масс по всем кластерам была минимальной: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\arg\min_{S} \sum_{i=1}^{k} \sum_{x \in S_i} (d(x, \mu_i))^2 &amp;lt;/math&amp;gt;,&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Алгоритм состоит из следующих шагов:&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;i&amp;gt;Инициализация центров масс&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге задаются начальные значения центров масс &amp;lt;math&amp;gt; \mu_1^1, ..., \mu_k^1&amp;lt;/math&amp;gt;. Существует несколько способов их выбора. Они будут рассмотрены ниже.&lt;br /&gt;
# &amp;lt;i&amp;gt;t-ый шаг итерации:&amp;lt;/i&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге каждый вектор &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; распределяется в свой кластер &amp;lt;math&amp;gt;S_j^t&amp;lt;/math&amp;gt; так, что: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;l_i^t = \arg \min_{j} (d(x_i, \mu_j^t))^2 , j = 1, ..., k; i = 1, ..., n&amp;lt;/math&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге происходит пересчет центров масс кластеров, полученных на предыдущем этапе: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;Критерий останова&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^t = \mu_i^{t+1},&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Способы инициализации начальных данных:&amp;lt;/i&amp;gt;&lt;br /&gt;
# Метод Forgy &amp;lt;br&amp;gt; Случайным образом выбираются &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества наблюдений &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;. Эти вектора используются в качестве центров масс кластеров &amp;lt;math&amp;gt;\mu_1, ..., \mu_k&amp;lt;/math&amp;gt;&lt;br /&gt;
# Метод случайного разбиения (Random Partition) &amp;lt;ref&amp;gt;[http://people.csail.mit.edu/tieu/notebook/kmeans/15_p600-hamerly.pdf Hamerly, G.; Elkan, C. (2002). &amp;quot;Alternatives to the k-means algorithm that find better clusterings&amp;quot;. Proceedings of the eleventh international conference on Information and knowledge management (CIKM).]&amp;lt;/ref&amp;gt; &amp;lt;br&amp;gt; Каждый вектор &amp;lt;math&amp;gt;x_i, i = 1, .., n&amp;lt;/math&amp;gt; случайным образом распределяется в кластер &amp;lt;math&amp;gt;S_j, j = 1, .., k&amp;lt;/math&amp;gt;, затем для каждого кластера вычисляется его центр масс &amp;lt;math&amp;gt;\mu_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительном ядром алгоритма является шаг 2, состоящий из следующих этапов:&lt;br /&gt;
# распределение векторов по кластерам;&lt;br /&gt;
# пересчет центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; заключается в следующем: для каждого вектора &amp;lt;math&amp;gt;x_i \in X, i = 1, ..., n&amp;lt;/math&amp;gt; необходимо посчитать расстояние между этим вектором и центром масс кластера &amp;lt;math&amp;gt;\mu_j^t, j = 1, ..., k&amp;lt;/math&amp;gt;. Следовательно, на каждой итерации необходимо выполнить &amp;lt;math&amp;gt;n * k&amp;lt;/math&amp;gt; операций вычисления расстояния между векторами. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; заключается в следующем: для каждого кластера &amp;lt;math&amp;gt;S_i^t \in S, i = 1, ..., k&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;
&lt;br /&gt;
# Макрооперация &amp;lt;i&amp;gt;&amp;quot;Расстояние между векторами&amp;quot;&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; В данном алгоритме используется Евклидова метрика:  &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;v \in R^m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;u \in R^m: d(v, u) = \sqrt{\sum_{j=1}^{m} (v_j - u_j)^2} &amp;lt;/math&amp;gt;;&lt;br /&gt;
# Макрооперация &amp;lt;i&amp;gt;&amp;quot;Пересчет центра масс кластера&amp;quot;&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Таким образом, алгоритм в терминах макроопераций выглядит следующим образом:&amp;lt;/p&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;Инициализация центров масс&amp;lt;/i&amp;gt; &amp;lt;p&amp;gt;Метод Forgy: центры масс &amp;lt;math&amp;gt;\mu_j^1, j = 1, ..., k&amp;lt;/math&amp;gt; выбираются случайным образом.&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;Метод случайного разбиения: вектора &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; случайным образом распределяются по кластерам. Для каждого кластера выполняется макрооперация &amp;quot;Пересчет центра масс кластера&amp;quot; для нахождения &amp;lt;math&amp;gt;\mu_j^1&amp;lt;/math&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;
# &amp;lt;i&amp;gt;t-ый шаг итерации:&amp;lt;/i&amp;gt;&lt;br /&gt;
#* &amp;lt;i&amp;gt;Распределение векторов по кластерам&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге для каждого вектора &amp;lt;math&amp;gt;x_i \in X&amp;lt;/math&amp;gt; k раз выполняется макрооперация &amp;quot;Расстояние между векторами&amp;quot; для нахождения расстояния между &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu_j^t, j = 1, ..., k&amp;lt;/math&amp;gt;. Затем для каждого вектора &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; находится минимальное расстояние.&lt;br /&gt;
#* &amp;lt;i&amp;gt;Пересчет центров масс кластеров&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt;На данном шаге для каждого кластера выполняется макрооперация &amp;quot;Пересчет центра масс&amp;quot; для нахождения &amp;lt;math&amp;gt;\mu_j^{t+1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;i&amp;gt;Критерий останова&amp;lt;/i&amp;gt; &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;\mu_i^t = \mu_i^{t+1},&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i = 1, ..., k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Псевдокод алгоритма&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;b&amp;gt;Входные данные&amp;lt;/b&amp;gt;  : Множество векторов &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;, количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;lt;b&amp;gt;Выходные данные&amp;lt;/b&amp;gt; : Набор меток &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; принадлежности к кластеру&lt;br /&gt;
 1  Инициализация центров масс &amp;lt;math&amp;gt;\mu_i^1, i = 1, ..., k&amp;lt;/math&amp;gt;;&lt;br /&gt;
 2  t := 1;&lt;br /&gt;
 3  Для каждого вектора &amp;lt;math&amp;gt;x_i \in X, i = 1, ..., n:&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;l_i^t = \arg \min_{j} (d(x_i,\mu_j^t))^2 , j = 1, ..., k&amp;lt;/math&amp;gt;;&lt;br /&gt;
 4  Для каждого кластера &amp;lt;math&amp;gt;S_i^t, i = 1, ..., k&amp;lt;/math&amp;gt; выполняется макрооперация &amp;lt;i&amp;gt;&amp;quot;Пересчет центра масс&amp;quot;&amp;lt;/i&amp;gt;;&lt;br /&gt;
 5  &amp;lt;b&amp;gt;if&amp;lt;/b&amp;gt; (&amp;lt;math&amp;gt;\exists i = 1, ..., k: \mu_i^t \neq \mu_i^{t+1}&amp;lt;/math&amp;gt;) {&lt;br /&gt;
 6      t := t + 1;&lt;br /&gt;
 7      &amp;lt;b&amp;gt;goto&amp;lt;/b&amp;gt; 3;&lt;br /&gt;
 8  } &amp;lt;b&amp;gt;else&amp;lt;/b&amp;gt; {&lt;br /&gt;
 9      &amp;lt;b&amp;gt;stop&amp;lt;/b&amp;gt;;&lt;br /&gt;
 10 };&lt;br /&gt;
&lt;br /&gt;
Существующие способы оптимизации алгоритма &amp;lt;ref&amp;gt;[http://link.springer.com/chapter/10.1007/3-540-45643-0_13  Phillips S. J. Acceleration of k-means and related clustering algorithms //Workshop on Algorithm Engineering and Experimentation. – Springer Berlin Heidelberg, 2002. – С. 166-177.]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.187.3017&amp;amp;rep=rep1&amp;amp;type=pdf  Hamerly G. Making k-means Even Faster //SDM. – 2010. – С. 130-140.]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;[http://cseweb.ucsd.edu/~elkan/kmeansicml03.pdf  Elkan C. Using the triangle inequality to accelerate k-means //ICML. – 2003. – Т. 3. – С. 147-153.]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Подсчитаем количество операций для каждого шага алгоритма.&lt;br /&gt;
&lt;br /&gt;
# Инициализация:&lt;br /&gt;
#* Метод Forgy: &amp;lt;math&amp;gt;N_{init} = 0&amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метод случайного разбиения: &lt;br /&gt;
#** Количество операций деления: &amp;lt;math&amp;gt;N^{/}_{init} = k * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#** Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{init} = d * (n - k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Распределение &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов &amp;lt;math&amp;gt;x_i \in R^d&amp;lt;/math&amp;gt; по &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластерам на одной итерации:&lt;br /&gt;
#* Количество операций умножения: &amp;lt;math&amp;gt;N^{*}_{distr} = k * n * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{distr} = k * n * (d - 1)&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций вычитания: &amp;lt;math&amp;gt;N^{-}_{distr} = k * n * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций взятия минимума: &amp;lt;math&amp;gt;N^{min}_{distr} = n * (k - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Пересчет центра масс &amp;lt;math&amp;gt;\mu_i \in R^d&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров на одной итерации:&lt;br /&gt;
#* Количество операций деления: &amp;lt;math&amp;gt;N^{/}_{update} = k * d&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* Количество операций сложения: &amp;lt;math&amp;gt;N^{+}_{update} = d * (n - k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Следовательно, в случае если алгоритм сошелся за &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, получаем:&lt;br /&gt;
* Операций сложения/вычитания: &amp;lt;math&amp;gt;N^{+,-}_{k-means} = N^{+}_{init} + i*(N^{+}_{distr} + N^{-}_{distr} + N^{+}_{update}) = d * (n - k) + i*(k*n*(d-1)+k*n*d+d*(n-k)) \thicksim O(i*k*n*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Операций умножения/деления: &amp;lt;math&amp;gt;N^{*,/}_{k-means} = N^{/}_{init} + i*(N^{*}_{distr} + N^{/}_{update}) = k * d + i*(k*n*d+k*d) \thicksim O(i*k*n*d)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, [[глоссарий#Последовательная сложность|&#039;&#039;последовательная сложность&#039;&#039;]] алгоритма k-means для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерных векторов и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров за &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, требуемых для сходимости алгоритма:  &amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;math&amp;gt;O(i * n * k * d)&amp;lt;/math&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф алгоритма k-means.&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 1&amp;lt;/i&amp;gt; представлена общая схема алгоритма k-means, согласно разделу [[#Схема реализации последовательного алгоритма|&amp;quot;Схема реализации последовательного алгоритма&amp;quot;]]. &lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Init&amp;lt;/i&amp;gt; - этап инициализации.&lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Distribute&amp;lt;/i&amp;gt; - этап распределения векторов по кластерам.&lt;br /&gt;
Вершина &amp;lt;i&amp;gt;Update&amp;lt;/i&amp;gt; - этап пересчета центров масс кластеров.&lt;br /&gt;
[[file:k-means-all.PNG|800px|thumb|center|Рисунок 1. Общая схема алгоритма k-means]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 2&amp;lt;/i&amp;gt; представлен информационный граф этапа распределения векторов по кластерам. На вход данному этапу подают множество векторов &amp;lt;math&amp;gt;x_1, x_2, ..., x_n&amp;lt;/math&amp;gt; из входных данных и множество центров масс кластеров &amp;lt;math&amp;gt;\mu^t_1, ..., \mu^t_k&amp;lt;/math&amp;gt;, полученных с шага инициализации или с предыдущей итерации. Для каждой пары векторов &amp;lt;math&amp;gt;x_i, \mu^t_j, i = 1, ..., n; j = 1,..., k&amp;lt;/math&amp;gt; считается расстояние между ними (вершина &amp;lt;i&amp;gt;dist&amp;lt;/i&amp;gt;). Информационный граф вычисления расстояния между двумя векторами рассмотрен ниже (&amp;lt;i&amp;gt;рисунок 3&amp;lt;/i&amp;gt;). Затем выполняется операция нахождения минимума (вершина &amp;lt;i&amp;gt;min&amp;lt;/i&amp;gt;), в результате которой каждому вектору &amp;lt;math&amp;gt;x_i, i = 1, ..., n&amp;lt;/math&amp;gt; приписывается метка &amp;lt;math&amp;gt;l^t_i&amp;lt;/math&amp;gt;, равная порядковому номеру кластера &amp;lt;math&amp;gt;S^t_j, j = 1 ..., k&amp;lt;/math&amp;gt;, которому принадлежит данный вектор: &amp;lt;math&amp;gt;x_i \in S^t_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k-means-distribute.PNG|800px|thumb|center|Рисунок 2. Схема этапа распределения векторов по кластерам]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 3&amp;lt;/i&amp;gt; представлен информационный граф вычисления расстояния между двумя векторами &amp;lt;math&amp;gt;x_i = (x_{i1}, ..., x_{id}), i = 1, ..., n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\mu^t_j = (\mu^t_{j1}, ..., \mu^t_{jd}) , j = 1, ..., k&amp;lt;/math&amp;gt;. Выполняется операция поэлементного вычитания двух векторов (вершины &amp;lt;i&amp;gt;-&amp;lt;/i&amp;gt;), затем возведение в квадрат (вершины &amp;lt;i&amp;gt;sqr&amp;lt;/i&amp;gt;), затем суммирование полученных результатов (вершина &amp;lt;i&amp;gt;+&amp;lt;/i&amp;gt;). На выходе получаем квадрат расстояния между векторами.&lt;br /&gt;
[[file:K-means-distance.PNG|800px|thumb|center|Рисунок 3. Схема вычисления расстояния между двумя векторами]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;i&amp;gt;рисунке 4&amp;lt;/i&amp;gt; представлен информационный граф этапа пересчета центров масс кластеров &amp;lt;math&amp;gt;\mu^t_1, \mu^t_2,..., \mu^t_k&amp;lt;/math&amp;gt;. Для каждого кластера &amp;lt;math&amp;gt;S^t_j, j = 1, ..., k&amp;lt;/math&amp;gt; выполняется операция суммирования (вершина &amp;lt;math&amp;gt;sum_j&amp;lt;/math&amp;gt;) векторов, которые принадлежат данному кластеру. Кроме того, для каждого кластера выполняется операция определения количества элементов в нем (вершина &amp;lt;math&amp;gt;size_j&amp;lt;/math&amp;gt;). Затем выполняется операция деления (вершина &amp;lt;i&amp;gt;/&amp;lt;/i&amp;gt;). На выходе получаем новые центры масс кластеров &amp;lt;math&amp;gt;\mu^{t+1}_1, \mu^{t+1}_2,..., \mu^{t+1}_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
[[file:k-means-update.PNG|800px|thumb|center|Рисунок 4. Схема этапа пересчета центров масс кластеров]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма имеет большие возможности для параллелизма:&lt;br /&gt;
* Распределение векторов по кластерам: вектора &amp;lt;math&amp;gt;x_i, x_j, i \neq j; i, j = 1, ..., n&amp;lt;/math&amp;gt; могут быть распределены по кластерам независимо друг от друга;&lt;br /&gt;
* Пересчет центров масс кластеров: центры масс кластеров &amp;lt;math&amp;gt;\mu_i, \mu_j, i \neq j; i, j = 1, ..., k&amp;lt;/math&amp;gt; могут быть пересчитаны независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
Однако, этап пересчета центров масс кластеров зависит от этапа распределения векторов по кластерам, так что эти этапы должны выполняться строго последовательно. Таким образом, параллельная сложность алгоритма может быть вычислена следующим образом: &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;N_{k-means} = i * (N_{distr} + N_{update})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - количество итераций, необходимых для сходимости алгоритма, &amp;lt;math&amp;gt;N_{distr}&amp;lt;/math&amp;gt; - параллельная сложность этапа распределения векторов по кластерам, &amp;lt;math&amp;gt;N_{update}&amp;lt;/math&amp;gt; - параллельная сложность этапа пересчета центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассчитаем параллельную сложность &amp;lt;math&amp;gt;N_{distr}&amp;lt;/math&amp;gt; этапа &amp;lt;i&amp;gt;распределения векторов по кластерам&amp;lt;/i&amp;gt; в предположении наличия неограниченного числа процессоров для одной итерации. Для каждой пары векторов &amp;lt;math&amp;gt;(x_i, \mu_j), i = 1,...,n; j = 1, ..., k&amp;lt;/math&amp;gt; операция вычисления расстояния выполняется независимо.&lt;br /&gt;
* При выполнении операция вычисления расстояния операции вычитания и умножения выполняются независимо друг от друга, поэтому требуется только одно вычитание и одно умножение. Число операций сложения: &amp;lt;math&amp;gt;N^{+}_{distr} = O(log(d))&amp;lt;/math&amp;gt; согласно [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритму нахождения частичной суммы элементов массива путем сдваивания]]; &lt;br /&gt;
* Для нахождения минимума среди &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов требуется &amp;lt;math&amp;gt;O(log(k))&amp;lt;/math&amp;gt; операций сравнения.&lt;br /&gt;
Таким образом, получаем, что &amp;lt;math&amp;gt;N_{distr} = O(log(k*d))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Рассчитаем параллельную сложность &amp;lt;math&amp;gt;N_{update}&amp;lt;/math&amp;gt; этапа &amp;lt;i&amp;gt;пересчета центров масс кластеров&amp;lt;/i&amp;gt; в предположении наличия неограниченного числа процессоров для одной итерации. Нахождение центра масс &amp;lt;math&amp;gt;\mu_j, j = 1, ..., k&amp;lt;/math&amp;gt; кластера &amp;lt;math&amp;gt;S_j&amp;lt;/math&amp;gt; может выполняться независимо от нахождения центров масс других кластеров. Таким образом, получаем, что &amp;lt;math&amp;gt;N_{update} \leq O(log(n))&amp;lt;/math&amp;gt; в худшем случае, когда почти все векторы находятся в одном кластере. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итоговая параллельная сложность алгоритма: &amp;lt;br&amp;gt; &amp;lt;math&amp;gt;N_{k-means} \leq i * (log(k*d) + log(n)) \thicksim O(i * log(k*d*n)); i&amp;lt;/math&amp;gt; - количество итераций, необходимых для сходимости.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Входные данные&amp;lt;/i&amp;gt;:&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов &amp;lt;math&amp;gt;x_i \in R^d, i = 1, ..., n&amp;lt;/math&amp;gt;;&lt;br /&gt;
* число кластеров &amp;lt;math&amp;gt;k \in N_{[1,n]}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Объем входных данных&amp;lt;/i&amp;gt;:&lt;br /&gt;
&amp;lt;math&amp;gt;n * d&amp;lt;/math&amp;gt; действительных чисел, 1 целое положительное число&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Выходные данные&amp;lt;/i&amp;gt;:&lt;br /&gt;
* вектор меток &amp;lt;math&amp;gt;L \in N^n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Объем выходных данных&amp;lt;/i&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;
# Число кластеров является входным аргументом алгоритма. Таким образом, некорректный выбор данного параметра может привести к плохим результатам работы алгоритма, поэтому зачастую проводят несколько прогонов алгоритма с разными значениями &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, чтобы подобрать оптимальный;&lt;br /&gt;
# Алгоритм может сойтись к локальному минимуму (достижение глобального минимума не гарантируется);&lt;br /&gt;
# Результат сильно зависит от выбора начальных значений центров масс кластеров. Существует улучшенная версия алгоритма - &amp;lt;b&amp;gt;k-means++ &amp;lt;/b&amp;gt;&amp;lt;ref&amp;gt;[http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf Arthur, D.; Vassilvitskii, S. (2007). &amp;quot;k-means++: the advantages of careful seeding&amp;quot;. Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms. Society for Industrial and Applied Mathematics Philadelphia, PA, USA. pp. 1027–1035.]&amp;lt;/ref&amp;gt;, которая предлагает свой способ нахождения начальных оптимальных значений центров масс кластеров;&lt;br /&gt;
# Чувствительность к выбросам и шумам, так как они учитываются при вычислении центров масс кластеров.&lt;br /&gt;
&lt;br /&gt;
;[[глоссарий#Устойчивость|&#039;&#039;Устойчивость&#039;&#039;]]: Алгоритм является устойчивым к погрешностям во входных данных, так как при вычислении центров кластеров расстояния между объектами усредняются, что приводит к уменьшению ошибки. Алгоритм также является устойчивым к погрешностям, допускаемых при вычислениях, так как операция сложения на каждой новой итерации выполняется над входными данными. &lt;br /&gt;
&lt;br /&gt;
;Сбалансированность: &amp;lt;p&amp;gt;Множество входных векторов можно равномерно разделить между параллельными вычислителями. Центры всех кластеров можно распространить на все вычислители (так как их заведомо меньше, чем число входных векторов). В таком случае на каждой итерации определение для каждого объекта ближайшего кластера представляет собой вычисление расстояния от каждого входного вектора, находящегося на вычислителе, до каждого кластера, и является сбалансированной операцией. После данного этапа происходит пересчёт центров кластеров. Данная операция является несбалансированной в случаях, когда число  объектов в одном из кластеров значительно больше числа объектов в других кластерах, из-за чего один кластер занимает большое число вычислителей, а остальные занимают по одному или несколько вычислителей. В таком случае для данного кластера потребуется вычислить центр [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритмом нахождения частичной суммы элементов массива путем сдваивания]]. Данная операция представляет несбалансированность порядка &amp;lt;math&amp;gt;O(log (k*n))&amp;lt;/math&amp;gt;. После данного шага происходит сбалансированная операция рассылки центров кластеров на все вычислители.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;В идеальном случае параллелизации алгоритм не является сбалансированным, однако в реальных задачах, когда &amp;lt;math&amp;gt;n &amp;gt;&amp;gt; p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - количество вычислителей, эта несбалансированность сглаживается наличием объектов из каждого кластера на каждом вычислителе&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Соотношение последовательной и параллельной сложности: &amp;lt;math&amp;gt;{O(n*d*k*i) \over O(log(n*d*k)*i)} \thicksim O \bigg( {n*d*k \over log(n*d*k)} \bigg)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]: &amp;lt;p&amp;gt;Последовательный алгоритм: &amp;lt;math&amp;gt;{O(n*d*k*i) \over n*d+n+1} \thicksim {O(d*k*i) \over d+1} \thicksim O(k*i)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число входных векторов, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; - размерность векторов, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - число кластеров, &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - число итераций, требуемое для сходимости&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Параллельный алгоритм: &amp;lt;math&amp;gt;O(log(n*d*k)*i) \over n*d+n+1&amp;lt;/math&amp;gt;&amp;lt;/p&amp;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;
==== Локальность реализации алгоритма ====&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;
&amp;lt;p&amp;gt;Набор данных для тестирования состоял из 946000 векторов размерности 2 (координаты на сфере)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Набор и границы значений изменяемых параметров запуска реализации алгоритма:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* число процессов (виртуальных ядер) [8 : 512];&lt;br /&gt;
* число кластеров [128 : 384].&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон эффективности реализации алгоритма:&lt;br /&gt;
&lt;br /&gt;
* минимальная эффективность реализации &amp;lt;math&amp;gt;2,47%&amp;lt;/math&amp;gt; достигается при делении исходных данных на 128 кластеров с использованием 512 процессов;&lt;br /&gt;
* максимальная эффективность реализации &amp;lt;math&amp;gt;7,13%&amp;lt;/math&amp;gt; достигается при делении исходных данных на 352 кластера с использованием 8 процессов.&lt;br /&gt;
На следующих рисунках приведены графики производительности и эффективности выбранной реализации &amp;lt;b&amp;gt;k means&amp;lt;/b&amp;gt; в зависимости от изменяемых параметров запуска.&lt;br /&gt;
[[file:kmeans_performance.jpg|thumb|center|720px|Рисунок 5. Параллельная реализация k means. Максимальная производительность. ]]&lt;br /&gt;
[[file:kmeans_efficiency.jpg|thumb|center|720px|Рисунок 6. Параллельная реализация k means. Максимальная эффективность. ]]&lt;br /&gt;
Исследовалась [https://github.com/serban/kmeans параллельная реализация алгоритма k means]. Для вычисления производительности в неё был вписан код на MPI, реализующий подсчёт числа операций с плавающей точкой.&lt;br /&gt;
&amp;lt;p&amp;gt;Были получены следующие оценки масштабируемости реализации алгоритма k means:&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;i&amp;gt;По числу процессов:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;-0.02209&amp;lt;/math&amp;gt;. Следовательно, с ростом числа процессов эффективность уменьшается.&lt;br /&gt;
*&amp;lt;i&amp;gt;По размеру задачи:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;0.01252&amp;lt;/math&amp;gt;. Следовательно, с ростом размера задачи (числа кластеров) эффективность увеличивается. &lt;br /&gt;
*&amp;lt;i&amp;gt;Общая оценка:&amp;lt;/i&amp;gt; &amp;lt;math&amp;gt;-0.00081&amp;lt;/math&amp;gt;. Таким образом, с ростом и размера задачи, и числа процессов эффективность уменьшается.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существуют следующие Open Source реализации алгоритма:&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de/ ELKI] - содержит реализацию алгоритма k-means на языке Java (в том числе реализацию улучшенного алгоритма k-means++)&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka/ Weka] - содержит реализацию k-means на языке Java&lt;br /&gt;
* [http://mahout.apache.org/ Apache Mahout] - содержит реализацию k-means в парадигме MapReduce&lt;br /&gt;
* [https://spark.apache.org/ Spark Mllib] - содержит распределенную реализацию k-means&lt;br /&gt;
* [http://accord-framework.net/ Accord.NET] - содержит реализацию k-means на C# (в том числе реализацию улучшенного алгоритма k-means++)&lt;br /&gt;
* [https://github.com/mlpack/mlpack MLPACK] - содержит реализацию k-means на языке C++&lt;br /&gt;
* [http://opencv.org/ OpenCV] - содержит реализацию k-means на C++. А также есть обертки для языков Python и Java&lt;br /&gt;
* [https://www.scipy.org/ SciPy] - содержит реализацию k-means на языке Python&lt;br /&gt;
* [http://scikit-learn.org/ Scikit-learn] - содержит реализацию k-means на языке Python&lt;br /&gt;
* [http://julialang.org/ Julia] - содержит реализацию алгоритма k-means на языке Julia&lt;br /&gt;
* [https://www.gnu.org/software/octave/ Octave] - содержит реализацию k-means на языке Octave&lt;br /&gt;
* [https://www.r-project.org/ R] - содержит реализацию k-means на языке R&lt;br /&gt;
* [http://torch.ch/ Torch] - содержит реализацию k-means на языке Lua&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:Guryanovak/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=17533</id>
		<title>Участник:Guryanovak/Алгоритм кластеризации, основанный на минимальном остовном дереве</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:Guryanovak/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=17533"/>
		<updated>2016-11-16T07:45:08Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Авторы описания: [[Участник:guryanovak|Гурьянов Алексей Константинович]], [[Участник:Валерия Кибитова|Кибитова Валерия Николаевна]]&lt;br /&gt;
&lt;br /&gt;
Оба автора внесли в данную статью равноценный вклад. Сбор информации, построение алгоритмов, а также составление данной статьи происходило совместно обоими авторами, четкого распределения ответственности произвести невозможно.&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на минимальном остовном дереве&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N^2 \log_2 N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O(Nm) &amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &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;
&lt;br /&gt;
В отличии от классических алгоритмов кластеризации, данный алгоритм не предполагает, что кластеры являются сферической формы, и способен выделять кластеры произвольной формы. Данный алгоритм применяется в кластеризации цветов в различных изображениях, например, таких как: фотографии, карты, коммерческой рекламе, а также в различных биологических изображениях.&lt;br /&gt;
&lt;br /&gt;
Проблема нахождения минимального остовного дерева, а также ее решение было предложено Борувка в 1926 году. Идея использовать минимальное остовное дерево для кластеризации впервые была предложена Zahn в 1971&amp;lt;ref&amp;gt;Zahn C. T. Graph-theoretical methods for detecting and describing gestalt clusters //IEEE Transactions on computers. – 1971. – Т. 100. – №. 1. – С. 68-86.&amp;lt;/ref&amp;gt;, в последующие годы данная идея развивалась такими учеными как: Asano, Bhattacharya, Keil, и Yao&amp;lt;ref&amp;gt;Asano T. et al. Clustering algorithms based on minimum and maximum spanning trees //Proceedings of the fourth annual symposium on Computational geometry. – ACM, 1988. – С. 252-257.&amp;lt;/ref&amp;gt;, Avis&amp;lt;ref&amp;gt;Avis D. Diameter partitioning //Discrete &amp;amp; Computational Geometry. – 1986. – Т. 1. – №. 3. – С. 265-276.&amp;lt;/ref&amp;gt;, Eldershaw и Hegland&amp;lt;ref&amp;gt;Eldershaw C., Hegland M. Cluster analysis using triangulation //Computational Techniques and Applications. – 1997. – С. 201-208.&amp;lt;/ref&amp;gt;, Paivinen и другими&amp;lt;ref&amp;gt;Grygorash O., Zhou Y., Jorgensen Z. Minimum spanning tree based clustering algorithms //2006 18th IEEE International Conference on Tools with Artificial Intelligence (ICTAI&#039;06). – IEEE, 2006. – С. 73-81.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основная идея алгоритма состоит из следующих шагов:&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;
В минимальном остовном дереве, выбирается &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; ребро (где &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; – это заранее определенное число кластеров), имеющие наибольшой вес. Выбранные ребра удаляются. Оставшиеся k компонент связности считаются кластерами.&lt;br /&gt;
&lt;br /&gt;
В данной статье алгоритм будет описываться с использованием алгоритма Борувки. Подробное описание которого можно найти пройдя по ссылке [[https://algowiki-project.org/ru/Алгоритм_Борувки Алгоритм Борувки]]&lt;br /&gt;
&lt;br /&gt;
Общая идея его состоит в следующем:&lt;br /&gt;
#Изначально каждая вершина графа — тривиальное дерево, а ребра не принадлежат никакому дереву.&lt;br /&gt;
#Для каждого дерева найдем минимальное по весу инцидентное ему ребро. Добавим все такие ребра.&lt;br /&gt;
#Повторяем шаг 2 пока в графе не останется только одно дерево.&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;m&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;(a_{i,1}, a_{i,2}, ..., a_{i,m})&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,m], i \in [1..N] &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;N&amp;lt;/math&amp;gt; чисел от &amp;lt;math&amp;gt;1&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;
&lt;br /&gt;
* &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин.&lt;br /&gt;
* &amp;lt;math&amp;gt;G = (V,E) &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;
* &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt; (Minimum Spanning Tree) - минимальное остовное дерево графа &amp;lt;math&amp;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;
* &amp;lt;math&amp;gt; | x , y | = \sqrt{\sum_i^m (x_i - y_i)^2} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Работу алгоритма можно разделить на три блока:&lt;br /&gt;
*1: Вычисление расстояний между вершинами и заполнение данных о компонентах связности&lt;br /&gt;
*2: Построение минимального основного дерева&lt;br /&gt;
*3: Разделение на K кластеров&lt;br /&gt;
&lt;br /&gt;
Из них основное время работы алгоритма занимают блоки 1 и 2.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
В алгоритме используются следующие структуры данных:&lt;br /&gt;
* Node – структура данных, описывающая вершину пространства. Включает в себя значения координат точки.&lt;br /&gt;
* Edge – структура данных, описывающая узел между двумя вершинами. Включает в себя индексы двух вершин и расстояние между ними&lt;br /&gt;
* Components – класс, описывающий состояние компонент связности в промежуточных состояниях графа по мере добавления в него ребер. Включает в себя информацию о том, какой компоненте связности принадлежит каждая точка и о расстоянии между каждой парой компонент связности, которая равна минимальному ребру между двумя вершинами в этих двух компонентах.&lt;br /&gt;
&lt;br /&gt;
Для описания алгоритма вводятся следующие макрооперации:&lt;br /&gt;
*1: getDistance – находит расстояние между двумя вершинами графа как расстояние между соответствующими им точками пространства. (Сложность – &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*2: initializeComponents – инициализирует состояние класса Components информацией о вершинах и ребрах. В каждую исходную компоненту связности входит одна вершина. (Сложность – &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*3: findMinimalOutgoingEdge – находит ребро c наименьшей длиной, исходящее из заданной компоненты связности. (Cложность – &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*4: findComp  – возвращает индекс компоненты связности в которой находится вершина (Cложность – &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;)&lt;br /&gt;
*5: connectComponentsWithEdge – соединяет две компоненты связности, связанные поданным на вход ребром. Объединение компонент включает в себя замену ребер инцидентности новой компоненты на минимальные ребра из пары объединяемых компонент. (Сложность – &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Макрооперации 2-5 относятся к последовательной реализации [[Алгоритм Борувки|алгоритма Борувки]], который был выбран как основа вычислительного ядра алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Схема работы алгоритма была создана с учетом последующей параллельной реализации,&amp;lt;ref&amp;gt;Chung S., Condon A. Parallel implementation of Bouvka&#039;s minimum spanning tree algorithm //Parallel Processing Symposium, 1996., Proceedings of IPPS&#039;96, The 10th International. – IEEE, 1996. – С. 302-308.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
int N;        // Размер входных данных&lt;br /&gt;
int number_of_clusters;        // Число кластеров&lt;br /&gt;
int size = N*N;        // Размер матрицы инцидентности&lt;br /&gt;
Node nodes[N];        // Вершины(точки), заданные начальными условиями&lt;br /&gt;
Edge edges[size];        // Все расстояния между вершинами &lt;br /&gt;
vector&amp;lt;Edge&amp;gt; tree;        // Рёбра минимального остовного дерева&lt;br /&gt;
Components comps;         // Структура компонент связности&lt;br /&gt;
read_data(&amp;amp;nodes, file_name);    // Считывание начальных данных&lt;br /&gt;
&lt;br /&gt;
//----------------Вычисление расстояний между вершинами----------------//&lt;br /&gt;
for(int i = 0; i &amp;lt; N-1; i++) {&lt;br /&gt;
    for(int j = i+1; j &amp;lt; N; j++) {&lt;br /&gt;
        double distance = getDistance(node[i], node[j]);        // Вычисление расстояний между вершинами&lt;br /&gt;
	edges[i*N + j] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
        edges[j*N + i] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//----------------Заполнение данных о компонентах связности--------------//&lt;br /&gt;
initializeComponents(&amp;amp;comps, nodes, edges); &lt;br /&gt;
&lt;br /&gt;
//----------------Построение минимального основного дерева--------------//&lt;br /&gt;
while(comps.size &amp;gt; 1) {&lt;br /&gt;
    vector&amp;lt;edges&amp;gt; mstedges;&lt;br /&gt;
    for component in comps {&lt;br /&gt;
        edge e = findMinimalOutgoingEdge(component, comps, edges) // Нахождение минимального ребра инцидентного данной компоненте&lt;br /&gt;
        mstedges.add(e)&lt;br /&gt;
    }&lt;br /&gt;
    for mstedge in mstedges {&lt;br /&gt;
        if (findComp(comps, mstedge.first) != findComp(comps, mstedge.second)) { &lt;br /&gt;
             connectComponentsWithEdge(comps, mstedge) // Объединение двух компонент&lt;br /&gt;
             tree.add(mstedge) // Добавление ребра в минимальное основное дерево&lt;br /&gt;
        }&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//-----------------------Разделение на K кластеров--------------------------//&lt;br /&gt;
sort(tree)&lt;br /&gt;
for(int i = tree.size() - 1; i &amp;gt; tree.size() - 1 - number_of_clusters; i--) {&lt;br /&gt;
    remove_edge(tree, i);        // Удаление самых длинных рёбер из MST - формирование кластеров&lt;br /&gt;
}&lt;br /&gt;
write_data(tree);        // Запись выходных данных&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Среди выделенных в описании частей алгоритма, самую большую сложность для оценивания временной сложности вызывает часть 3, являющаяся реализацией алгоритма Борувки. Однако если заметить, что после выполнения каждого тела внешнего цикла количество компонент связности будет уменьшаться как минимум вдвое (так как одно ребро в списке ребер минимального основного дерева может повториться максимум двое), то можно сделать вывод, что сложность в худшем случае этого блока алгоритма является &amp;lt;math&amp;gt; O (N^2 \log_2 N) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Асимптотическую сложность алгоритма можно выразить как сумму временных сложностей четырех выделенных в описании частей:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; TC(N, m) = O(N^2m) + O(N^2) + O(N^2 \log_2 N) + O(N \log_2 N) = O(N^2 (m + \log_2 N)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если посчитать размерность пространства входных точек за константу при расчете сложности, то временная сложность выразится как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; TC(N, m) = O(N^2 \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;
[[Файл:InformationGraph-2-8.png|frame|center|alt=Алгоритм| Информационный граф параллельного алгоритма кластеризации на основе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
В то время как узлы каждого слоя не зависят информационно от каждого узла предыдущего слоя, из-за того что структура информационного графа определяется динамически по ходу действия программы (на основе выбора ребер, по которым будет проводится слияние компонент связности), составить информационный граф, использующий более разреженную структуру связей между слоями, не представляется возможным.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Данная задача хорошо параллелизуется благодаря тому, что наиболее требующие вычислительных ресурсов блоки алгоритмического ядра алгоритма можно эффективно разделить по процессам. Процесс считывания входных данных и процесс расчета расстояния между точками, которые представляет собой большое количество попарных взятий евклидова расстояния, можно выполнить за &amp;lt;math&amp;gt;O(1)&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(1)&amp;lt;/math&amp;gt; времени при наличии как минимум &amp;lt;math&amp;gt;N^3&amp;lt;/math&amp;gt; процессоров. Процесс обновления информации о компонентах связности, который заключается в обновлении индексов кластеров вершин и в обновлении расстояний между кластерами, можно представить в виде независимой последовательности операций взятия минимума и операций присваивания в количестве  &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; штук (количество компонент связности), что позволит выполнить ярус, ответственный за обновление структуры компонент связности за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; при наличие как минимум &amp;lt;math&amp;gt;2N&amp;lt;/math&amp;gt; процессоров.  Это значит, что каждый ярус будет выполняться за &amp;lt;math&amp;gt; O(1) &amp;lt;/math&amp;gt; времени, и аналогично последовательному алгоритму Борувки, после выполнения 2 ярусов количество компонент связности уменьшится минимум вдвое, что гарантирует то, что за время выполнения алгоритма повторится не более чем &amp;lt;math&amp;gt; 4 * \log_2 N &amp;lt;/math&amp;gt; ярусов. Учитывая ярусы, посвященные считыванию ввода, вычислению расстояний между точками пространства, удалению &amp;lt;math&amp;gt;K-1&amp;lt;/math&amp;gt; ребер из минимального опорного дерева и выводу ответа, итоговую параллельная сложность алгоритма можно оценить как &amp;lt;math&amp;gt; 2 * \log_2 N + 5&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; векторов размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt;(a_{i,1}, a_{i,2}, ..., a_{i,m})&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,m], i \in [1..N] &amp;lt;/math&amp;gt; и количество кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;, на которые необходимо разбить множество точек. Общий объем входных данных можно оценить как &amp;lt;math&amp;gt;O(Nm)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если оценивать размерность пространства входных точек константой, то объем входных данных можно оценить как &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;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;1&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; \frac{N^2 \log_2 N} {\log_2 N} = N^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вычислительную мощность алгоритма можно оценить как &amp;lt;math&amp;gt; \frac {N^2 \log_2 N} {N}   = N \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;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Существующих реализаций алгоритма кластеризации в настоящий момент немного. Ниже приводится список некоторых из них:&lt;br /&gt;
*[https://github.com/jakevdp/mst_clustering MST Clustering Python]&lt;br /&gt;
Однако существует множество реализаций алгоритма поиска минимального остовного дерева, на основе которых можно реализовать алгоритм кластеризации.&lt;br /&gt;
*[https://github.com/umarali/boruvka-mst-parallel  Parallel Implementation of Borůvka&#039;s MST Algorithm ]&lt;br /&gt;
*[http://www.mlpack.org/docs/mlpack-2.0.2/doxygen.php?doc=namespacemlpack_1_1emst.html  С++ mlpack MST Algorithm ]&lt;br /&gt;
*[https://github.com/nastra/algorithms-java Java Graph Algorithms]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Kruskal Java MST Algorithm ]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Prim Java MST Algorithm ]&lt;br /&gt;
*[https://github.com/dineshappavoo/GraphMinimumSpanningTree Java Graph Minimum Spanning Tree ]&lt;br /&gt;
*[https://pypi.python.org/pypi/PyAlgDat Python PyAlgDat ]&lt;br /&gt;
*[https://networkx.github.io/documentation/networkx-1.10/reference/algorithms.mst.html Python NetworkX Minimum Spanning Tree]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_53_0/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html C++ Boost Minimum Spanning Tree]&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Guryanovak&amp;diff=17532</id>
		<title>Обсуждение участника:Guryanovak</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:Guryanovak&amp;diff=17532"/>
		<updated>2016-11-16T07:44:56Z</updated>

		<summary type="html">&lt;p&gt;Zhum: Содержимое страницы заменено на «п. 2.4 отсутствует ~~~~»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4 отсутствует&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 10:44, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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:Tereshinvs&amp;diff=17531</id>
		<title>Обсуждение участника:Tereshinvs</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:Tereshinvs&amp;diff=17531"/>
		<updated>2016-11-16T07:43:42Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет текста программы&lt;br /&gt;
* нет полного описания аппаратной платформы (ОС, размер памяти)&lt;br /&gt;
* нет пояснений к графику&lt;/div&gt;</summary>
		<author><name>Zhum</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:Tereshinvs/MST_Clusterization&amp;diff=17530</id>
		<title>Участник:Tereshinvs/MST Clusterization</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:Tereshinvs/MST_Clusterization&amp;diff=17530"/>
		<updated>2016-11-16T07:43:38Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&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; V = \left\{ v_1, v_2, \ldots, v_N \right\} &amp;lt;/math&amp;gt; в некотором метрическом пространстве с метрикой &amp;lt;math&amp;gt; \rho(x, y) &amp;lt;/math&amp;gt;&lt;br /&gt;
# Матрица отношений между объектами: симметричная матрица вещественных чисел размером &amp;lt;math&amp;gt; N \times N &amp;lt;/math&amp;gt; с неотрицательными элементами с нулевой диагональю.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается постановка с известным количеством кластеров &amp;lt;math&amp;gt; K &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается алгоритм алгоритм кластеризации на основе MST (Minimum spanning tree — минимальное остовное дерево).&lt;br /&gt;
&lt;br /&gt;
Минимальное остовное дерево (или минимальное покрывающее дерево) в связанном взвешенном неориентированном графе — это остовное дерево этого графа, имеющее минимальный возможный вес, где под весом дерева понимается сумма весов входящих в него рёбер.&lt;br /&gt;
&lt;br /&gt;
Общая схема алгоритма:&lt;br /&gt;
# Построение минимального остовного дерева одним из известных методов (алгоритмы Крускала, Прима, Борувки и их комбинации; наиболее пригодным для параллельного исполнения считается алгоритм Борувки)&lt;br /&gt;
# Удаление &amp;lt;math&amp;gt; K-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;
Пусть &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; — множество объектов, &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — множество номеров (имён, меток) кластеров. Задана функция расстояния между объектами &amp;lt;math&amp;gt;\rho(x,y)&amp;lt;/math&amp;gt;. Требуется разбить выборку на непересекающиеся подмножества, называемые &#039;&#039;кластерами&#039;&#039;, так, чтобы каждый кластер состоял из объектов, близких по метрике &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt;, а объекты разных кластеров существенно отличались. При этом каждому объекту &amp;lt;math&amp;gt;v_i\in V&amp;lt;/math&amp;gt;&lt;br /&gt;
приписывается номер кластера &amp;lt;math&amp;gt;y_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Алгоритм кластеризации&#039;&#039; — это функция &amp;lt;math&amp;gt;a\colon V\to Y&amp;lt;/math&amp;gt;, которая любому объекту &amp;lt;math&amp;gt;v\in V&amp;lt;/math&amp;gt; ставит в соответствие номер кластера &amp;lt;math&amp;gt;y\in Y&amp;lt;/math&amp;gt;. Множество &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; в некоторых случаях известно заранее, однако чаще ставится задача определить оптимальное число кластеров, с точки зрения того или иного &#039;&#039;критерия качества&#039;&#039; кластеризации.&lt;br /&gt;
&lt;br /&gt;
В данном случае задачу можно сформулировать так: выбрать пороговое значение веса ребра &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt;, так чтобы объекты, соединённые ребром весом меньше &amp;lt;math&amp;gt;W&amp;lt;/math&amp;gt; лежали в одном кластере, а соединённые ребром весом больше &amp;lt;math&amp;gt;W&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;
# Добавление найденных наименьших вершин в MST&lt;br /&gt;
# &amp;quot;Склейка&amp;quot; компонент связности MST&lt;br /&gt;
# Нахождение веса &amp;lt;math&amp;gt;W_{K-1}&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;(K-1)&amp;lt;/math&amp;gt;-ого наибольшего ребра&lt;br /&gt;
# Удаление рёбер весом не меньше чем &amp;lt;math&amp;gt;W_{K-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot; line start=&amp;quot;0&amp;quot; highlight=&amp;quot;1,9&amp;quot;&amp;gt;&lt;br /&gt;
  Ввод: граф V&lt;br /&gt;
&lt;br /&gt;
  Поместить каждую вершину в отдельное дерево леса T&lt;br /&gt;
  Пока в T больше одной компоненты связности:&lt;br /&gt;
    Для каждой компоненты C из T:&lt;br /&gt;
      S — пустое множество рёбер&lt;br /&gt;
      Для каждой вершины v в C:&lt;br /&gt;
        Найти кратчайшее ребро с одним концом v, а другим не из C, и поместить его в S&lt;br /&gt;
      Добавить кратчайшее ребро из S в T&lt;br /&gt;
    Объединить компоненты связности&lt;br /&gt;
  T — MST&lt;br /&gt;
  &lt;br /&gt;
  Найти W — вес (K-1)-ого максимального ребра в T&lt;br /&gt;
  Удалить из T рёбра с весом большим или равным W&lt;br /&gt;
&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;
&amp;lt;!--# Для приближённого построения триангуляции Делонэ существуют алгоритмы разделяй-и-властвуй, имеющие гарантированную сложность &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;gt;&lt;br /&gt;
# Нахождение ребер наименьшего веса для всех компонент связности требует &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; операций на каждом шаге, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; — число рёбер в графе.&lt;br /&gt;
# Добавление новых рёбер в MST требует &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; операций за всё время работы алгоритма.&lt;br /&gt;
# При использовании структуры данных система непересекающихся множеств с ранговой эвристикой, объединение деревьев требует примерно , где &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
# Доказано, что количество шагов алгоритма &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итого &amp;lt;math&amp;gt;O(V \log N)&amp;lt;/math&amp;gt; операций. При использовании триангуляции Делонэ в некоторых случаях &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; имеет тот же порядок, что и &amp;lt;math&amp;gt;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^2 \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удаление &amp;lt;math&amp;gt;K-1&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(N^2 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[File:Tereshinvs_mst_scheme.png|frame|center|alt=Информационный граф|Информационный граф алгоритма кластеризации на основе минимального остовного дерева]]&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины находятся в своих собственных деревьях (стадия вершин). Затем с помощью нахождения кратчайших рёбер, соединяющих разные поддеревья, происходит слияние поддеревьев (стадия объединения деревьев) до тех пор, пока не останется одно дерево, являющееся минимальным остовным деревом исходного графа. После этого нужно будет лишь удалить нужное количество рёбер (выделение кластеров).&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Основным местом для распараллеливания алгоритма является поиск наименьшего ребра из одной компоненты связности в другие. Так как компоненты связности в данном случае независимы, то отдельные узлы вычислительной системы могут выполнять поиск наименьших рёбер независимо.&lt;br /&gt;
&lt;br /&gt;
Узким местом является нахождение k-ой порядковой статистики для выделения кластеров, а так же объединение компонент связности. Но всего объединений компонент связности &amp;lt;math&amp;gt;N-1&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; N &amp;lt;/math&amp;gt; (количество элементов рассматриваемого множества) и матрица вещественных чисел размера &amp;lt;math&amp;gt; N \times N &amp;lt;/math&amp;gt; отношений между элементами этого множества. Матрица предполагается симметричной, а её диагональные элементы равными нулю, поэтому её размер можно оценить как &amp;lt;math&amp;gt; \tfrac{N(N-1)}{2} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Кроме того, имеется натуральное число &amp;lt;math&amp;gt; K \leqslant 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; 1 &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;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
С использованием библиотеки Boost::graph::distributed программу можно реализовать следующим образом (указана только часть построения MST):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot; line start=&amp;quot;0&amp;quot; highlight=&amp;quot;1,9&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/use_mpi.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/mpi_process_group.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/adjacency_list.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/distributed/vertex_list_adaptor.hpp&amp;gt;&lt;br /&gt;
#include &amp;lt;boost/graph/metis.hpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;fstream&amp;gt;&lt;br /&gt;
#include &amp;lt;vector&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef boost::adjacency_list&amp;lt;&lt;br /&gt;
	boost::listS,&lt;br /&gt;
	boost::distributedS&amp;lt;&lt;br /&gt;
		boost::graph::distributed::mpi_process_group,&lt;br /&gt;
		boost::vecS&lt;br /&gt;
	&amp;gt;,&lt;br /&gt;
	boost::undirectedS,&lt;br /&gt;
	boost::no_property,&lt;br /&gt;
	boost::property&amp;lt;&lt;br /&gt;
		boost::edge_weight_t,&lt;br /&gt;
		float&lt;br /&gt;
	&amp;gt;&lt;br /&gt;
&amp;gt; Graph;&lt;br /&gt;
&lt;br /&gt;
typedef boost::graph_traits&amp;lt;Graph&amp;gt;::edge_descriptor Edge;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[])&lt;br /&gt;
{&lt;br /&gt;
	boost::mpi::environment env(argc, argv);&lt;br /&gt;
&lt;br /&gt;
	std::ifstream in(&amp;quot;big.graph&amp;quot;);&lt;br /&gt;
	boost::graph::metis_reader reader(in);&lt;br /&gt;
&lt;br /&gt;
	Graph graph(&lt;br /&gt;
		reader.begin(), reader.end(),&lt;br /&gt;
		reader.weight_begin(),&lt;br /&gt;
		reader.num_vertices()&lt;br /&gt;
	);&lt;br /&gt;
&lt;br /&gt;
	typedef boost::property_map&amp;lt;Graph, boost::edge_weight_t&amp;gt;::type WeightMap;&lt;br /&gt;
	WeightMap weight_map = boost::get(boost::edge_weight, graph);&lt;br /&gt;
&lt;br /&gt;
	std::vector&amp;lt;Edge&amp;gt; mst_edges;&lt;br /&gt;
&lt;br /&gt;
	boost::graph::distributed::dense_boruvka_minimum_spanning_tree(&lt;br /&gt;
            boost::graph::make_vertex_list_adaptor(graph),&lt;br /&gt;
	    weight_map,&lt;br /&gt;
	    std::back_inserter(mst_edges)&lt;br /&gt;
	);&lt;br /&gt;
&lt;br /&gt;
	if (process_id(graph.process_group()) == 0)&lt;br /&gt;
            std::cout &amp;lt;&amp;lt; &amp;quot;Finished&amp;quot; &amp;lt;&amp;lt; std::endl;&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;
В этом параграфе описывается масштабируемость параллельной реализации алгоритма из библиотеки Boost::graph::distributed.&lt;br /&gt;
&lt;br /&gt;
К сожалению, не удалось запустить программу, использующую данную библиотеку на суперкомпьютере IBM BlueGene/P, поэтому замеры производительности производились на хилом процессоре Intel Core i5 4210U с двумя ядрами и четырьмя потоками.&lt;br /&gt;
&lt;br /&gt;
[[File:boost_mst_time.png|frame|center|alt=Время работы|Время работы алгоритма построения MST на полном графе из 5000 вершин]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Библиотека шаблонов для С++ Boost предлагает прямую параллельную реализацию алгоритма Борувки в компоненте graph/distributed. Кроме этого, в ней имеются реализации других алгоритмов построения MST, а также много других параллельных алгоритмов на графах. Библиотека основана на технологии MPI. IBM предоставил патч для Boost 1.47.0 для её использования с компиляторами IBM CL C/C++.&lt;br /&gt;
&lt;br /&gt;
Кроме этой библиотеки не существует распространённой библиотеки, реализующей параллельный алгоритм построения минимального остовного дерева. Но имеется достаточно большое количество библиотек, реализующих однопоточное построение MST:&lt;br /&gt;
* JGraphT для Java&lt;br /&gt;
* SciPy MST&lt;br /&gt;
* множество любительских реализаций, который можно легко найти в интернете по запросу &amp;quot;mst algorithm&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации, http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf&lt;br /&gt;
# Minimum spanning tree, https://en.wikipedia.org/wiki/Minimum_spanning_tree&lt;br /&gt;
# Алгоритм Борувки, https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm&lt;br /&gt;
# Euclidean minimum spanning tree, https://en.wikipedia.org/wiki/Euclidean_minimum_spanning_tree&lt;br /&gt;
# Система непересекающихся множеств, http://e-maxx.ru/algo/dsu&lt;br /&gt;
# Boost Library, http://www.boost.org/&lt;br /&gt;
# Параллельное построение MST в библиотеке Boost, http://www.boost.org/doc/libs/1_55_0/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#id14&lt;br /&gt;
# Патч Boost 1.47.0 для IBM CL C/C++, http://www-01.ibm.com/support/docview.wss?uid=swg27027469&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=17529</id>
		<title>Участник:Sarseev/Алгоритм кластеризации, основанный на минимальном покрывающем дереве</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:Sarseev/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BF%D0%BE%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=17529"/>
		<updated>2016-11-16T07:30:51Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Арсеев С. П., группа 620&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Данный алгоритм нацелен на решение задачи кластеризации, то есть, разделения множества объектов на группы, называемые кластерами, так, чтобы внутри одного кластера оказались наиболее похожие (по некоторой метрике) друг на друга объекты, а объекты из разных кластеров как можно сильнее отличались друг от друга.&lt;br /&gt;
Сам алгоритм состоит из следующих шагов:&lt;br /&gt;
# Сведение набора объектов к графу. Объекты становятся вершинами графа, и каждые две вершины соединяются ребром, длина которого равняется расстоянию между этими объектами по некоторой метрике. Выбор метрики зависит от предметной области и желаемых свойств алгоритма. Например, метрикой может служить евклидово расстояние.&lt;br /&gt;
# Построение минимального покрывающего (остовного) дерева в этом графе. Более подробно этот шаг описан в [[Построение_минимального_остовного_дерева_(MST)|соответствующей статье]].&lt;br /&gt;
# Кластеризация путём удаления некоторого количества рёбер в этом дереве. В случае, если заранее известно количество кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, достаточно удалить &amp;lt;math&amp;gt;k-1&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;n&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, которые нужно кластеризовать. Каждый такой объект можно рассматривать как вектор или элемент некоторого метрического пространства. На пространстве, которому принадлежат &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;, задана метрика &amp;lt;math&amp;gt;d_{ij}=d(a_i,a_j)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Первый шаг алгоритма строит граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин графа &amp;lt;math&amp;gt;(|V|=n)&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество его рёбер &amp;lt;math&amp;gt;(|E| = \frac{n*(n-1)}{2})&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
На втором этапе строится минимальное покрывающее дерево &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt;. После удаления рёбер дерево распадается на поддеревья &amp;lt;math&amp;gt;MST_i&amp;lt;/math&amp;gt;. Вершины, входящие в &amp;lt;math&amp;gt;i&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;
# Преобразование исходных данных. Сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск минимального покрывающего дерева. Последовательная сложность всех трёх основных алгоритмов ([[Алгоритм_Борувки|Борувки]], [[Алгоритм_Крускала|Крускала]] и [[Алгоритм_Прима|Прима]]) &amp;lt;math&amp;gt;O(|E|*log(|E|)) = O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Разбиение на кластеры. Вычислительная сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; для простых стратегий разбиения.&lt;br /&gt;
Таким образом, вычислительным ядром алгоритма является алгоритм построения минимального покрывающего дерева, поскольку он имеет наибольшую сложность. Стратегии распараллеливания алгоритма основаны на распараллеливании алгоритма построения минимального покрывающего дерева.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В состав алгоритма входит алгоритм [[Построение_минимального_остовного_дерева_(MST)|построения минимального покрывающего дерева]], являющийся его вычислительным ядром. Также в отдельную часть можно выделить алгоритм разбиения получившегося покрывающего дерева на кластеры.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Описание алгоритма на псевдокоде:&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
MST_Cluster(object[n] objects){&lt;br /&gt;
    g = Pairs(objects, metric);&lt;br /&gt;
    mst = Create_MST(graph);&lt;br /&gt;
    parts = Split_MST(mst);&lt;br /&gt;
    for i in range(parts){&lt;br /&gt;
         clusters[i] = parts[i].vertices;&lt;br /&gt;
    }&lt;br /&gt;
    return clusters&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма определяется самой сложной его частью (см. пункт 1.3) и равна &amp;lt;math&amp;gt;O(n^2*log(n^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:MST_cluster.png|thumb|center|1043px|Информационный граф алгоритма. Показана последовательная реализация (большой прямоугольник) и схема распараллеливания алгоритма построения минимального покрывающего дерева.]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Ресурс параллелизма алгоритма основан на свойстве ассоциативности рёбер: &amp;lt;br&amp;gt;&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес (множество покрывающих деревьев) графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&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;
Сложность параллельного алгоритма зависит от соотношения объёма входных данных и числа процессов. Существуют распределённые алгоритмы построения минимального покрывающего дерева, имеющие асимптотическую сложность &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;(o_1, o_2, ..., o_n)&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Выходом алгоритма является множество непересекающихся кластеров: &amp;lt;math&amp;gt;C=(C_1, C_2, ..., C_k); C_i = (o_{i,1}, o_{i,2}, ..., o_{i, n_i}); o(i, l) \ne o(j, m); i \ne j; \forall l, m; \sum{n_i} = n &amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Важным свойством алгоритма является то, что при его распараллеливании по описанной выше стратегии важно правильно выбирать количество процессов. При увеличении количества процессов возрастают затраты на фазу слияния деревьев, полученных каждым процессом: растёт число этапов, в которые производится слияние, а следовательно, возрастают затраты на коммуникацию между процессами, и распределение нагрузки становится более неравномерным из-за того, что часть процессов должна проделать значительно большее количество работы по слиянию деревьев. &amp;lt;br&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма имеет порядок &amp;lt;math&amp;gt;O(n*log(n^2))&amp;lt;/math&amp;gt;. &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;
[[Файл:MST_cluster_time.png|thumb|center|600px|Время работы алгоритма в зависимости от числа процессов и размера получающегося графа.]]&lt;br /&gt;
На графике на логарифмической шкале показана зависимость времени работы алгоритма от числа процессов. Приведены измерения для трёх наборов данных, различающихся размером получаюшегося графа: 4, 8 и 30 миллиардов рёбер соответственно. Как видно из графика, при большом объёме данных алгоритм хорошо масштабируется. Однако, существует предел возможностей распараллеливания: если количество процессов будет сопоставимо с размером набора данных (не показано на графике), время работы может начать расти с увеличением количества процессов, потому что ускорение за счёт распараллеливания будет нивелироваться возрастающими затратами на обмен данными между процессами.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Основным вычислительным ядром алгоритма является алгоритм построения минимального покрывающего (остовного) дерева, применяющийся для многих задач, и многие библиотеки ограничиваются его реализацией, без реализации основанного на нём алгоритма кластеризации. Примеры реализаций алгоритмов построения MST: &lt;br /&gt;
* С++:  [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/ Boost graph library] (алгоритмы Борувки и Крускала)&lt;br /&gt;
* C++, MPI: [http://www.boost.org/doc/libs/1_62_0/libs/graph_parallel/doc/html/index.html Parallel boost graph library] (алгоритм Борувки)&lt;br /&gt;
* Java: [http://jgrapht.org/ JGraphT] (алгоритмы Крускала и Прима)&lt;br /&gt;
* Python: [http://docs.scipy.org/doc/scipy-0.15.1/reference/index.html SciPy]&lt;br /&gt;
== Литература ==&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации &amp;lt;br&amp;gt;&lt;br /&gt;
# C. Kingsford. Minimum Spanning Trees &amp;amp; Clustering ([https://www.cs.umd.edu/class/fall2009/cmsc451/lectures/Lec07a-mst.pdf])&lt;br /&gt;
# Y. Zhou et al. Clustering with Minimum Spanning Trees&lt;/div&gt;</summary>
		<author><name>Zhum</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:Sarseev&amp;diff=17528</id>
		<title>Обсуждение участника:Sarseev</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:Sarseev&amp;diff=17528"/>
		<updated>2016-11-16T07:30:36Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п 2.4&lt;br /&gt;
&lt;br /&gt;
* нет описания аппаратной платформы&lt;br /&gt;
* нет текста программы&lt;br /&gt;
* нет указания на реализацию алгоритма (своя или сторонняя)&lt;br /&gt;
* зависимость от размера графа неочевидна, переделайте в зависимость от числа входных точек&lt;br /&gt;
* число точек на графике довольно мало, поэтому вывести чёткую зависимость можно весьма условно&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 10:30, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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:SenderovichNikita/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BF%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B8_%D0%BA%D0%B0%D1%80%D0%BA%D0%B0%D1%81%D0%B0&amp;diff=17527</id>
		<title>Участник:SenderovichNikita/Алгоритм кластеризации, основанный на построении каркаса</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:SenderovichNikita/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BF%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B8_%D0%BA%D0%B0%D1%80%D0%BA%D0%B0%D1%81%D0%B0&amp;diff=17527"/>
		<updated>2016-11-16T07:25:32Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = MST-Кластеризация &lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N^2\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^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор статьи: [[Участник:SenderovichNikita|Сендерович Никита Леонидович]]&lt;br /&gt;
&lt;br /&gt;
Данный документ содержит описание алгоритма кластеризации, основанного на построении [[Построение минимального остовного дерева (MST)|минимального остовного дерева]] (каркаса, остова, кратчайшего незамкнутого пути, минимального покрывающего дерева, англ. Minimum Spanning Tree (MST)) - эвристического графового алгоритма построения разбиения множества объектов на такие непересекающиеся подмножества объектов, что объекты, принадлежащие одному подмножеству, схожи по заданной метрике в большей степени, чем объекты, принадлежащие разным группам.&lt;br /&gt;
&lt;br /&gt;
Как и все алгоритмы кластеризации, данный алгоритм может быть применён для решения задач кластерного анализа, выделения типовых и нетипичных объектов в рассматриваемой выборке объектов. Его достоинствами по сравнению с другими аналогичными алгоритмами машинного обучения являются наглядность, идейная простота, возможность контролировать число кластеров. Главным недостатком является ограниченная применимость: алгоритм лучше всего подходит для выделения кластеров типа сгущений или лент, наличие разреженного фона или узких перемычек между кластерами может приводить к неадекватному с точки зрения аналитика результату&amp;lt;ref&amp;gt;[http://www.ccas.ru/voron/download/Clustering.pdf Воронцов К.В. &amp;quot;Лекции по алгоритмам кластеризации и многомерного шкалирования&amp;quot;]&amp;lt;/ref&amp;gt;. Другим недостатком является высокая вычислительная сложность: на выборках более 100 тыс. объектов без применения параллельных вычислений применить алгоритм невозможно. Рассмотрению соответствующих параллельных алгоритмов и посвящена данная статья.&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Рассматривается выборка &amp;lt;math&amp;gt;X = \{x_i\}, i = 1, 2, \ldots, N&amp;lt;/math&amp;gt;, состоящая из &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; объектов с заданной симметричной неотрицательной функцией расстояния &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt;. Данную выборку можно рассматривать как связный неориентированный взвешенный граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, множество вершин которого совпадает со множеством объектов выборки, веса рёбер соответствуют значениям функции расстояния между соответствующими вершинами.&lt;br /&gt;
&lt;br /&gt;
Также должно быть задано искомое число кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;. [[file:MSTClusteringFull.png|thumb|700px|Рис.1. Ключевая идея алгоритма кластеризации на основе построения остовного дерева, разбиение на 3 кластера]]&lt;br /&gt;
&lt;br /&gt;
Идея рассматриваемого алгоритма кластеризации очень проста:&lt;br /&gt;
* с помощью какого-либо алгоритма на определённом выше графе строится [[Построение минимального остовного дерева (MST)| минимальное остовное дерево]]&lt;br /&gt;
* из полученного дерева выбрасывается &amp;lt;math&amp;gt;K - 1&amp;lt;/math&amp;gt; ребро с наибольшими весами, что приводит к появлению в остовном дереве &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; компонент связности, каждая из которых объявляется кластером&lt;br /&gt;
&lt;br /&gt;
Иллюстрация работы алгоритма приведена на рис. 1. В данном случае рассматриваются 13 объектов на евклидовой плоскости, построено остовное дерево, после чего из него выброшены 2 наиболее длинных ребра и получены три кластера.&lt;br /&gt;
&lt;br /&gt;
Существуют различные модификации описанного алгоритма, связанные с другим алгоритмом конструирования исходного графа или условием выбрасывания рёбер из построенного остовного дерева. Например, число &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; может быть полезно определить не изначально, а подобрать, рассматривая отсортированную по убыванию последовательность весов рёбер в остовном дереве: если имеется резкое уменьшение весов, то можно удалить все рёбра до него. Формализацией такого подхода является алгоритм ZEMST&amp;lt;ref&amp;gt;[http://www4.ncsu.edu/~zjorgen/ictai06.pdf Minimum Spanning Tree Based Clustering Algorithms]&amp;lt;/ref&amp;gt;. Однако в дальнейшем ограничимся анализом определённого выше простейшего варианта.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан связный неориентированный граф на множестве объектов &amp;lt;math&amp;gt;G = (X, E)&amp;lt;/math&amp;gt; с неотрицательными весами рёбер &amp;lt;math&amp;gt;\rho(e) = \rho(x_i, x_j) \geqslant 0&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Наиболее частый используемый на практике сценарий следующий:&lt;br /&gt;
* каждый объект описывается &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерным вектором в пространстве&lt;br /&gt;
* используется функция расстояния &amp;lt;math&amp;gt;\ell_p&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, случай, когда в качестве метрики выбирается евклидова метрика &amp;lt;math&amp;gt;\ell_2&amp;lt;/math&amp;gt; в литературе называется EMST - Euclidean Minimum Spanning Tree.&lt;br /&gt;
&lt;br /&gt;
Хотя в некоторых задачах возможна ситуация, когда данный граф не является полным (по каким-то причинам определены не все расстояния между вершинами), но в описанных типовых случаях граф является полным, содержит &amp;lt;math&amp;gt;\frac{N(N - 1)}{2} = O(N^2)&amp;lt;/math&amp;gt; рёбер.&lt;br /&gt;
&lt;br /&gt;
==== Обзор алгоритмов поиска минимального остова ====&lt;br /&gt;
Для поиска минимального остовного дерева на практике используется следующие три похожих друг на друга алгоритма и их комбинации:&lt;br /&gt;
* [[Алгоритм Прима]]&amp;lt;ref&amp;gt;Prim, R C. “Shortest Connection Networks and Some Generalizations.” Bell System Technical Journal 36, no. 6 (November 1957): 1389–1401. doi:10.1002/j.1538-7305.1957.tb01515.x.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* [[Алгоритм Крускала]]&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* [[Алгоритм Борувки]]&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&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;T&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; состоит из одной вершины, выбранной произвольно. Далее на каждом шаге отыскивается минимальное ребро, ровно один из концов которого принадлежит &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, оно добавляется в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; вместе со вторым своим концом. Алгоритм завершается через &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;gt; шаг - ровно столько рёбер в дереве на &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; вершинах. Последовательная реализация, при которой минимальное ребро на каждом шаге ищется полным перебором рёбер, исходящих из &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, имеет сложность &amp;lt;math&amp;gt;O(N^3)&amp;lt;/math&amp;gt; на полном графе. Алгоритм можно ускорить, если поддерживать двоичное дерево поиска по рёбрам, ровно один из концов которых принадлежит &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Тогда при добавлении очередной вершины в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; необходимо добавить в двоичное дерево не более чем &amp;lt;math&amp;gt;N - 1&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(\log 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 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;В алгоритме Крускала&#039;&#039;&#039; остов &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; изначально пуст, и в остов на каждом шаге добавляется вместе со своими концами минимальное ребро, имеющееся в графе, не нарушающее свойства ацикличности уже выбранного на предыдущих шагах леса &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Через &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;gt; шаг лес &amp;lt;math&amp;gt;T&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^3)&amp;lt;/math&amp;gt;. Если же предварительно отсортировать все рёбра по весу за &amp;lt;math&amp;gt;O(N^2 \log N^2) = O(N^2 \log N)&amp;lt;/math&amp;gt;, то дальше все рёбра искомого остова можно будет добавить в процессе одного прохода по этому массиву рёбер в порядке возрастания весов - для каждого ребра проверяем, не нарушает ли оно свойство ацикличности &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; и добавляем его в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, если не нарушает. Для того чтобы быстро проверять свойство принадлежности обоих концов ребра одной из компонент &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, используется эффективная структура данных - система непересекающихся множеств&amp;lt;ref&amp;gt;[http://e-maxx.ru/algo/dsu Система непересекающихся множеств]&amp;lt;/ref&amp;gt;. Сложность одной такой проверки &amp;lt;math&amp;gt;O(\alpha(N))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\alpha(N)&amp;lt;/math&amp;gt; - обратная функция Аккермана, значение которой не превосходит 4 для &amp;lt;math&amp;gt;N \leqslant 10^{500}&amp;lt;/math&amp;gt;. Итоговая сложность такого алгоритма равна сложности сортировки и также составляет &amp;lt;math&amp;gt;O(N^2 \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;В алгориме Борувки&#039;&#039;&#039;, как и в алгоритме Крускала, поддерживается лес &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Изначально лес состоит из &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; изолированных вершин графа. На каждом шаге путём перебора всех рёбер графа для каждого дерева в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; ищется минимальное ребро, ровно один конец которого принадлежит этому дереву. После этого все найденные рёбра добавляются в &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Поскольку после каждой итерации число деревьев в лесе &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, исходно равное &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;, уменьшается не менее чем в 2 раза, таких шагов будет произведено не более &amp;lt;math&amp;gt;\log N&amp;lt;/math&amp;gt;, поэтому итоговая сложность алгоритма составляет &amp;lt;math&amp;gt;O(N^2\log N)&amp;lt;/math&amp;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;T&amp;lt;/math&amp;gt;, трудно приспособить к распределённым архитектурам. А при работе с полным графом с большим числом вершин хранить его становится практически невозможно. В алгоритме Крускала можно распараллелить этап сортировки рёбер, но на последнем шаге алгоритма необходимо последовательно просмотреть отсортированный массив &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt; рёбер, поддерживая СНМ, что сводит на нет параллелизм. Таким образом, для этих двух алгоритмов необходима существенная переработка для получения эффективного параллельного решения &amp;lt;ref&amp;gt;[http://www.cslab.ntua.gr/~knikas/files/papers/mtaap12kruskal.pdf An approach to parallelize Kruskal&#039;s algorithm using Helper Threads]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На этом фоне выгодно выделяется исторически первый алгоритм Борувки. Он был разработан чешским учёным Отакаром Борувкой в 1926 году для решения задачи построения оптимальной электрической сети в Моравии. Он эффективнее всего использует тат факт, что остовные деревья сперва можно найти для отдельных частей графа, после чего объединить полученные деревья. Далее будем рассматривать именно этот метод для построения минимального остовного дерева в алгоритме кластеризации.&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;\rho(x_i, x_j)&amp;lt;/math&amp;gt; между объектами (вершинами полного графа)&lt;br /&gt;
* функции для работы с графом - инициализация добавление, удаление ребра&lt;br /&gt;
* использование упомянутого выше алгоритма СНМ (система непересекающихся множеств) для поддержки компонент связности: можно объединять компоненты при добавлении ребра и проверять принадлежность двух вершин одной компоненте за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; (функции &#039;&#039;компонента&#039;&#039;, &#039;&#039;обновить_компоненты&#039;&#039;)&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;
&amp;lt;source lang=&amp;quot;text&amp;quot; line start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
Вход: Выборка объектов X размера N&lt;br /&gt;
Выход: рёбра минимального остовного дерева T&lt;br /&gt;
Алгоритм:&lt;br /&gt;
  T := пустое множество рёбер&lt;br /&gt;
  инициализировать компоненты числами от 1 до N&lt;br /&gt;
  есть_разные_компоненты = 1&lt;br /&gt;
  Пока (есть_разные_компоненты == 1): &lt;br /&gt;
    есть_разные_компоненты = 0&lt;br /&gt;
    инициализировать массив D кратчайших рёбер, исходящих из компонент, бесконечно длинными рёбрами&lt;br /&gt;
    цикл по всем парам вершин (x, y) из X:&lt;br /&gt;
      если компонента(x) не равно компонента(y):&lt;br /&gt;
        есть разные компоненты = 1&lt;br /&gt;
        d = расстояние(x, y)&lt;br /&gt;
        если (ребро (x, y) &amp;lt; ребро D(компонента(x))):&lt;br /&gt;
            D(компонента(x)) = (x, y)&lt;br /&gt;
        если (ребро (x, y) &amp;lt; ребро D(компонента(y))):&lt;br /&gt;
            D(компонента(y)) = (x, y)&lt;br /&gt;
    цикл по рёбрам E из D:&lt;br /&gt;
       обновить_компоненты(E)&lt;br /&gt;
       добавить E в множество T&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;text&amp;quot; line start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
Вход: Выборка объектов X, число кластеров K&lt;br /&gt;
Выход: метки кластеров для каждого объекта из X&lt;br /&gt;
Алгоритм:&lt;br /&gt;
   T := рёбра минимального остовного дерева для набора объектов X&lt;br /&gt;
   E := отсортировать рёбра T по убыванию&lt;br /&gt;
   цикл по первым K рёбрам в E:&lt;br /&gt;
      удалить ребро из T&lt;br /&gt;
   цикл по вершинам v из T:&lt;br /&gt;
     компоненты[v] = компонента(v)&lt;br /&gt;
   вернуть компоненты&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
В разделе &amp;quot;Обзор алгоритмов поиска минимального остова&amp;quot; уже было показано, что алгоритм Борувки поиска остова имеет сложность &amp;lt;math&amp;gt;O(N^2 \log N)&amp;lt;/math&amp;gt;: поскольку на каждом шаге число компонент уменьшается не менее чем в 2 раза, цикл &#039;&#039;while&#039;&#039; выполнится не более &amp;lt;math&amp;gt;\log N&amp;lt;/math&amp;gt; раз. Внутри цикла находятся: &lt;br /&gt;
* инициализация массива D кратчайших рёбер, исходящих из компонент связности, бесконечностями - &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
* перебор по всем рёбрам полного графа и обновление содержимого ячеек массива D - &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
* добавление найденных рёбер из массива D в граф &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
* обновление компонент связности в графе &amp;lt;math&amp;gt;T&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(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;O(N)&amp;lt;/math&amp;gt; определяются финальные метки кластеров.&lt;br /&gt;
&lt;br /&gt;
Итоговая сложность &amp;lt;math&amp;gt;O(N^2 \log N)&amp;lt;/math&amp;gt; операций. В ходе работы основными являются операции сравнения и обращения в память. Обратим внимание, что &amp;quot;узким местом&amp;quot; алгоритма является перебор по всем рёбрам для нахождения минимальных рёбер, исходящих из каждой компоненты связности остовного леса &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приведём информационный граф, оставив за скобками детали реализации последних шагов алгоритма - сортировки и выбрасывания рёбер:&lt;br /&gt;
[[file:InformationGraph.png|thumb|center|700px|Рис.2. Информационная структура алгоритма кластеризации, основанного на поиске минимального остовного дерева]]&lt;br /&gt;
&lt;br /&gt;
Кратко опишем шаги схемы. Для вычисления массива D необходимо обработать некоторые из перебираемых рёбер (конкретные связи зависят от структуры текущего остовного дерева &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;), поэтому на этом этапе присутствует нерегулярность по данным; кроме того необходима инициализация массива D. Операции обновления структуры остовного графа независимы, могут быть распараллелены и склеены в шаг 3. Алгоритм сортировки теоретически имеет высоту ярусно-параллельной формы &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;. Независимо могут быть выполнены и последние этапы алгоритма, связанные с выбрасыванием рёбер и получением финального ответа, хотя на практике это не так уж важно.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Как показал анализ сложности узким местом является нахождение минимальных рёбер, исходящих из каждой компоненты связности остовного леса &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Информационный граф говорит о том, что этот этап можно эффективно распаралелить, так как выбор минимального исходящего ребра для каждой компоненты связности остовного дерева и даже для каждой вершины может исполнятся независимо. Таким образом &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt; рёбер - ширина ярусно-параллельной формы алгоритма. Процедуры инициализации массива D, добавления найденных рёбер в граф могут быть произведены независимо по элементам. Параллельно может быть проведена и сортировка, и выбрасывание рёбер в конце, хотя это не так принципиально в реальности. Но это позволяет говорить, что высота ярусно-параллельной формы имеет порядок &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Операции на ключевом этапе алгоритма достаточно сбалансированы - в полном графе для каждой из вершин необходимо просмотреть всех её соседей, поэтому в случае равномерного по вершинам графа распределения работ, эффективность параллельной реализации будет высокой. В целом, имеет место массовый параллелизм на итерациях вычисления ближайших вершин для текущего набора компонент связности &amp;lt;math&amp;gt;T&amp;lt;/math&amp;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;d&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;N&amp;lt;/math&amp;gt; целых чисел от 1 до &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;
Распараллеливание описанного алгоритма может принести значительный выигрыш - без него алгоритм фактически невозможно запустить на данных с числом объектов более 100 тысяч. Теоретические возможности распараллеливания на самом сложном этапе - вычислении минимального остовного дерева - были подробно обсуждены выше. Однако на практике может возникнуть ряд трудностей, связанных с эффективным обменом информацией между процессами. Так, всем параллельно работающим процессам необходимо быстро отвечать на вопрос о расположении вершин графа в одной компоненте связности - решения этой проблемы можно добиться, если поддерживать в каждом вычислительном узле данные сведения. Кроме того, важно помнить, что в условиях полного графа каждому узлу необходима и информация обо всех кластеризуемых объектах.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Как правило, в библиотеках реализуют не собственно алгоритм кластеризации с использованием остовного дерева, а только алгоритм остовного дерева, предоставляя пользователю возможность самостоятельно реализовать функции построения графа и обработки результатов работы алгоритма. &lt;br /&gt;
&lt;br /&gt;
Параллельная реализация алгоритма MST:&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
Последовательные реализации доступны во многих библиотеках:&lt;br /&gt;
* [https://networkx.github.io/documentation/networkx-1.9/_modules/networkx/algorithms/mst.html NetworkX MST] - реализация в библиотеке для работы с графами на Python&lt;br /&gt;
* [http://www.boost.org/doc/libs/1_38_0/libs/graph/doc/prim_minimum_spanning_tree.html Алгоритм Прима], [http://www.boost.org/doc/libs/1_62_0/libs/graph/doc/kruskal_min_spanning_tree.html Алгоритм Крускала] - C++ реализация в boost&lt;br /&gt;
* [http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Алгоритм Прима], [http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Алгоритм Крускала] - Java реализация в JGraphT&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Zhum</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:SenderovichNikita&amp;diff=17526</id>
		<title>Обсуждение участника:SenderovichNikita</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:SenderovichNikita&amp;diff=17526"/>
		<updated>2016-11-16T07:25:22Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4 не заполнен.&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 10:25, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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:Denemmy/Partitioning_Around_Medoids_(%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC)&amp;diff=17525</id>
		<title>Участник:Denemmy/Partitioning Around Medoids (Алгоритм)</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:Denemmy/Partitioning_Around_Medoids_(%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC)&amp;diff=17525"/>
		<updated>2016-11-16T07:24:20Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Partitioning Around Medoids&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(T*K*N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; N*(N-1)/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;
Авторы: [[Участник:Denemmy|Галеев Д.Ф]], [[Участник:zaputlyaev|Запутляев И.]]&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;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм &#039;&#039;&#039;Partitioning Around Medoids&#039;&#039;&#039; (&#039;&#039;&#039;PAM&#039;&#039;&#039;) был создан Леонардом Кауфманом (Leonard Kaufman) и Питером Руссивом (Peter J. Rousseeuw)&amp;lt;ref&amp;gt;Kaufman, L. and Rousseeuw, P.J., Clustering by means of Medoids. Statistical Data Analysis Based on the L1-Norm and Related Methods. Springer US; 1987, p. 405–416.&amp;lt;/ref&amp;gt;. и он очень похож на алгоритм K-means, в основном потому, что оба являются алгоритмами кластеризации, другими словами, оба разделяют множество объектов на группы (кластеры) и работа обоих основана на попытках минимизировать ошибку, но PAM работает с медоидами - объектами, являющимися частью исходного множества и представляющими группу, в которую они включены, а K-means работает с центроидами - искусственно созданными объектами, представляющими кластер.&lt;br /&gt;
&lt;br /&gt;
Алгоритм PAM разделяет множество из N объектов на K кластеров, где и множество объектов, и число K являются входными данными алгоритма. Алгоритм работает с матрицей расстояний, его цель - минимизировать расстояние между представителями каждого кластера и его членами. Алгоритм использует следующую модель для решения задачи:&amp;lt;ref&amp;gt;Fasulo D. An analysis of recent work on clustering algorithms. – Technical report, 1999. – №. 01-03. – С. 02.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F(x)=minimize \sum_{i=1}^{N}\sum_{j=1}^{N} d(i,j)z_{ij}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt; \sum_{i=1}^N {z_{ij} = 1} , j = 1,2,...,N&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;z_{ij} \le y_i , i, j = 1,2,...,N&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
3. &amp;lt;math&amp;gt;\sum_{i=1}^N {y_i = K} , K - &amp;lt;/math&amp;gt; число кластеров;&lt;br /&gt;
&lt;br /&gt;
4. &amp;lt;math&amp;gt;y_i , z_{ij} \in \{ 0,1 \} , i, j = 1,2,...,N&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где функция &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; - целевая минимизируемая функция, &amp;lt;math&amp;gt;d(i,j)&amp;lt;/math&amp;gt; - мера расстояния между объектами &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;z_{ij}&amp;lt;/math&amp;gt; - переменная, которая гарантирует, что расстояние только между объектами из одного кластера будет вычислено в целевой функции. Остальные выражения являются следующими ограничениями:&lt;br /&gt;
&lt;br /&gt;
1. Каждый объект принадлежит одному и только одному кластеру;&lt;br /&gt;
&lt;br /&gt;
2. Каждый объект относится к медоиде, представляющей его кластер;&lt;br /&gt;
&lt;br /&gt;
3. Есть в точности K кластеров;&lt;br /&gt;
&lt;br /&gt;
4. Решающая переменная принимает значения 0 или 1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PAM может работать с двумя типами входных данных:&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;
Фаза &#039;&#039;&#039;Build&#039;&#039;&#039;:&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;
&lt;br /&gt;
Фаза &#039;&#039;&#039;Swap&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
4. Для каждого кластера найти объекты, снижающие среднее расстояние, и если такие объекты есть, выбрать те, которые снижают его сильней всего, в качестве медоид;&lt;br /&gt;
&lt;br /&gt;
5. Если хотя бы одна медоида поменялась, вернуться к шагу 3, иначе завершить алгоритм.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&#039;&#039;Обозначения&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;M = \{ x_{m_1}, x_{m_2}, ... , x_{m_s} \} - &amp;lt;/math&amp;gt; множество медоид;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;L = \{ x_{l_1}, x_{l_2}, ... , x_{m_t} \} - &amp;lt;/math&amp;gt; множество объектов &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, не являющихся медоидами;&lt;br /&gt;
&lt;br /&gt;
3. &amp;lt;math&amp;gt;F_{M,L} = \sum_{j = 1}^{N} \min_{ x_{m_q} \in M}  d(x_{m_q},x_{l_j}) - &amp;lt;/math&amp;gt; целевая функция.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
1. Множество &amp;lt;math&amp;gt;X = \{ x_{1}, x_{2}, \dots, x_{N} \}&amp;lt;/math&amp;gt; объектов &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, каждый из который задается &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; вещественными значениями;&lt;br /&gt;
&lt;br /&gt;
2. Симметрическая матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, элементы &amp;lt;math&amp;gt;d_{ij} = d(x_i,x_j)&amp;lt;/math&amp;gt; которой являются расстояниями между объектами &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_j&amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
3. Число кластеров &amp;lt;math&amp;gt;K \le N&amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
4. Метрика &amp;lt;math&amp;gt;d_{ij} = d(x_i,x_j)&amp;lt;/math&amp;gt;, задающая расстояние между объектами &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вычислительные формулы метода&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Фаза &#039;&#039;&#039;Build&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Представляет из себя выбор K медоид за K последовательных шагов:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;M_0 = \varnothing, L_0 = X&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;x_{m_i} = \arg \min_{x_{l_q} \in L_0} \sum_{j = 1}^N  d(x_{l_q},x_j), L_1 = L_0 \backslash \{x_{m_i}\}, M_1 = M_1 \cup \{x_{m_i}\}, i=1 \dots N &amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Фаза &#039;&#039;&#039;Swap&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Представляет из себя итерационный процесс.&lt;br /&gt;
&lt;br /&gt;
1. Начальное приближение: &amp;lt;math&amp;gt;M_0 = M_{BUILD}, L_0 = L_{BUILD}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
2. Множественные вычисления значений целевой функции для различных вариантов замены текущей медоиды на новую:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = arg \min_{i \in M , j \in L} \sum_{y \in L \backslash \{j\}} {min (d(i,y),d(j,y))} &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
3. Изменение множеств в соответствии с выбранной парой &amp;lt;math&amp;gt;(x_{m_t}, x_{l_q})&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M_i = M_{i-1} \backslash \{x_{m_t}\} \cup \{x_{l_q}\} , L_i = L_{i-1} \cup \{x_{m_t}\} \backslash \{x_{l_q}\} &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
4. Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;M = \{ x_{m_{1}}, x_{m_{2}}, ..., x_{m_{K}} \}&amp;lt;/math&amp;gt; - множество медоид;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;K_{m_i} = \{ x_{l_q} \in L \| x_{m_i} = \arg \min_{x_{m_s} \in M}  d(x_{l_q}, x_{m_s}) \}, i=1 \dots K, K -&amp;lt;/math&amp;gt; множество искомых кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма PAM сосредоточено в стадии &#039;&#039;&#039;SWAP&#039;&#039;&#039; и состоит из вычислений значений целевой функции для различных вариантов замены текущей медоиды на новую:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = arg \min_{i \in M , j \in L} \sum_{y \in L \backslash \{j\}} {min (d(i,y),d(j,y))} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Если на вход алгоритма была подана не матрица расстояний, а матрица объектов с их координатами в пространстве &amp;lt;math&amp;gt;R^P&amp;lt;/math&amp;gt;, то операция вычисления расстояния между объектами &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;P&amp;lt;/math&amp;gt; будет являться макрооперацией. В качестве меры расстояния может быть использована евклидова метрика:&lt;br /&gt;
&amp;lt;math&amp;gt; d(a,b) = \sqrt{(a_1-b_1)^2+(a_2-b_2)^2+\dots+(a_P-b_P)^2} = \sqrt{\sum_{i=1}^P (a_i-b_i)^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Псевдокод алгоритма&#039;&#039; &amp;lt;ref&amp;gt;Park H. S., Jun C. H. A simple and fast algorithm for K-medoids clustering //Expert Systems with Applications. – 2009. – Т. 36. – №. 2. – С. 3336-3341.:&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 1  функция PAM(D, k, tmax=100):&lt;br /&gt;
 2      &amp;lt;span style=&amp;quot;color:#408080&amp;quot;&amp;gt;# D - матрица расстояний, k - число кластеров, tmax - маскимальное число итераций&amp;lt;/span&amp;gt;&lt;br /&gt;
 3      выполнить фазу BUILD, получить множество метоидов M и множество не-метоидов L&lt;br /&gt;
 4      вычислить значение целевой функции F&lt;br /&gt;
 5      для t = 0..tmax-1:&lt;br /&gt;
 6          выполнить фазу SWAP, вычислить значение целевой функции F&#039;&lt;br /&gt;
 7          delta = F - F&#039;&lt;br /&gt;
 8          если delta &amp;gt; 0:&lt;br /&gt;
 9              обновить множества M и L&lt;br /&gt;
 10             F = F&#039;&lt;br /&gt;
 11         иначе:&lt;br /&gt;
 12             выйти из цикла&lt;br /&gt;
 13     вернуть М&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;N&amp;lt;/math&amp;gt;, число итераций алгоритма как &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На стадии &#039;&#039;&#039;BUILD&#039;&#039;&#039; каждый шаг нахождения очередного метоида имеет сложность &amp;lt;math&amp;gt;O(N^2)&amp;lt;/math&amp;gt; по количеству операций сложения вещественных чисел и по количеству операций сравнения двух вещественных чисел.&lt;br /&gt;
&amp;lt;br&amp;gt;Тогда стадия &#039;&#039;&#039;BUILD&#039;&#039;&#039; имеет сложность &amp;lt;math&amp;gt;O(K*N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На стадии &#039;&#039;&#039;SWAP&#039;&#039;&#039; вычисление целевой функции имеет сложность &amp;lt;math&amp;gt;O(K*N^2)&amp;lt;/math&amp;gt; по количеству операций сложения вещественных чисел и по количеству операций сравнения двух вещественных чисел.&lt;br /&gt;
&lt;br /&gt;
Таким образом алгоритм &amp;lt;b&amp;gt;PAN&amp;lt;/b&amp;gt; имеет сложность &amp;lt;math&amp;gt;O(T*K*N^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фаза BUILD&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Общий вид информационного графа для шага t представлен на рисунке 1:&lt;br /&gt;
&lt;br /&gt;
[[Файл:BUILD_2.png|thumb|center|800px|Рисунок 1. Информационный граф шага t фазы BUILD алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Операции&#039;&#039;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_M&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_L&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - операции обновления множества метоидов и не-метоидов соответственно&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;SUM&amp;lt;/b&amp;gt; - вычисление функции ошибки, на вход подается расстояния от очередного объекта до всех остальных, а также минимальные расстояния от выбранных на данном шаге метоидов до всех остальных вершин&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;MIN&amp;lt;math&amp;gt;_s&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - нахождение аргумента, соответствующего минимальному значению, а также само минимальное значение&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;MIN&amp;lt;math&amp;gt;_d&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - нахождение минимальных расстояний от медоидов до остальных вершин, используется в операции SUM&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
Количество шагов t равно &amp;lt;b&amp;gt;K&amp;lt;/b&amp;gt;, где &amp;lt;b&amp;gt;K&amp;lt;/b&amp;gt; - число кластеров&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фаза SWAP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Общий вид информационного графа для итерации t представлен на рисунке 2:&lt;br /&gt;
&lt;br /&gt;
[[Файл:SWAP_2.png|thumb|center|800px|Рисунок 2. Информационный граф итерации t фазы SWAP алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Операции&#039;&#039;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_M&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;Update&amp;lt;math&amp;gt;_L&amp;lt;/math&amp;gt;&amp;lt;/b&amp;gt; - операции обновления множества метоидов и не-метоидов соответственно&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;SUM&amp;lt;/b&amp;gt; - вычисление целевой функции&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;MIN&amp;lt;/b&amp;gt; - нахождение аргумента, соответствующего минимальному значению, а также само минимальное значение&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&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;N&amp;lt;/math&amp;gt;. Тогда параллельная сложность фазы &amp;lt;b&amp;gt;BUILD&amp;lt;/b&amp;gt; имеет &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сложения и &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сравнения двух вещественных чисел. Таким образом параллельная сложность фазы &amp;lt;b&amp;gt;BUILD&amp;lt;/b&amp;gt; равна &amp;lt;math&amp;gt;O(K*N*T)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность фазы &amp;lt;b&amp;gt;SWAP&amp;lt;/b&amp;gt; имеет &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сложения и &amp;lt;math&amp;gt;O(K*N)&amp;lt;/math&amp;gt; операций сравнения двух вещественных чисел. Параллельная сложность фазы &amp;lt;b&amp;gt;SWAP&amp;lt;/b&amp;gt; равна &amp;lt;math&amp;gt;O(K*N*T)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 Таким образом параллельная сложность алгоритма равна &amp;lt;math&amp;gt;O(K*N*T)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
 * число &amp;lt;math&amp;gt;K&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;N*(N-1)/2&amp;lt;/math&amp;gt;, из данных чисел однозначно восстанавливается симметрическая матрица расстояний &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
&#039;&#039;Объём входных данных&#039;&#039;: &amp;lt;math&amp;gt;N*(N-1)/2&amp;lt;/math&amp;gt; вещественное число и &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; целых числа.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&lt;br /&gt;
 * K чисел &amp;lt;math&amp;gt;m_1, m_2, ..., m_K&amp;lt;/math&amp;gt; - индексы объектов соответствующие метоидам;&lt;br /&gt;
 * N-K чисел &amp;lt;math&amp;gt;k_1, k_2, ..., k_{N-K}&amp;lt;/math&amp;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;
=== Свойства алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма &amp;lt;i&amp;gt;k means&amp;lt;/i&amp;gt; равна &amp;lt;math&amp;gt;\frac{K*N^2*\Tau}{N^2} = K*\Tau &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; &amp;amp;ndash; число кластеров, &amp;lt;math&amp;gt;\Tau&amp;lt;/math&amp;gt; &amp;amp;ndash; число итераций алгоритма.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Детерминированность |&#039;&#039;Детерминированность&#039;&#039;]] и [[глоссарий#Устойчивость |&#039;&#039;Устойчивость&#039;&#039;]] &amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Алгоритм &amp;lt;i&amp;gt;PAM&amp;lt;/i&amp;gt; является итерационным, количество итераций может быть ограничено сверху, однако в общем случае не фиксируется. Из-за недетермирированности выбора элементов, на которых достигается минимум целевой функции, алгоритм не является &amp;lt;b&amp;gt;детермирированным&amp;lt;/b&amp;gt;. Однако, данный алгоритм является &amp;lt;b&amp;gt;устойчивым&amp;lt;/b&amp;gt;, поскольку не накапливает ошибки в процессе своей работы. &amp;lt;ref&amp;gt;Нейский И. М. Классификация и сравнение методов кластеризации //ББК 32.813 И 76 Составитель: ЮН Филиппович. – 2006. – С. 130.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Сильные стороны алгоритма&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;ul&amp;gt;  &lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Меньшая чувствительность к выбросам, чем k-means&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Несложность реализации&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Возможность распараллеливания&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Однако равномерная загрузка процессоров не всегда возможна.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Недостатки алгоритма&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;Квадратичная сложность алгоритма&amp;lt;/i&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Количество кластеров является параметром алгоритма&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Во многих задачах число кластеров может быть неизвестным.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;&amp;lt;i&amp;gt;Возможность сходимости к локальному оптимуму&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Оптимальное решение не гарантировано.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&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;
Исследование проводилось на суперкомпьютере &#039;&#039;&#039;&amp;quot;Ломоносов&amp;quot;&#039;&#039;&#039;. Алгоритм реализован с использованием технологии &#039;&#039;&#039;MPI&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Данные были сгенерированы в &#039;&#039;&#039;Matlab&#039;е&#039;&#039;&#039; случайным образом. Количество кластеров равно &#039;&#039;&#039;16&#039;&#039;&#039;, каждый кластер был промоделирован нормальным распределением с некоторыми случайными значениями среднего и среднеквадратического отклонения. Размерность данных равна &#039;&#039;&#039;2&#039;&#039;&#039;, а число точек на каждый кластер было достаточно большим для проведения экспериментов с различными значениями размерности матрицы расстояний. Пример сгенерированных данных показан на рисунке 3.&lt;br /&gt;
&lt;br /&gt;
[[Файл:16_clusters.jpg|thumb|center|512px|Рисунок 3. Пример сгенерированных данных.]]&lt;br /&gt;
&lt;br /&gt;
Измерялось &#039;&#039;&#039;время выполнения&#039;&#039;&#039; стадий &#039;&#039;&#039;BUILD&#039;&#039;&#039; и &#039;&#039;&#039;SWAP&#039;&#039;&#039;, без учета времени, потраченного на построение матрицы расстояний, а также измерялось &#039;&#039;&#039;число итераций&#039;&#039;&#039; алгоритма.&lt;br /&gt;
Были проведены эксперименты для следующих значений размерности матрицы расстояний и числа процессоров:&lt;br /&gt;
&lt;br /&gt;
 Значения размерности: [2500, 2000, 1500, 1000, 500, 200, 150, 100, 50]&lt;br /&gt;
 Значения числа процессоров: [500, 400, 256, 128, 64, 32, 16, 8, 4, 1]&lt;br /&gt;
&lt;br /&gt;
Полученные графики производительности и эффективности представлены на рисунках 4 и 5 соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GFLOPS.png|thumb|center|512px|Рисунок 4. Зависимость производительности реализации параллельного алгоритма от размера матрицы и числа процессоров]]&lt;br /&gt;
[[Файл:EFF.png|thumb|center|512px|Рисунок 5. Зависимость эффективности реализации параллельного алгоритма от размера матрицы и числа процессоров.]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, из полученных графиков можно сделать вывод о том, что эффективность задачи при фиксированном размере входной матрицы понижается с увеличением числа процессоров, это связано с увеличением передачи данных между процессорами. При фиксированном значениии числа процессоров эффективность увеличивается при увеличении размера входной матрицы, это в свою очередь связано с тем, что доля времени, потраченного на передачу данных между процессорами, от общего затраченного на вычисления времени уменьшается.&lt;br /&gt;
&lt;br /&gt;
Реализация алгоритма, которая использовалась в экспериментах выложена в репозитории[https://denemmy@bitbucket.org/denemmy/pam.git]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/ELKI ELKI] реализует несколько вариантов алгоритма кластеризации, включая алгоритм PAM. Написан на Java&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://java-ml.sourceforge.net/ Java-ML]. Включает реализацию k-metoid. Написан на Java&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php?title=Julia_language&amp;amp;action=edit&amp;amp;redlink=1 Julia] содержит реализацию k-metoid в пакете для кластеризации JuliaStats&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/R_(programming_language) R] включает различные варианты k-means в пакете flexclust. Алгоритм PAM реализован в пакете cluster&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/MATLAB MATLAB]. Реализованы PAM, CLARA и другие алгоритмы кластеризации&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://research.omicsgroup.org/index.php/Python_(programming_language) Python]. Алгоритм PAM реализован как k-medoids в пакете pyclust, содержащем также различные варианты k-means&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zhum</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:Astandrik/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=17524</id>
		<title>Участник:Astandrik/Алгоритм кластеризации, основанный на минимальном остовном дереве</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:Astandrik/%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,_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BD%D0%B0_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%BC_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5&amp;diff=17524"/>
		<updated>2016-11-16T07:24:01Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на минимальном остовном дереве&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O(N^2 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;
Основные авторы описания: [[Участник:Astandrik|Стандрик Антон Сергеевич]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм предназначен для решения задач кластеризации. Говоря в общем, кластеризация — многомерная статистическая процедура, выполняющая сбор данных, содержащих информацию о выборке объектов, и затем упорядочивающая объекты в сравнительно однородные группы &amp;lt;ref&amp;gt;Классификация и кластер. Под ред. Дж. Вэн Райзина. М.: Мир, 1980. 390 с.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Впервые идея использования минимального остовного дерева для кластеризации данных была преждожена C. T. Zah в 1971 году&amp;lt;ref name=zakh&amp;gt;C. T. Zahn, “graph-Theriotical methos for detecting and describing getalt clusters,” IEEE Trans. Computers, vol. 20, no. 1, pp. 68-86, 1971 &amp;lt;/ref&amp;gt;. Этот метод широко используется исследователями при анализе изображений &amp;lt;ref&amp;gt;Y. Xu, V. Olman and E. C. Uberbacher, “A mentation using minimum spanning trees,” Image and Vission Computing, vol. 15, pp. 47-57,1997.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Zhi Min Wang, Yeng Chai Soh, Qing Song, Kang Sim, “Adaptive spatial information-theoretic clustering for image segmentation,” Pattern Recognition, vol. 42, no. 9, pp. 2029-2044, 2009.&amp;lt;/ref&amp;gt;, а также при анализе биологических данных &amp;lt;ref&amp;gt;Y. Xu, V. Olman and D. Xu, “Clustering gene expression data using a graph-Theriotic approach: An application of minimum spanning trees,” Bioinformatics, vol. 18, no. 4, pp. 536-545, 2002.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем описании работы алгоритма можно выделить следующие шаги:&lt;br /&gt;
&lt;br /&gt;
# Разбиение входных векторов на пары и вычисление расстояний в них. Для этого небходим выбор метрики пространства, в котором осуществляется кластерный анализ. В качестве примеров метрик можно привести:&lt;br /&gt;
#* Метрика Евклида: &amp;lt;math&amp;gt;d_{ij} = \sqrt{\sum^n_{k=1}(x_{ik} - x_{jk})^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метрика city-block (манхэттенская метрика): &amp;lt;math&amp;gt;d_{ij} = \sum^n_{k=1}|x_{ik} - x_{jk}| &amp;lt;/math&amp;gt;&lt;br /&gt;
#* Метрика Брея-Картиса: &amp;lt;math&amp;gt;d_{ij} = \sum^n_{k=1}\frac{|x_{ik} - x_{jk}|}{|x_{ik} + x_{jk}| }&amp;lt;/math&amp;gt;&lt;br /&gt;
# Построение минимального остовного дерева по заданному множеству. Существует три основных алгоритма [[Построение минимального остовного дерева (MST) | построения минимального остовного дерева]]:&lt;br /&gt;
#* [[Алгоритм Прима]]&lt;br /&gt;
#* [[Алгоритм Крускала]]&lt;br /&gt;
#* [[Алгоритм Борувки]]&lt;br /&gt;
# Разделение полученного дерева на несвязное множество поддеревьев в соответствии с некоторой стратегией. Можно выделить три популярные стратегии кластеризации:&lt;br /&gt;
#* SEMST&amp;lt;ref&amp;gt;Asano, M.K.T., Bhattacharya, B., Yao, F.: Clustering algorithms based on minimum and maximum spanning trees. In: In Proceedings of the fourth annual symposium on Computational Geometry. (1998) 252–257&amp;lt;/ref&amp;gt; (standard Euclidean minimum spanning tree clustering algorithm) - предполагается, что нам заранее известно число кластеров. В таком случае, если число кластеров равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, то из полученного в первом пункте остовного дерева удаляются &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; связей с наибольшим весом.&lt;br /&gt;
#* CEMST&amp;lt;ref&amp;gt;Ye, B., Chao, K.M.: Spanning Trees and Optimization Problems. Chapman and&lt;br /&gt;
Hall (2004)&amp;lt;/ref&amp;gt;(The maximum cost minimum spanning tree) - Работает так же как и SEMST, однако вместо веса вершины используется её стоимость, которая вычисляется следующим образом: Пусть ребро &amp;lt;math&amp;gt;e = (x_{i}, x_{j})&amp;lt;/math&amp;gt;, тогда &amp;lt;math&amp;gt;cost(e) = w(e) * degree(x_{i}) * degree(x_{j})&amp;lt;/math&amp;gt;. Далее, зная число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; и стоимости рёбер, удаляются &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; рёбер с максимальной стоимостью.&lt;br /&gt;
#* ZEMST&amp;lt;ref name=zakh /&amp;gt; (The Zahn’s minimum spanning tree) - от двух предыдущих эта стратегия отличается тем, что здесь число кластеров не известно заранее, а вычисляется в процессе кластеризации. Его суть в том, что удаляются те рёбра, веса которых значительно превосходят средний вес ребра в соседних поддеревьях.&lt;br /&gt;
&lt;br /&gt;
==== Достоинства ====&lt;br /&gt;
&lt;br /&gt;
Основным достоинством алгоритма является его способность выделять кластеры произвольной формы. На следующем примере можно видеть, как алгоритм даже в случае SEMST алгоритма корректно разбивает вложенные окружности на три кластера:&lt;br /&gt;
&lt;br /&gt;
[[File:Selection_117_.png|frame|center|alt=Point-connected clusters| Слева направо: Рисунок 1: Начальные данные. Рисунок 2: построенное минимальное остовное дерево. Рисунок 3: разбиение на кластеры путём удаления самых длинных связей.]]&lt;br /&gt;
&lt;br /&gt;
==== Недостатки ====&lt;br /&gt;
&lt;br /&gt;
Недостатком является трудность определения четких критериев, при которых осуществляется разбиение данных на разные кластеры.&lt;br /&gt;
&lt;br /&gt;
Кроме того, существуют наборы данных, на которых популярные алгоритмы (SEMST, CEMST, ZEMST) не дают правильного результата, и для которых требуется применение более сложных алгоритмов, к числу которых можно отнести, например, алгоритм HEMST &amp;lt;ref&amp;gt;Clustering with Minimum Spanning Trees Yan Zhou, Oleksandr Grygorash, Thomas F. Hain, Meador Inge Zach Jorgensen School of Computer and Information Sciences University of South Alabama, Mobile, AL 36688 USA Urban Insight Inc. 5657 Wilshire Blvd Ste 290, Los Angeles, CA 90036&amp;lt;/ref&amp;gt; с временем работы &amp;lt;math&amp;gt;O(n^2 * log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В качестве иллюстрации можно привести два похожих визуально, но различных с точки зрения разбиения на кластеры с помощью описываемого алгоритма, примера. (В приведенных примерах первый рисунок обозначает начальный набор данных. Второй - построенное на основе данных минимальное остовное дерево. Третий рисунок - разбиение на кластеры.):&lt;br /&gt;
&lt;br /&gt;
[[File:Selection_116_.png|frame|center|alt=Point-connected clusters| Правильное разбиение на два кластера.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Selection_115_.png|frame|center|alt=Point-connected clusters| Неверное разбиение на один кластер.]]&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; : &amp;lt;math&amp;gt;(a_1, a_2, ..., a_n)&amp;lt;/math&amp;gt;, где   &amp;lt;math&amp;gt; a_j \in \mathbb {R} \quad \forall j \in [1,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;E&amp;lt;/math&amp;gt; - множество рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин.&lt;br /&gt;
* &amp;lt;math&amp;gt;G = (V,E) &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;
* &amp;lt;math&amp;gt;MST(G)&amp;lt;/math&amp;gt; (Minimum Spanning Tree) - минимальное остовное дерево графа &amp;lt;math&amp;gt;&amp;lt;/math&amp;gt;. Остовное дерево — ациклический связный подграф данного связного неориентированного графа, в который входят все его вершины. Минимальное остовное дерево в связанном взвешенном неориентированном графе — это остовное дерево этого графа, имеющее минимальный возможный вес, где под весом дерева понимается сумма весов входящих в него рёбер.&lt;br /&gt;
* &amp;lt;math&amp;gt;MSF(G)&amp;lt;/math&amp;gt; (Minimum Spanning Forest) - набор минимальных остовных деревьев для всех компонент связности графа &amp;lt;math&amp;gt; G &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В описании алгоритмов этой статьи часто используется термин &amp;quot;построение MST&amp;quot;, подразумевая, что на самом деле в результате построения может получиться как одна компонента связности (MST), так и множество компонент связности (MSF).&lt;br /&gt;
&lt;br /&gt;
==== Алгоритмы кластеризации ====&lt;br /&gt;
&#039;&#039;&#039;SEMST&#039;&#039;&#039;(standard Euclidean minimum spanning tree) - простейший алгоритм, основанный на заранее известном числе кластеров &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;. Путём удаления &amp;lt;math&amp;gt; k - 1 &amp;lt;/math&amp;gt; связей с наибольшим весом предполагается разбиение остовного дерева на нужное количество кластеров. Иными словами минимальное остовное дерево разбивается на &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;  поддеревьев: &amp;lt;math&amp;gt; G = \{G_1,G_2,...,G_k\} &amp;lt;/math&amp;gt; путём минимизации суммы всех весов: &amp;lt;math&amp;gt; W = \sum ^k_{l=1} \sum _{x_i,x_j \in V_l} w_{x_i,x_j} -&amp;gt; min &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; V_l &amp;lt;/math&amp;gt; - множество вершин в поддереве &amp;lt;math&amp;gt; G_l &amp;lt;/math&amp;gt;. В конце такой процедуры каждое множество &amp;lt;math&amp;gt; V_l &amp;lt;/math&amp;gt; становится кластером &amp;lt;math&amp;gt; C_l &amp;lt;/math&amp;gt;. Поскольку (в случае использования алгоритма Крускала для построения остовного дерева) рёбра перед началом кластеризации уже отсортированы, для удаления наибольших &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; рёбер требуется &amp;lt;math&amp;gt; O(k) &amp;lt;/math&amp;gt; операций, и для распределения получившихся вершин по кластерам требуется время &amp;lt;math&amp;gt; O(n) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CEMST&#039;&#039;&#039;(maximum cost Euclidean minimum spanning tree) - Работает в точности, как и SEMST, но здесь вместо весов рёбер используется т.н. &amp;quot;стоимость&amp;quot;, которая высчитывается следующим образом: если ребро &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt; соединяет вершины &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; x_j &amp;lt;/math&amp;gt;, то его &amp;quot;стоимость&amp;quot; считается как: &amp;lt;math&amp;gt; Cost_{x_i, x_j} = w_{x_i,x_j} * Deg(x_i) * Deg(x_j) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; Deg(x_i) &amp;lt;/math&amp;gt; - степень вершины &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt; (т.е.  количество рёбер графа G, инцидентных вершине &amp;lt;math&amp;gt; x_i &amp;lt;/math&amp;gt;). В остальном алгоритм действует точно так же, как и SEMST, удаляя &amp;lt;math&amp;gt; k-1 &amp;lt;/math&amp;gt; рёбер с наибольшей &amp;quot;стоимостью&amp;quot;. Сложность &amp;lt;math&amp;gt; O(k) &amp;lt;/math&amp;gt; . Алгоритм направлен на удаление рёбер, соединяющих высоко нагруженные (с точки зрения количества инцидентных рёбер) вершины.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ZEMST&#039;&#039;&#039; (The Zahn’s maximum spanning tree (Zahn, 1971)) &amp;lt;ref&amp;gt; Attribute Clustering Based on Heuristic Tree Partition Jorge Cordero H. and Yifeng Zeng Department of Computer Science Aalborg University 9220 Aalborg, Denmark&amp;lt;/ref&amp;gt; - отличается от двух предыдущих алгоритмов в первую очередь тем, что здесь количество кластеров заранее не известно, но определяется исходя из данных. Данный алгоритм не является жадным, в отличие от первых двух, так как в нём рассматривается не только само ребро и его вес, а также и поддеревья, которые соединены этим ребром. То есть для ребра &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt; рассматриваются поддеревья &amp;lt;math&amp;gt; N_i = (V_{N_i}, E_{N_i}), N_j = (V_{N_j}, E_{N_j}) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; N_i &amp;lt;/math&amp;gt; - поддерево  глубины(высоты) &amp;lt;math&amp;gt; d &amp;lt;/math&amp;gt;(величина d определяется эмпирически), состоящее из всех достижимых из вершины &amp;lt;math&amp;gt; x_j &amp;lt;/math&amp;gt; вершин за &amp;lt;math&amp;gt; d &amp;lt;/math&amp;gt; шагов через все достижимые из неё рёбра (исключая само ребро &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt;). &lt;br /&gt;
Введём обозначения:&lt;br /&gt;
*&amp;lt;math&amp;gt;\overline{w_{N_i}} = \frac{\sum_{(x_r, x_s) \in E_{N_j}}w_{x_r,x_s}}{|E_{N_j}|} &amp;lt;/math&amp;gt; - средний вес ребра в поддереве &amp;lt;math&amp;gt; N_i &amp;lt;/math&amp;gt;&lt;br /&gt;
*&amp;lt;math&amp;gt;\sigma_{N_i} = \sqrt{\frac{\sum_{(x_r, x_s) \in E_{N_j}}(w_{x_r,x_s} - \overline{w_{N_i}})^2}{|E_{N_j}|}} &amp;lt;/math&amp;gt; - стандартное отклонение веса ребра в поддереве &amp;lt;math&amp;gt; N_i &amp;lt;/math&amp;gt;&lt;br /&gt;
Ребро &amp;lt;math&amp;gt; (x_i, x_j) &amp;lt;/math&amp;gt; удаляется из списка в случае, если выполняется хотя бы одно из двух условий:&lt;br /&gt;
#&amp;lt;math&amp;gt; w_{x_i,x_j} &amp;gt; \overline{w_{N_i}} - \sigma_{N_i} &amp;lt;/math&amp;gt;&lt;br /&gt;
#&amp;lt;math&amp;gt; w_{x_i,x_j} &amp;gt; \overline{w_{N_j}} - \sigma_{N_j} &amp;lt;/math&amp;gt;&lt;br /&gt;
#&amp;lt;math&amp;gt; w_{x_i,x_j} &amp;gt; \frac{\sum_{(x_r, x_s) \in E_i \cup E_j}w_{x_r,x_s}}{|E_i \cup E_j|} &amp;lt;/math&amp;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(Nd) &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; O(n^2) &amp;lt;/math&amp;gt; &lt;br /&gt;
# Поиск минимального остовного дерева. Последовательная сложность: &amp;lt;math&amp;gt; O(E*log(E)) &amp;lt;/math&amp;gt;&lt;br /&gt;
# Разбиение остовного дерева на кластеры. Последовательная сложность зависит от реализации. (от &amp;lt;math&amp;gt; O(1) &amp;lt;/math&amp;gt; в случае SEMST до &amp;lt;math&amp;gt; O(n^2) &amp;lt;/math&amp;gt; в случае ZEMST).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В реализации алгоритма используются следующие структуры данных:&lt;br /&gt;
#&#039;&#039;&#039;Узел&#039;&#039;&#039;(Node)(Вершина) - Представляется набором координат в выбранном пространстве.&lt;br /&gt;
#&#039;&#039;&#039;Ребро&#039;&#039;&#039;(Edge) - Представляется: двумя индексами вершин, которые соединяет; своим весом; флагом, который указывает, входит ли данное ребро в минимальное остовное дерево.&lt;br /&gt;
&lt;br /&gt;
В дальнейшем описании алгоритма используются следующие макрооперации:&lt;br /&gt;
#Вычисление расстояния между вершинами.&lt;br /&gt;
#Добавление ребра в граф - Осуществляется путём выставление специального флага у соответствующего ребра в true;&lt;br /&gt;
#Удаление ребра из графа - Операция, обратная к добавление: соответствующий флаг устанавливается в false. В начальный момент времени у всех рёбер флаг установлен в false, что означает отсутствие рёбер в графе. &lt;br /&gt;
&lt;br /&gt;
Следующие три используемые операции относятся к реализации алгоритма Крускала при помощи леса непересекающихся множеств. Подробнее об этой структуре и связанных операциях можно почитать, например, здесь &amp;lt;ref&amp;gt; Томас Кормен и др. Алгоритмы: построение и анализ = INTRODUCTION TO ALGORITHMS. — 2-е изд. — М.: «Вильямс», 2006. — С. 1296. — ISBN 0-07-013151-1. (Глава 21. Раздел 21.3: &amp;quot;Леса непересекающихся множеств) &amp;lt;/ref&amp;gt;, в данном пункте они будут только перечислены:&lt;br /&gt;
#Создание дерева с одним узлом - Make-Set&lt;br /&gt;
#Объединение компонент связности - Union&lt;br /&gt;
#Поиск компонент связности - Find-Set&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
int N;        // Размер входных данных&lt;br /&gt;
int number_of_clusters;        // Число кластеров&lt;br /&gt;
int size = N* (N-1) / 2;        // Количество связей в полном графе&lt;br /&gt;
Node nodes[N];        // Вершины(точки), заданные начальными условиями&lt;br /&gt;
Edge edges[size];        // Все расстояния между вершинами &lt;br /&gt;
vector&amp;lt;Edge&amp;gt; MST;        // Рёбра минимального остовного дерева&lt;br /&gt;
read_data(&amp;amp;nodes, file_name);        // Считывание начальных данных&lt;br /&gt;
&lt;br /&gt;
//----------------Часть 1--------------//&lt;br /&gt;
for(int i = 0; i &amp;lt; N-1; i++) {&lt;br /&gt;
  for(int j = i+1; j &amp;lt; N; j++) {&lt;br /&gt;
	double distance = getDistance(node[i], node[j]);        // Вычисление расстояния между вершинами&lt;br /&gt;
	edges[i*N + j] = Edge(i,j,distance);        // Запись значения расстояния в массив&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//----------------Часть 2--------------//&lt;br /&gt;
sort(edges, size);        // Сортировка рёбер графа в порядке возрастания&lt;br /&gt;
for(int i = 0; i &amp;lt; size; i++) {        // Обходим список рёбер по возрастанию &lt;br /&gt;
	if(find_set(edges[i].first) != find_set(edges[i].second)) {        // Если вершины принадлежат разным подграфам - связываем их&lt;br /&gt;
          Union(edges[i].first, edges[i].second);        // Если вершины принадлежат разным подграфам - связываем их&lt;br /&gt;
	  MST.push_back(edges[i]);        // Запись использованного ребра в минимальное остовное дерево&lt;br /&gt;
	  edges[i]-&amp;gt;is_in_MST = true;     // Запись использованного ребра в минимальное остовное дерево&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//----------------Часть 3--------------//&lt;br /&gt;
for(int i = MST.size() - 1; i &amp;gt; MST.size() - 1 - number_of_clusters; i--) {&lt;br /&gt;
	remove_edge(MST, i);        // Удаление самых длинных рёбер из MST - формирование кластеров&lt;br /&gt;
}&lt;br /&gt;
write_data(MST);        // Запись выходных данных&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как описано в пункте 1.3, весь описываемый алгоритм (начиная от момента получения данных и заканчивая моментом, когда данные отданы на вывод), может быть условно разделён на три части. В то время как первая часть может быть достаточно эффективно решена &amp;quot;в лоб&amp;quot;, вторая и третьи части, как указано в пункте 1.1, могут иметь альтернативы. В данном фрагменте кода подробно описано использование [[Алгоритм Крускала | Алгоритма Крускала]] для построения MST, и алгоритма SEMST для разбиения на кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как и в остальных пунктах, здесь сложность алгоритма состоит их суммы сложностей трёх его составных частей. Если оценивать сложность асимптотически в зависимости от входных данных, то:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T(n) = O(n^2) + O(\frac{n (n-1)}{2})log(\frac{n (n-1)}{2})) + O(1) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На первый взгляд второе слагаемое может показаться слишком большим. Однако, верны и следующие равенства:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; O(\frac{n (n-1)}{2} log(\frac{n (n-1)}{2}) = O(n^2 log(n^2)) = O(2 n^2 log(n)) = O(n^2 log(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^2 log(n)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:Graph_algo10.png|frame|center|alt=Алгоритм| Макро-описание параллельного алгоритма кластеризации]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Данная задача хорошо распараллеливается благодаря тому, что все &amp;quot;тяжелые&amp;quot; вычислительные задачи, начиная с момента считывания входных данных, можно распределить по процессам практически равномерно. Это достигается за счет следующей реализации:&lt;br /&gt;
#Мастер-процесс считывает входные данные и отправляет их остальным процессам с помощью MPI_Broadcast.&lt;br /&gt;
#Все пары точек разбиваются так, что каждому процессу достается одинаковое количество пар.&lt;br /&gt;
#Каждый процесс-рабочий строит MST на своём наборе пар точек.&lt;br /&gt;
#Процессы объединяют свои деревья по алгоритму, изображенному в информационном графе. То есть объединение осуществляется последовательно между группами размера &amp;lt;math&amp;gt;2^k&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; k \in 1,2,..log_2p&amp;lt;/math&amp;gt;, где p - число процессов.&lt;br /&gt;
&lt;br /&gt;
Таким образом &#039;&#039;&#039;высота Ярусно-Параллельной формы&#039;&#039;&#039; (при наличии &amp;lt;math&amp;gt; p = 2^i &amp;lt;/math&amp;gt; процессоров) будет &amp;lt;math&amp;gt; 1 + i + 1 + 1 &amp;lt;/math&amp;gt;. &lt;br /&gt;
В то же время, как будет показано в пункте 1.10, оптимальное количетво процессоров может быть оценено по формуле  &amp;lt;math&amp;gt; p(N) \approx \sqrt {\frac{N-1}{2}} &amp;lt;/math&amp;gt;. Тогда &#039;&#039;&#039;ширину Ярусно-Параллельной формы&#039;&#039;&#039; можно оценить как &amp;lt;math&amp;gt; O(\sqrt{N}) &amp;lt;/math&amp;gt;. Учитывая приближенную формулу для числа процессоров, оценим высоту ЯП при помощи соотношения &amp;lt;math&amp;gt; 2^i \approx \sqrt {\frac{N-1}{2}} &amp;lt;/math&amp;gt;. Тогда высоту ЯП можно оценить как &amp;lt;math&amp;gt; O(log(N)) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Говоря о праллельной реализации вообще и, в частности, обращаясь к информационному графу, видно, что в основе параллельного алгоритма лежит массовый параллелизм, и единственным проблематичным местом может оказаться самый начальный этап считывания данных мастер-процессом с последующей рассылкой их всем процессам-рабочим. Проблема в том, что объем данных, пересылаемых каждому процессу, практически нельзя уменьшить из-за того, что в расчете MST на каждом наборе рёбер потенциально могут участвовать все вершины графа, и поэтому для их обработки требуется знание их пространственных координат.&lt;br /&gt;
&lt;br /&gt;
Самой &amp;quot;тяжелой&amp;quot; операцией здесь является самый первый расчёт MST, поскольку он выполняется чуть больше чем за квадратичное время. Все последующие объединения деревьев занимают асимптотически одинаковое количество процессорного времени и не зависят друг от друга (вплоть до последнего объединения в MST).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Входные данные ====&lt;br /&gt;
В качестве входных данных программе дается множество векторов &amp;lt;math&amp;gt;  [x_1,x_2,...,x_N]&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; x_j = (a_{1j}, a_{2j}, ..., a_{nj})&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Для удобства изучения свойств алгоритма и отображения результатов можно брать массив векторов размерности 2. В этом случае наглядность будет обеспечена естественной возможностью отобразить вектора в плоскости.&lt;br /&gt;
&lt;br /&gt;
Кроме того может оказаться полезным особым образом подготовить данные перед анализом (удаление &amp;quot;шумов&amp;quot;, выбросов и масштабирование данных по тем или иным координатам).&lt;br /&gt;
==== Выходные данные ====&lt;br /&gt;
Выходными данными программы будет множество &amp;lt;math&amp;gt; M = ((x_1,x_2,..,x_k),(x_{k+1},...),...) &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;(x_{i}, Cluster-Number)&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; p = 2^i &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; i \in \mathbb {N} &amp;lt;/math&amp;gt;. Ясно, что такое допущение не повлияет на качество анализа, так как другое число процессоров лишь приведет к незначительным накладным расходам, сложность которых асимптотически стремится к нулю. В таком случае можно также ввести допущение о размере входных данных: &amp;lt;math&amp;gt; N = 2^j &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; j \in \mathbb {N}, j &amp;gt; i &amp;lt;/math&amp;gt;. В этом случае MST-часть дерева графа алгоритма является идеально сбалансированным бинарным деревом. Тогда сложность можно оценить следующим образом:&lt;br /&gt;
#На первом этапе каждый процесс производит расчет на &amp;lt;math&amp;gt; 2^{j - i} * (2^j - 1)&amp;lt;/math&amp;gt; рёбрах, получая локальное MST(или MSF) на не более чем &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt; вершинах. &lt;br /&gt;
#Далее следует &amp;lt;math&amp;gt; i = log_2(2^i) &amp;lt;/math&amp;gt; шагов объединения не более чем по N рёбер в каждом.&lt;br /&gt;
&lt;br /&gt;
Таким образом, задача эффективного запуска алгоритма состоит в том, чтобы найти оптимальный баланс между количеством расчетов на каждом узле на первом шаге и общим количеством объединений поддеревьев. Пусть дано &amp;lt;math&amp;gt; p = 2^i &amp;lt;/math&amp;gt; вычислительных узлов и &amp;lt;math&amp;gt; N = 2^j &amp;lt;/math&amp;gt;. В таком случае каждый узел получает считает &amp;lt;math&amp;gt; 2^{j - i} * (2^j - 1)&amp;lt;/math&amp;gt; рёбер на первом шаге. Высота части ЯП-формы, отвечающей за MST, равна &amp;lt;math&amp;gt; i &amp;lt;/math&amp;gt;. Тогда всего будет &amp;lt;math&amp;gt; i &amp;lt;/math&amp;gt; уровней объединений MST с числом рёбер не более чем &amp;lt;math&amp;gt; 2N &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;  T&#039;(N,p) = \frac{N(N - 1)}{2 p} * logN  + 2N*log(N) * log_2p&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Объяснение формулы: первое слагаемое, исходя из оценки для сложности алгоритма Прима: &amp;lt;math&amp;gt; O(ElogV) &amp;lt;/math&amp;gt;, - время(число операций) расчета на каждом процессоре на первом шаге. После первого шага (поскольку построено минимальное остовное дерево) каждый процесс передает не более чем &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt; рёбер дальше. Второе слагаемое: каждый процесс после первого шага получает не более чем &amp;lt;math&amp;gt; 2N &amp;lt;/math&amp;gt; рёбер (от двух процессоров при объединении), и всего таких шагов объединения не более чем &amp;lt;math&amp;gt; log_2p &amp;lt;/math&amp;gt;, при этом предполагается, что сложностью объединения &amp;lt;math&amp;gt; 2N &amp;lt;/math&amp;gt; рёбер на каждом шаге можно пренебречь (или допустить его пренебрежительно малым), поскольку предполагается, что все процессы после первого шага получают свою часть уже отсортированных рёбер(то есть наибольшее ребро процессора &amp;lt;math&amp;gt; i &amp;lt;/math&amp;gt; меньше наименьшего ребра процессора &amp;lt;math&amp;gt; i+1 &amp;lt;/math&amp;gt;), и объединение будет просто конкатенацией блоков памяти.&lt;br /&gt;
&lt;br /&gt;
С помощью этой формулы можно построить графики зависимости времени вычислений от количества процессоров и входных данных.Поскольку рост вычислительного времени при увеличении объёма входных данных достаточно очевиден, этот объём будет зафиксирован, и будет исследоваться поведение приближенного априорного вычислительного времени алгоритма в зависимости от числа процессоров. Проведём два вычислительных эксперимента:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt; N = 10^4&amp;lt;/math&amp;gt;&lt;br /&gt;
[[File:График1_.JPG|frame|center|alt=Алгоритм| По оси абсцисс: количество вычислительных узлов. По оси ординат: время вычислений. При фиксированном &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt; N = 1.5*10^4 &amp;lt;/math&amp;gt;&lt;br /&gt;
[[File:График2_.JPG|frame|center|alt=Алгоритм| По оси абсцисс: количество вычислительных узлов. По оси ординат: время вычислений. При фиксированном &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Из этих графиков становится видно качественное поведение времени вычислений при увеличении числа процессоров. Можно заметить, что для каждого набора входных данных есть число процессоров, минимизирующее время вычислений. Но в то же время видно, что при слишком большом количестве процессоров алгоритм начинает работать хуже, чем при меньшем. Это вполне понятно: Слишком большое число процессоров порождает большое число обменов между ними, объединений деревьев, сортировок, и т.д. Таким образом, именно эта часть &amp;lt;math&amp;gt;  2N*log(N) * log_2p &amp;lt;/math&amp;gt; оценки времени вычислений отвечает за ухудшение эффективности алгоритма при числе процессоров, которое больше оптимального. Кроме того, видно, что при увеличении объёма входных данных оптимальное количество процессоров также увеличивается: количество обменов и слияний деревьев компенсируется уменьшением вычислительной нагрузки на каждый отдельный процессор.&lt;br /&gt;
&lt;br /&gt;
Благодаря такой оценке становится возможным оценить примерное оптимальное число процессоров &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; в зависимости от объёма входных данных &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;. Продифференцировав приближенную формулу &amp;lt;math&amp;gt; T&#039;(N,p) = \frac{N(N - 1)}{2 p} * logN  + 2N*log(N) * log_2p &amp;lt;/math&amp;gt; по &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt; и приравняв производную нулю, получим следующее выражение для оптимального числа процессоров:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; p(N) \approx \frac{N-1}{4} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Вычислительная мощность параллельного алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Согласно определению, [[глоссарий#Вычислительная мощность|&#039;&#039;вычислительная мощность&#039;&#039;]] алгоритма равна отношению числа операций к суммарному объему входных и выходных данных. В то же время, учитывая вышесказанное, видно, что число операций в данном алгоритме зависит не только от объема входных данных, но также от числа процессоров. Таким образом, имеет смысл оценить вычислительную мощность как в случае оптимального числа процессоров, так и в общем случае числа процессоров, равного &amp;lt;math&amp;gt; p &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В общем случае, исходя из предыдущего пункта, число операций будет равно&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \frac{N(N - 1)}{2} * logN  + \sum_{i = 0}^{log_2p}(2N*log(N) * \frac{p}{2^i}) = N logN (\frac{N-1}{2} + 2 * p * \sum_{i = 0}^{log_2p}( \frac{1}{2^i})) = &amp;lt;/math&amp;gt; {переходя к пределу в сумме} &amp;lt;math&amp;gt; \approx  N logN (\frac{N-1}{2} + 2 * p) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, вычислительная мощность в общем случае: &amp;lt;math&amp;gt; \approx \frac{N logN (\frac{N-1}{2} + 2 * p)}{2N} = \frac{logN *(\frac{N-1}{2} + 2 * p)}{2} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В случае оптимального числа вычислительных узлов:  &amp;lt;math&amp;gt; \approx \frac{N logN (\frac{N-1}{2} + \frac{N-1}{2})}{2N} =  \frac{logN *  (N-1)}{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;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Поскольку оптимальное число процессоров достаточно велико и линейно зависит от объема(размера) входных данных, задача хорошо масштабируется и на графике ниже можно увидеть почти линейный рост производительности при фиксированном количестве входных данных и увеличении числа процессоров. При малом объеме данных (1000 точек) виден выход на логистическую кривую при приближении числа процессоров к 256 с последующим падением производительности при дальнейшем увеличении числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[File:Efficiency2.JPG|frame|center|alt=Алгоритм| Производительность алгоритма]]&lt;br /&gt;
&lt;br /&gt;
Говоря о вычислительном времени и проводя тесты на реальных данных, можно заметить концептуальную правильность сделанных качественных выводов о поведении времени вычислений для различного набора входных данных и числа процессоров. На графиках ниже видно, что время вычисления задачи уменьшается до определенного момента, когда количество процессоров, нужное для задачи, становится слишком большим, после чего начинает увеличиваться, когда число процессоров растет.&lt;br /&gt;
&lt;br /&gt;
 [[File:Timeline1.JPG|frame|center|alt=Алгоритм| Вычислительное время алгоритма для &amp;lt;math&amp;gt;N = 17000&amp;lt;/math&amp;gt;]]&lt;br /&gt;
 [[File:Снимок2.JPG|frame|center|alt=Алгоритм| Вычислительное время алгоритма для &amp;lt;math&amp;gt;N = 3000&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;
Как таковые, алгоритмы самого процесса кластеризации в популярных библиотеках (таких, как Scipy, Numpy или Boost) не реализованы (в отличие от других алгоритмов кластеризации). Однако, можно найти пользовательские любительские реализации (некоторые из которых приведены в списке ниже). Кроме того, существует масса реализаций основного коспонента данного алгоритма - вычисления MST - на многих языках программирования в разных библиотеках. Ссылки на некоторые из этих реализаций(перечислить все было бы невозможно) также приведены в списке ниже.&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/jakevdp/mst_clustering  Minimum Spanning Tree Clustering in Python  Author: Jake VanderPlas]&lt;br /&gt;
*[https://github.com/amueller/information-theoretic-mst Information Theoretic Clustering using Minimum Spanning Trees]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_51_0/libs/graph/doc/prim_minimum_spanning_tree.html C++ Boost library prim_minimum_spanning_tree]&lt;br /&gt;
*[http://www.boost.org/doc/libs/1_35_0/libs/graph/doc/kruskal_min_spanning_tree.html C++ Boost library kruskal_minimum_spanning_tree]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html Java library JGraphT PrimMinimumSpanningTree]&lt;br /&gt;
*[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html Java library JGraphT  KruskalMinimumSpanningTree] &lt;br /&gt;
*[http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.sparse.csgraph.minimum_spanning_tree.html SciPy library MST]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Zhum</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:Astandrik&amp;diff=17523</id>
		<title>Обсуждение участника:Astandrik</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:Astandrik&amp;diff=17523"/>
		<updated>2016-11-16T07:23:48Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* на первом графике производительно названа эффективностью&lt;br /&gt;
* &amp;quot;виден выход ... при приближении числа процессоров к 256 с последующим падением производительности при дальнейшем увеличении числа процессоров&amp;quot; - дальнейшего увеличения числа процессоров нет на графике, поэтому из него этого не следует...&lt;br /&gt;
* нет описания аппаратной платформы&lt;br /&gt;
* нет текста программы&lt;br /&gt;
* нет указания на реализацию алгоритма (своя или сторонняя)&lt;br /&gt;
&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 10:23, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</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:Denemmy&amp;diff=17522</id>
		<title>Обсуждение участника:Denemmy</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:Denemmy&amp;diff=17522"/>
		<updated>2016-11-16T07:16:22Z</updated>

		<summary type="html">&lt;p&gt;Zhum: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;п. 2.4&lt;br /&gt;
&lt;br /&gt;
* нет описания аппаратной платформы&lt;br /&gt;
* нет указания на реализацию алгоритма (своя или сторонняя)&lt;br /&gt;
[[Участник:Zhum|Zhum]] ([[Обсуждение участника:Zhum|обсуждение]]) 10:16, 16 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Zhum</name></author>
	</entry>
</feed>