<?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=Innot</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=Innot"/>
	<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/Innot"/>
	<updated>2026-10-22T22:07:58Z</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21489</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21489"/>
		<updated>2016-12-21T22:54:10Z</updated>

		<summary type="html">&lt;p&gt;Innot: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP увеличение количества узлов вело только к увеличению времени выполнения; оценки, полученные при распараллеливании этой функции далее не приводятся. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    #pragma omp parallel for reduction(+:number_links)&lt;br /&gt;
    for (size_t ii = 0; ii &amp;lt; cluster1.size(); ++ii) {&lt;br /&gt;
        for (size_t jj = 0; jj &amp;lt; cluster2.size(); ++jj) {&lt;br /&gt;
            std::size_t i = cluster1[ii];&lt;br /&gt;
            std::size_t j = cluster2[jj];&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь для тестирования использовалась система с конфигурацией AMD FX-8120 4027 MHz, 24G DDR3 1866.&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
&lt;br /&gt;
    int procNum = 0;&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;procNum);&lt;br /&gt;
&lt;br /&gt;
    const size_t mSize = m_adjacency_matrix.size();&lt;br /&gt;
    const size_t q = mSize / procNum;&lt;br /&gt;
    const size_t blockSize = (mSize % procNum == 0) ? q : (q + 1);&lt;br /&gt;
&lt;br /&gt;
    MPI_Request reqs[mSize];&lt;br /&gt;
    MPI_Request sendReqs[blockSize];&lt;br /&gt;
    MPI_Status stats[mSize];&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        for (size_t i = 0; i &amp;lt; blockSize; ++i) {&lt;br /&gt;
            reqs[i] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        for (size_t r = 1; r &amp;lt; procNum; ++r) {&lt;br /&gt;
            for (size_t i = r * blockSize; i &amp;lt; (r + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, r, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
        for (size_t j = i + 1; j &amp;lt; mSize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        MPI_Waitall(mSize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        size_t j = 0;&lt;br /&gt;
        for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i, ++j) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, sendReqs + j);&lt;br /&gt;
        }&lt;br /&gt;
        for (; j &amp;lt; blockSize; ++j) {&lt;br /&gt;
            sendReqs[j] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(blockSize, sendReqs, stats);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тестирование было выполнено на кластере «Ломоносов» с узлами Intel Xeon E5-2697 v3 2.60GHz 64G DDR3. Результаты приведены ниже в форме графиков соответствия времени выполнения размеру задачи для последовательно выполняемой версии, а также зависимость времени выполнения тестов Chainlink (800 точек) и WingNut (1000 точек) от количества узлов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rock.create_adjacency_matrix.png]]&lt;br /&gt;
[[Файл:Rock.size.to.time.png]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, попытки распараллеливания данной реализации алгоритма не привели к уменьшению времени ее работы, что согласуется с оценками параллельного ресурса самого алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21488</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21488"/>
		<updated>2016-12-21T22:45:16Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP увеличение количества узлов вело только к увеличению времени выполнения; оценки, полученные при распараллеливании этой функции далее не приводятся. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    #pragma omp parallel for reduction(+:number_links)&lt;br /&gt;
    for (size_t ii = 0; ii &amp;lt; cluster1.size(); ++ii) {&lt;br /&gt;
        for (size_t jj = 0; jj &amp;lt; cluster2.size(); ++jj) {&lt;br /&gt;
            std::size_t i = cluster1[ii];&lt;br /&gt;
            std::size_t j = cluster2[jj];&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь для тестирования использовалась система с конфигурацией AMD FX-8120 4027 MHz, 24G DDR3 1866.&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
&lt;br /&gt;
    int procNum = 0;&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;procNum);&lt;br /&gt;
&lt;br /&gt;
    const size_t mSize = m_adjacency_matrix.size();&lt;br /&gt;
    const size_t q = mSize / procNum;&lt;br /&gt;
    const size_t blockSize = (mSize % procNum == 0) ? q : (q + 1);&lt;br /&gt;
&lt;br /&gt;
    MPI_Request reqs[mSize];&lt;br /&gt;
    MPI_Request sendReqs[blockSize];&lt;br /&gt;
    MPI_Status stats[mSize];&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        for (size_t i = 0; i &amp;lt; blockSize; ++i) {&lt;br /&gt;
            reqs[i] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        for (size_t r = 1; r &amp;lt; procNum; ++r) {&lt;br /&gt;
            for (size_t i = r * blockSize; i &amp;lt; (r + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, r, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
        for (size_t j = i + 1; j &amp;lt; mSize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        MPI_Waitall(mSize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        size_t j = 0;&lt;br /&gt;
        for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i, ++j) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, sendReqs + j);&lt;br /&gt;
        }&lt;br /&gt;
        for (; j &amp;lt; blockSize; ++j) {&lt;br /&gt;
            sendReqs[j] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(blockSize, sendReqs, stats);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тестирование было выполнено на кластере «Ломоносов» с узлами Intel Xeon E5-2697 v3 2.60GHz 64G DDR3. Результаты приведены ниже в форме графиков соответствия времени выполнения размеру задачи для последовательно выполняемой версии, а также зависимость времени выполнения тестов Chainlink (800 точек) и WingNut (1000 точек) от количества узлов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rock.create_adjacency_matrix.png]]&lt;br /&gt;
[[Файл:Rock.size.to.time.png]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, попытки распараллеливания данной реализации алгоритма не привели к уменьшению времени ее работы, что согласуется с оценками параллельного ресурса самого алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21487</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21487"/>
		<updated>2016-12-21T22:44:16Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP увеличение количества узлов вело только к увеличению времени выполнения; оценки, полученные при распараллеливании этой функции далее не приводятся. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    #pragma omp parallel for reduction(+:number_links)&lt;br /&gt;
    for (size_t ii = 0; ii &amp;lt; cluster1.size(); ++ii) {&lt;br /&gt;
        for (size_t jj = 0; jj &amp;lt; cluster2.size(); ++jj) {&lt;br /&gt;
            std::size_t i = cluster1[ii];&lt;br /&gt;
            std::size_t j = cluster2[jj];&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь для тестирования использовалась система с конфигурацией AMD FX-8120 4027 MHz, 24G DDR3 1866.&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
&lt;br /&gt;
    int procNum = 0;&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;procNum);&lt;br /&gt;
&lt;br /&gt;
    const size_t mSize = m_adjacency_matrix.size();&lt;br /&gt;
    const size_t q = mSize / procNum;&lt;br /&gt;
    const size_t blockSize = (mSize % procNum == 0) ? q : (q + 1);&lt;br /&gt;
&lt;br /&gt;
    MPI_Request reqs[mSize];&lt;br /&gt;
    MPI_Request sendReqs[blockSize];&lt;br /&gt;
    MPI_Status stats[mSize];&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        for (size_t i = 0; i &amp;lt; blockSize; ++i) {&lt;br /&gt;
            reqs[i] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        for (size_t r = 1; r &amp;lt; procNum; ++r) {&lt;br /&gt;
            for (size_t i = r * blockSize; i &amp;lt; (r + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, r, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
        for (size_t j = i + 1; j &amp;lt; mSize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        MPI_Waitall(mSize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        size_t j = 0;&lt;br /&gt;
        for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i, ++j) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, sendReqs + j);&lt;br /&gt;
        }&lt;br /&gt;
        for (; j &amp;lt; blockSize; ++j) {&lt;br /&gt;
            sendReqs[j] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(blockSize, sendReqs, stats);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тестирование было выполнено на кластере «Ломоносов» с узлами Intel Xeon E5-2697 v3 2.60GHz 64G DDR3. Результаты приведены ниже в форме графиков соответствия времени выполнения размеру задачи для последовательно выполняемой версии, а также зависимость времени выполнения тестов Chainlink (600 точек) и WingNut (800 точек) от количества узлов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rock.create_adjacency_matrix.png]]&lt;br /&gt;
[[Файл:Rock.size.to.time.png]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, попытки распараллеливания данной реализации алгоритма не привели к уменьшению времени ее работы, что согласуется с оценками параллельного ресурса самого алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21486</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21486"/>
		<updated>2016-12-21T18:31:29Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    #pragma omp parallel for reduction(+:number_links)&lt;br /&gt;
    for (size_t ii = 0; ii &amp;lt; cluster1.size(); ++ii) {&lt;br /&gt;
        for (size_t jj = 0; jj &amp;lt; cluster2.size(); ++jj) {&lt;br /&gt;
            std::size_t i = cluster1[ii];&lt;br /&gt;
            std::size_t j = cluster2[jj];&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
&lt;br /&gt;
    int procNum = 0;&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;procNum);&lt;br /&gt;
&lt;br /&gt;
    const size_t mSize = m_adjacency_matrix.size();&lt;br /&gt;
    const size_t q = mSize / procNum;&lt;br /&gt;
    const size_t blockSize = (mSize % procNum == 0) ? q : (q + 1);&lt;br /&gt;
&lt;br /&gt;
    MPI_Request reqs[mSize];&lt;br /&gt;
    MPI_Request sendReqs[blockSize];&lt;br /&gt;
    MPI_Status stats[mSize];&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        for (size_t i = 0; i &amp;lt; blockSize; ++i) {&lt;br /&gt;
            reqs[i] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        for (size_t r = 1; r &amp;lt; procNum; ++r) {&lt;br /&gt;
            for (size_t i = r * blockSize; i &amp;lt; (r + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, r, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
        for (size_t j = i + 1; j &amp;lt; mSize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        MPI_Waitall(mSize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        size_t j = 0;&lt;br /&gt;
        for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i, ++j) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, sendReqs + j);&lt;br /&gt;
        }&lt;br /&gt;
        for (; j &amp;lt; blockSize; ++j) {&lt;br /&gt;
            sendReqs[j] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(blockSize, sendReqs, stats);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rock.create_adjacency_matrix.png|600px]]&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/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21481</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=21481"/>
		<updated>2016-12-21T15:19:50Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    #pragma omp parallel for reduction(+:number_links)&lt;br /&gt;
    for (size_t ii = 0; ii &amp;lt; cluster1.size(); ++ii) {&lt;br /&gt;
        for (size_t jj = 0; jj &amp;lt; cluster2.size(); ++jj) {&lt;br /&gt;
            std::size_t i = cluster1[ii];&lt;br /&gt;
            std::size_t j = cluster2[jj];&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
&lt;br /&gt;
    int procNum = 0;&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;procNum);&lt;br /&gt;
&lt;br /&gt;
    const size_t mSize = m_adjacency_matrix.size();&lt;br /&gt;
    const size_t q = mSize / procNum;&lt;br /&gt;
    const size_t blockSize = (mSize % procNum == 0) ? q : (q + 1);&lt;br /&gt;
&lt;br /&gt;
    MPI_Request reqs[mSize];&lt;br /&gt;
    MPI_Request sendReqs[blockSize];&lt;br /&gt;
    MPI_Status stats[mSize];&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        for (size_t i = 0; i &amp;lt; blockSize; ++i) {&lt;br /&gt;
            reqs[i] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        for (size_t r = 1; r &amp;lt; procNum; ++r) {&lt;br /&gt;
            for (size_t i = r * blockSize; i &amp;lt; (r + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, r, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i) {&lt;br /&gt;
        for (size_t j = i + 1; j &amp;lt; mSize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (m_rank == 0) {&lt;br /&gt;
        MPI_Waitall(mSize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        size_t j = 0;&lt;br /&gt;
        for (size_t i = m_rank * blockSize; i &amp;lt; (m_rank + 1) * blockSize &amp;amp;&amp;amp; i &amp;lt; mSize; ++i, ++j) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), mSize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, sendReqs + j);&lt;br /&gt;
        }&lt;br /&gt;
        for (; j &amp;lt; blockSize; ++j) {&lt;br /&gt;
            sendReqs[j] = MPI_REQUEST_NULL;&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(blockSize, sendReqs, stats);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rock.create_adjacency_matrix.png|400px]]&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/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Innot</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rock.create_adjacency_matrix.png&amp;diff=21480</id>
		<title>Файл:Rock.create adjacency matrix.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rock.create_adjacency_matrix.png&amp;diff=21480"/>
		<updated>2016-12-21T15:16:40Z</updated>

		<summary type="html">&lt;p&gt;Innot: Performance of ROCK clustering algorithm with one method severely paralellized. Two testcases.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Performance of ROCK clustering algorithm with one method severely paralellized. Two testcases.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18326</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18326"/>
		<updated>2016-11-18T20:49:07Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Исходная версия алгоритма показала следующие результаты. Здесь и далее для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Название теста&lt;br /&gt;
! Время выполнения (сек)&lt;br /&gt;
! Количество точек&lt;br /&gt;
! Количество кластеров&lt;br /&gt;
|-&lt;br /&gt;
| Chainlink&lt;br /&gt;
| 312.7&lt;br /&gt;
| 1500&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| TwoDiamonds&lt;br /&gt;
| 160.4&lt;br /&gt;
| 1270&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| Target&lt;br /&gt;
| 142.9&lt;br /&gt;
| 1150&lt;br /&gt;
| 6&lt;br /&gt;
|-&lt;br /&gt;
| WingNut&lt;br /&gt;
| 1324.6&lt;br /&gt;
| 2400&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| EngyTime&lt;br /&gt;
| 2728.09&lt;br /&gt;
| 4096&lt;br /&gt;
| 13&lt;br /&gt;
|-&lt;br /&gt;
| GolfBall&lt;br /&gt;
| 2429.69&lt;br /&gt;
| 4002&lt;br /&gt;
| 13&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    for (auto i : cluster1)&lt;br /&gt;
        for (auto j : cluster2)&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
    return number_links;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    auto m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
    int numprocs = 0, rank = 0, len = 0, rc = 0;&lt;br /&gt;
    char hostname[MPI_MAX_PROCESSOR_NAME];&lt;br /&gt;
&lt;br /&gt;
    MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;numprocs);&lt;br /&gt;
    MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;rank);&lt;br /&gt;
    MPI_Get_processor_name(hostname, &amp;amp;len);&lt;br /&gt;
    printf (&amp;quot;Number of tasks= %d My rank= %d Running on %s\n&amp;quot;, numprocs, rank, hostname);&lt;br /&gt;
&lt;br /&gt;
    size_t msize = m_adjacency_matrix.size();&lt;br /&gt;
    unsigned num_tasks = msize / numprocs;&lt;br /&gt;
    unsigned delta = msize % numprocs;&lt;br /&gt;
    MPI_Request reqs[msize];&lt;br /&gt;
    MPI_Status stats[msize];&lt;br /&gt;
&lt;br /&gt;
    if (rank == 0) {&lt;br /&gt;
        // Initialize async receives for rows computed at other nodes&lt;br /&gt;
        for (size_t r = 1; r != numprocs; r++) {&lt;br /&gt;
            for (size_t i = num_tasks*r; i &amp;lt; num_tasks*(r + 1); i++) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), msize, MPI_DOUBLE, r, MPI_ANY_TAG, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // compute what is needed&lt;br /&gt;
    for (size_t i = num_tasks*rank; i &amp;lt; num_tasks*(rank + 1); i++) {&lt;br /&gt;
        for (size_t j = 0; j &amp;lt; msize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (rank == 0) {&lt;br /&gt;
        // finish is there&#039;s anything left&lt;br /&gt;
        for (size_t i = num_tasks * numprocs; i != msize; i++) {&lt;br /&gt;
            for (size_t j = 0; j &amp;lt; msize; j++) {&lt;br /&gt;
                double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
&lt;br /&gt;
                if (distance &amp;lt; m_radius) {&lt;br /&gt;
                    m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(msize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        // send the data to node 0&lt;br /&gt;
        for (size_t i = num_tasks*rank; i &amp;lt; num_tasks*(rank + 1); i++) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), msize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, попытки распараллеливания данной реализации алгоритма не привели к уменьшению времени ее работы, что согласуется с оценками параллельного ресурса самого алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17029</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17029"/>
		<updated>2016-11-14T14:37:08Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из pyclustering, выполненная на C++. Исходная версия алгоритма показала следующие результаты. Здесь и далее для тестирования использовался суперкомпьютер «Ломоносов».&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Название теста&lt;br /&gt;
! Время выполнения (сек)&lt;br /&gt;
! Количество точек&lt;br /&gt;
! Количество кластеров&lt;br /&gt;
|-&lt;br /&gt;
| Chainlink&lt;br /&gt;
| 312.7&lt;br /&gt;
| 1500&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| TwoDiamonds&lt;br /&gt;
| 160.4&lt;br /&gt;
| 1270&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| Target&lt;br /&gt;
| 142.9&lt;br /&gt;
| 1150&lt;br /&gt;
| 6&lt;br /&gt;
|-&lt;br /&gt;
| WingNut&lt;br /&gt;
| 1324.6&lt;br /&gt;
| 2400&lt;br /&gt;
| 2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, calculate_links и create_adjacency_matrix. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод calculate_links в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    for (auto i : cluster1)&lt;br /&gt;
        for (auto j : cluster2)&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
    return number_links;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод create_adjacency_matrix также был распараллелен с помощью MPI. К качественному ускорению это не привело, в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения.&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/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17027</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17027"/>
		<updated>2016-11-14T13:47:12Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из pyclustering, выполненная на C++. Исходная версия алгоритма показала следующие результаты. Здесь и далее для тестирования использовался суперкомпьютер «Ломоносов».&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TODO&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, calculate_links и create_adjacency_matrix. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод calculate_links в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    for (auto i : cluster1)&lt;br /&gt;
        for (auto j : cluster2)&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
    return number_links;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод create_adjacency_matrix также был распараллелен с помощью MPI. К качественному ускорению это не привело, в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения.&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/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17020</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17020"/>
		<updated>2016-11-14T13:21:55Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из pyclustering, выполненная на C++. Исходная версия алгоритма показала следующие результаты.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TODO&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, calculate_links и create_adjacency_matrix. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод calculate_links в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    for (auto i : cluster1)&lt;br /&gt;
        for (auto j : cluster2)&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
    return number_links;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод create_adjacency_matrix также был распараллелен с помощью MPI. К качественному ускорению это не привело, в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17010</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=17010"/>
		<updated>2016-11-14T13:07:00Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Как показано на информационном графе, значительная часть алгоритма не предполагает параллельного выполнения. Взаимной независимостью обладают только фрагмент, где создаются локальные кучи, и ряд действий по перемещению точек между кучами.&lt;br /&gt;
&lt;br /&gt;
Для оценки производительности была выбрана реализация из pyclustering, выполненная на C++. Исходная версия алгоритма показала следующие результаты.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TODO&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, calculate_links и create_adjacency_matrix. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод calculate_links в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже.&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод create_adjacency_matrix также был распараллелен с помощью MPI. К качественному ускорению это не привело, в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13603</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13603"/>
		<updated>2016-10-24T19:59:10Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{O(n^{2,37}) + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;, если в качестве алгоритма вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = O(n^{2,37}) + O(n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13602</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13602"/>
		<updated>2016-10-24T19:49:01Z</updated>

		<summary type="html">&lt;p&gt;Innot: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{O(n^{2,37}) + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;, если в качестве алгоритма вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L = \frac{O(n^{2,37})&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13601</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13601"/>
		<updated>2016-10-24T19:44:03Z</updated>

		<summary type="html">&lt;p&gt;Innot: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{O(n^{2,37}) + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;, если в качестве алгоритма вычисления количества связей используется алгоритмом Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13600</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13600"/>
		<updated>2016-10-24T19:38:49Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Схема реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;C_L = TODO&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13599</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13599"/>
		<updated>2016-10-24T19:35:10Z</updated>

		<summary type="html">&lt;p&gt;Innot: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;C_L = TODO&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = Вектор из &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;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13598</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=13598"/>
		<updated>2016-10-24T19:22:20Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=12026</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=12026"/>
		<updated>2016-10-15T20:55:43Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Порогрвое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{C_L + O(n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11949</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11949"/>
		<updated>2016-10-15T20:47:18Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оцентка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Порогрвое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{{L_p} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11899</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11899"/>
		<updated>2016-10-15T20:42:59Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_t = C_L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;{L_p} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - время работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Порогрвое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 1. &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;
Рассматриваемый алгоритм является детерминированным. Его вычислительная мощность равна&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{{L_p} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11849</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11849"/>
		<updated>2016-10-15T20:37:41Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, временная сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и также равняется &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление кучи/куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;{L_p} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - время работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Порогрвое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 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;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{{L_p} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11828</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11828"/>
		<updated>2016-10-15T20:34:40Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Свойства и структура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление кучи/куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;{L_p} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - время работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Порогрвое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 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;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{{L_p} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11798</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11798"/>
		<updated>2016-10-15T20:29:43Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK.info.graph.png|400px|thumb|right|Визуализация информационного графа]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;Qs&#039;&#039;&#039; - вычисление локальных кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление кучи/куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;{L_p} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - время работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Порогрвое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &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;
 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;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{L + O(n^2\log n)}{{L_p} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{L_p}&amp;lt;/math&amp;gt; - соответственно последовательная и параллельная сложность алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&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; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;/div&gt;</summary>
		<author><name>Innot</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:ROCK.info.graph.png&amp;diff=11737</id>
		<title>Файл:ROCK.info.graph.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:ROCK.info.graph.png&amp;diff=11737"/>
		<updated>2016-10-15T20:18:12Z</updated>

		<summary type="html">&lt;p&gt;Innot: A diagram showing the data flow of a simple ROCK clustering algorithm implementation.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A diagram showing the data flow of a simple ROCK clustering algorithm implementation.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11214</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11214"/>
		<updated>2016-10-15T18:54:55Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Существующие реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1]:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда, вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Требуемое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3.&lt;br /&gt;
# Пакет CBA из репозитория CRAN содержит реализацию на R под GPL 2: [https://cran.r-project.org/web/packages/cba/index.html]&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11120</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11120"/>
		<updated>2016-10-15T18:32:41Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Схема реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда, вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо клатеризовать&lt;br /&gt;
 2. Желаемое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
 1. &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; полученных кластеров&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11064</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11064"/>
		<updated>2016-10-15T18:17:21Z</updated>

		<summary type="html">&lt;p&gt;Innot: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число соседей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
procedure compute_links(S)&lt;br /&gt;
begin&lt;br /&gt;
    Compute nbrlist[i] for every i in S&lt;br /&gt;
    Set link[i, j] to zero for all i, j&lt;br /&gt;
    for i := 1 to n do&lt;br /&gt;
    begin&lt;br /&gt;
        N := nbrlist[i]&lt;br /&gt;
        for j := 1 to |N| - 1 do&lt;br /&gt;
        begin&lt;br /&gt;
            for l := j + 1 to |N| do&lt;br /&gt;
            begin&lt;br /&gt;
                link[N[j], N[l]]] := link[N[j], N[l]] + 1&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&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;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо клатеризовать&lt;br /&gt;
 2. Желаемое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
 1. &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; полученных кластеров&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11000</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=11000"/>
		<updated>2016-10-15T18:02:19Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Схема реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число соседей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
procedure compute_links(S)&lt;br /&gt;
begin&lt;br /&gt;
    Compute nbrlist[i] for every i in S&lt;br /&gt;
    Set link[i, j] to zero for all i, j&lt;br /&gt;
    for i := 1 to n do&lt;br /&gt;
    begin&lt;br /&gt;
        N := nbrlist[i]&lt;br /&gt;
        for j := 1 to |N| - 1 do&lt;br /&gt;
        begin&lt;br /&gt;
            for l := j + 1 to |N| do&lt;br /&gt;
            begin&lt;br /&gt;
                link[N[j], N[l]]] := link[N[j], N[l]] + 1&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&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;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо клатеризовать&lt;br /&gt;
 2. Желаемое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
 1. &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; полученных кластеров&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10919</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10919"/>
		<updated>2016-10-15T17:48:05Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Схема реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число соседей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
	link := compute_links(S)&lt;br /&gt;
	for s in S do&lt;br /&gt;
	begin&lt;br /&gt;
		q[s] := build_local_heap(link, s)&lt;br /&gt;
	end&lt;br /&gt;
	Q := build_global_heap(S, q)&lt;br /&gt;
	while size(Q) &amp;gt; k do&lt;br /&gt;
	begin&lt;br /&gt;
		u := extract_max(Q)&lt;br /&gt;
		v := max(q[u])&lt;br /&gt;
		delete(Q, v)&lt;br /&gt;
		w := merge(u, v)&lt;br /&gt;
		for each x in q[u] or q[v] do&lt;br /&gt;
		begin&lt;br /&gt;
			link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
			delete(q[x], u)&lt;br /&gt;
			delete(q[x], v)&lt;br /&gt;
			insert(q[x], w, g(x, w))&lt;br /&gt;
			insert(q[w], x, g(x, w))&lt;br /&gt;
			update(Q, x, q[x])&lt;br /&gt;
		end&lt;br /&gt;
		insert(Q, w, q[w])&lt;br /&gt;
		deallocate(q[u])&lt;br /&gt;
		deallocate(q[v])&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда, вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;/div&gt;</summary>
		<author><name>Innot</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:Innot&amp;diff=10844</id>
		<title>Участник:Innot</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:Innot&amp;diff=10844"/>
		<updated>2016-10-15T17:32:58Z</updated>

		<summary type="html">&lt;p&gt;Innot: Новая страница: «Баранов Максим Сергеевич, ВМК МГУ, 627 гр.»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Баранов Максим Сергеевич, ВМК МГУ, 627 гр.&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10830</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10830"/>
		<updated>2016-10-15T17:31:02Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число соседей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда, вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10808</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10808"/>
		<updated>2016-10-15T17:24:34Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}}&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем и должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;TODO&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;TODO&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число соседей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда, вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создании очередей, и параллельная обработка локальных очередей во внутреннем цикле итерационного этапа алгоритма.&lt;br /&gt;
&lt;br /&gt;
TODO?&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;/div&gt;</summary>
		<author><name>Innot</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10712</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=10712"/>
		<updated>2016-10-15T17:07:51Z</updated>

		<summary type="html">&lt;p&gt;Innot: /* Свойства и структура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластрерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK) построен на основе понятия связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым.&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров алгоритм завершается.&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;math&amp;gt;TODO&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как такая функция не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем и должна обладать следующим свойством: каждая точка, принадлежащая кластеру Ci, имеет приблизительно &amp;lt;math&amp;gt;TODO&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения используется определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;TODO&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей: подсчет количества связей между точками и итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число соседей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&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;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда, вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; [3] (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество точек). Для случаев разреженное матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [4]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &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;
Итак, сложность описанного алгоритма будет&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L + O(n^2\log{n})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; - сложность алгоритма вычисления количества связей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Блок L соответствует предварительному подсчету числа связей точек, блок QLs и QG - созданию локальных и глобальной куч соответственно, блок M - слиянию кластеров, блок U - обновлению куч.&lt;br /&gt;
&lt;br /&gt;
TODO&lt;/div&gt;</summary>
		<author><name>Innot</name></author>
	</entry>
</feed>