<?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=Darel</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=Darel"/>
	<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/Darel"/>
	<updated>2026-10-22T21:58:17Z</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:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21301</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21301"/>
		<updated>2016-12-19T02:42:00Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Coctic}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O((log_2(n)+log_2(d)+log_2(k))i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо если разница значений функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; на текущей и предыдущей итерациях меньше некоторого заданного порога &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как указано выше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Распределение по кластерам. &lt;br /&gt;
Для рассчёта расстояния от данного вектора до всех кластеров можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. После этого нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. Данные операции для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, можно делать параллельно. &lt;br /&gt;
&amp;lt;math&amp;gt;O(log_2(k)+log_2(d))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Обновление кластерных центров &lt;br /&gt;
&amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Рассчёт &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; для каждой координаты каждого вектора. &lt;br /&gt;
Вычитаем, возводим в квадрат, складываем за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций и полученные значения складываем за &amp;lt;math&amp;gt;log_2(n)&amp;lt;/math&amp;gt; операций. &amp;lt;math&amp;gt;O(log_2(d)+log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На сходимость уйдёт &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность &amp;lt;math&amp;gt;O((log_2(n)+log_2(d)+log_2(k))i)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21300</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21300"/>
		<updated>2016-12-19T02:40:57Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Coctic}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо если разница значений функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; на текущей и предыдущей итерациях меньше некоторого заданного порога &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как указано выше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Распределение по кластерам. &lt;br /&gt;
Для рассчёта расстояния от данного вектора до всех кластеров можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. После этого нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. Данные операции для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, можно делать параллельно. &lt;br /&gt;
&amp;lt;math&amp;gt;O(log_2(k)+log_2(d))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Обновление кластерных центров &lt;br /&gt;
&amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Рассчёт &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; для каждой координаты каждого вектора. &lt;br /&gt;
Вычитаем, возводим в квадрат, складываем за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций и полученные значения складываем за &amp;lt;math&amp;gt;log_2(n)&amp;lt;/math&amp;gt; операций. &amp;lt;math&amp;gt;O(log_2(d)+log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На сходимость уйдёт &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность &amp;lt;math&amp;gt;O((log_2(n)+log_2(d)+log_2(k))i)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21292</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21292"/>
		<updated>2016-12-18T22:47:48Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|{{REVISIONUSER}}}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо если разница значений функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; на текущей и предыдущей итерациях меньше некоторого заданного порога &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как указано выше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояния от данного вектора до всех кластеров можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. После этого нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. Данные операции для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, можно делать параллельно. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21291</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21291"/>
		<updated>2016-12-18T22:44:08Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Макроструктура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|{{REVISIONUSER}}}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо если разница значений функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; на текущей и предыдущей итерациях меньше некоторого заданного порога &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как указано выше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21290</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=21290"/>
		<updated>2016-12-18T22:43:37Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|{{REVISIONUSER}}}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо если разница значений функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; на текущей и предыдущей итерациях меньше некоторого заданного порога &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18987</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18987"/>
		<updated>2016-11-22T23:09:35Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18986</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18986"/>
		<updated>2016-11-22T23:09:10Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
&lt;br /&gt;
Компилятор mpicc, опция -O2. Исследуемые точки: для 1024, 2048, 4096, 8192, 16384, 32768 векторов программа запускалась на 1, 2, 4, 8, 16, 32, 64, 128, 256 ядрах. Режим исполнения по умолчанию – SMP-режим, на каждый вычислительный узел один mpi процесс.&lt;br /&gt;
&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18015</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18015"/>
		<updated>2016-11-17T20:22:51Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18014</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=18014"/>
		<updated>2016-11-17T20:20:01Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Для масштабируемости использовалась следующая [http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html параллельная реализация алгоритма] с использованием MPI. Тесты были проведены на компьютерной системе [http://hpc.cmc.msu.ru/bgp BlueGene], которая включает в себя 8192 процессорных ядра и имеет пиковую производительность 27.9 терафлопс.&lt;br /&gt;
&lt;br /&gt;
Исследовалась сильная масштабируемость алгоритма. На графике видно, что алгоритм хорошо распараллеливается и достигается значительное ускорение. Рассчитывалось распределение десятимерных векторов на десять кластеров.&lt;br /&gt;
[[Файл:HCM_BlueGene.png]]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16113</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16113"/>
		<updated>2016-11-08T09:54:52Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16112</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16112"/>
		<updated>2016-11-08T09:53:54Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Литература */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling».(Электронное издание)] Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16111</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16111"/>
		<updated>2016-11-08T09:52:12Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Литература */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Technical University of Denmark, Department of Automation, Bldg 326, DK-2800 Lyngby, DENMARK.&lt;br /&gt;
Tech. report no 98-H-874 (nfmod), 30 Oct 1998. (Электронное издание)] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16110</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16110"/>
		<updated>2016-11-08T09:47:49Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16109</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=16109"/>
		<updated>2016-11-08T09:47:24Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Программная реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=15001</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=15001"/>
		<updated>2016-10-31T01:20:16Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=15000</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=15000"/>
		<updated>2016-10-31T01:18:25Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блок, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=14999</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=14999"/>
		<updated>2016-10-31T01:18:13Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке ниже приведён информационный граф алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Блока, определяющий ближайший кластер, подробнее.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=14998</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=14998"/>
		<updated>2016-10-31T01:05:17Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Общая схема работы алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph1.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Описание блока, определяющего ближайший кластер.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist1.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=14997</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=14997"/>
		<updated>2016-10-31T00:45:37Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[Участник:Darel|Ян-Мартин Тамм]]&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Общая схема работы алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Описание блока, определяющего ближайший кластер.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;[http://faculty.petra.ac.id/resmana/private/fuzzy/nfmod.pdf Jan Jantzen «Neurofuzzy Modelling». Электронное издание] &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11129</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11129"/>
		<updated>2016-10-15T18:34:34Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Программная реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Общая схема работы алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Описание блока, определяющего ближайший кластер.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11113</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11113"/>
		<updated>2016-10-15T18:29:48Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Общая схема работы алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Описание блока, определяющего ближайший кластер.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11097</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11097"/>
		<updated>2016-10-15T18:25:35Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Общая схема работы алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM graph.png]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Описание блока, определяющего ближайший кластер.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Файл:HCM_dist.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11082</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=11082"/>
		<updated>2016-10-15T18:22:22Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:HCM graph.png]]&lt;br /&gt;
[[Файл:HCM_dist.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=10983</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=10983"/>
		<updated>2016-10-15T17:59:18Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:HCM graph.png]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=8120</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=8120"/>
		<updated>2016-10-14T20:30:46Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Существующие реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6628</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6628"/>
		<updated>2016-10-13T17:03:05Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;(не обязательно)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6627</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6627"/>
		<updated>2016-10-13T17:02:32Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;не обязательно&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Прост в реализации и вычислениях&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6621</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6621"/>
		<updated>2016-10-13T16:59:12Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;не обязательно&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;\frac{kdi}{k+d}&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Программная реализация =&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
* [http://scikit-learn.org/ scikit-learn] библиотека машинного обучения для языка Python.&lt;br /&gt;
* [https://www.scipy.org/ SciPy] библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de ELKI] библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.&lt;br /&gt;
* [http://spark.apache.org/ Spark] распределённая версия алгоритма.&lt;br /&gt;
* [http://mlpack.org MLPACK] библиотека машинного обучения для C++.&lt;br /&gt;
* [http://opencv.org/ OpenCV] библиотека содержит реализацию алгоритма.&lt;br /&gt;
* [http://mahout.apache.org Apache Mahout] содержит реализацию алгоритма на базе MapReduce.&lt;br /&gt;
* [http://www.cs.waikato.ac.nz/~ml/weka Weka] библиотека и инструмент для анализа данных на языке Java.&lt;br /&gt;
* [http://torch.ch/ Torch] библиотека машинного обучения на базе языка программирования Lua.&lt;br /&gt;
* [https://spark.apache.org/ Apache Spark MLlib] содержит распределенную реализацию алгоритма.&lt;br /&gt;
* [http://www.accord-framework.net/ Accord.NET] содержит несколько различных реализаций на языке C#.&lt;br /&gt;
* [https://www.r-bloggers.com/k-means-clustering-in-r/ R k-means] реализация на языке R.&lt;br /&gt;
= Литература =&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Jan Jantzen «Neurofuzzy Modelling». &amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6574</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6574"/>
		<updated>2016-10-13T16:07:25Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;не обязательно&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;kdi&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют).&amp;lt;/li&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6573</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6573"/>
		<updated>2016-10-13T16:06:32Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;не обязательно&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Вычислительная мощность &amp;lt;math&amp;gt;ki&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют).&amp;lt;/li&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6569</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6569"/>
		<updated>2016-10-13T16:04:42Z</updated>

		<summary type="html">&lt;p&gt;Darel: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;не обязательно&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют).&amp;lt;/li&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6568</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6568"/>
		<updated>2016-10-13T16:02:52Z</updated>

		<summary type="html">&lt;p&gt;Darel: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(nkdi)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&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;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Распределение векторов по кластерам&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Пересчёт центров кластеров&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; векторов из множества всех векторов &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера &amp;lt;math&amp;gt;\rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2&amp;lt;/math&amp;gt;, после чего из них выбирается наименьшее и заносится в матрицу.&amp;lt;br&amp;gt;Подсчёт функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.&amp;lt;br&amp;gt;Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере. &amp;lt;math&amp;gt;&lt;br /&gt;
c = \frac{1}{|C|} \sum\limits_{u \in C} u&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def kmeans(points, k, cutoff):&lt;br /&gt;
    &lt;br /&gt;
    # Pick out k random points to use as our initial centroids&lt;br /&gt;
    initial = random.sample(points, k)&lt;br /&gt;
    &lt;br /&gt;
    # Create k clusters using those centroids&lt;br /&gt;
    clusters = [Cluster([p]) for p in initial]&lt;br /&gt;
    &lt;br /&gt;
    # Loop through the dataset until the clusters stabilize&lt;br /&gt;
    while True:&lt;br /&gt;
        # Create a list of lists to hold the points in each cluster&lt;br /&gt;
        lists = [ [] for c in clusters]&lt;br /&gt;
&lt;br /&gt;
        # For every point in the dataset ...&lt;br /&gt;
        for p in points:&lt;br /&gt;
            # Get the distance between that point and the centroid of the first cluster.&lt;br /&gt;
            smallest_distance = getDistance(p, clusters[0].centroid)&lt;br /&gt;
        &lt;br /&gt;
            # Set the cluster this point belongs to&lt;br /&gt;
            clusterIndex = 0&lt;br /&gt;
        &lt;br /&gt;
            # For the remainder of the clusters ...&lt;br /&gt;
            for i in range(clusterCount - 1):&lt;br /&gt;
                # calculate the distance of that point to each other cluster&#039;s centroid&lt;br /&gt;
                distance = getDistance(p, clusters[i+1].centroid)&lt;br /&gt;
                # If it&#039;s closer to that cluster&#039;s centroid update what we&lt;br /&gt;
                # think the smallest distance is, and set the point to belong to that cluster&lt;br /&gt;
                if distance &amp;lt; smallest_distance:&lt;br /&gt;
                    smallest_distance = distance&lt;br /&gt;
                    clusterIndex = i+1&lt;br /&gt;
            lists[clusterIndex].append(p)&lt;br /&gt;
        &lt;br /&gt;
        # Set our total_shift to zero for this iteration&lt;br /&gt;
        total_shift = 0.0&lt;br /&gt;
        &lt;br /&gt;
        # As many times as there are clusters ...&lt;br /&gt;
        for i in range(k):&lt;br /&gt;
            # Calculate how far the centroid moved in this iteration&lt;br /&gt;
            shift = clusters[i].update(lists[i])&lt;br /&gt;
            # Keep track of the total moves from all cluster centroid updates&lt;br /&gt;
            total_shift += shift&lt;br /&gt;
        &lt;br /&gt;
        # If the centroids have stopped moving much, we&#039;re done!&lt;br /&gt;
        if total_shift &amp;lt; cutoff:&lt;br /&gt;
            break&lt;br /&gt;
    return clusters&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Распределение по кластерам — для каждого из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов и каждого из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров посчитать расстояние, сложив &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; раз значения, на вычисление каждого из которых нужно 2 операции —&amp;amp;nbsp;&amp;lt;math&amp;gt;O(nkd)&amp;lt;/math&amp;gt;. Пересчёт центров — &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; раз сложить и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; раз поделить — &amp;amp;nbsp;&amp;lt;math&amp;gt;O(n+k)&amp;lt;/math&amp;gt;. На завершение алгоритма понадобится &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций. Итого сложность &amp;lt;math&amp;gt;O(nkdi)&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;d&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(d)&amp;lt;/math&amp;gt; операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов за &amp;lt;math&amp;gt;log_2(k)&amp;lt;/math&amp;gt; операций. И всё это нужно сделать для &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; итераций, общая сложность получается &amp;lt;math&amp;gt;O(log_2(k)log_2(d)i)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; &lt;br /&gt;
*&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d&amp;lt;/math&amp;gt; — множество векторов&lt;br /&gt;
*&amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt; — количество кластеров&lt;br /&gt;
*&amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; — порог остановки &#039;&#039;не обязательно&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nd+1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039;&lt;br /&gt;
*&amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; — матрица принадлежности векторов кластерам размера &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;. При этом данная матрица содержит только &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;nk&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Алгоритм является устойчивым — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего совсем не повлияют).&amp;lt;/li&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6255</id>
		<title>Участник:Ян-Мартин Тамм/Строгий алгоритм С средних</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%A1%D1%82%D1%80%D0%BE%D0%B3%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=6255"/>
		<updated>2016-10-13T01:32:31Z</updated>

		<summary type="html">&lt;p&gt;Darel: Новая страница: «{{algorithm | name              = Строгий алгоритм С средних (K-means, Hard C-Means) | serial_complexity = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; | pf_height…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Строгий алгоритм С средних (K-means, Hard C-Means)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;-&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;mn+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;cn&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Описание алгоритма =&lt;br /&gt;
&amp;lt;b&amp;gt;Строгий алгоритм C средних&amp;lt;/b&amp;gt;, более известный как &amp;lt;b&amp;gt;K-средних&amp;lt;/b&amp;gt;, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества. &amp;lt;br&amp;gt;Общая идея алгоритма такова: произвольным образом выбираем центры кластеров, после чего повторяем следующие два действия, пока не будет выполнено условие остановки:&lt;br /&gt;
#Распределяем каждый вектор в ближайший кластер&lt;br /&gt;
#Пересчитываем значения центров как среднее значение координат векторов в каждом кластере&lt;br /&gt;
Минимизируется сумма расстояний векторов от центров их кластеров. Остановка производится если либо значение данной функции становится ниже некоторого порога, либо мало изменяется по сравнению с предыдущей итерацией.&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Дано множество d-мерных векторов &amp;amp;nbsp;&amp;lt;math&amp;gt;U=\{u_1, u_2, ..., u_n\}&amp;lt;/math&amp;gt; и число кластеров &amp;lt;math&amp;gt;k \in \mathbb{N}&amp;lt;/math&amp;gt;. Требуется разбить множество &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; непересекающихся непустых кластеров &amp;lt;math&amp;gt;C_1, C_2, ..., C_k&amp;lt;/math&amp;gt; таких, что &amp;lt;math&amp;gt;{\bigcup \limits _{i = 1}^k C_i} = U &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;br&amp;gt;Для этого минимизируется функция &amp;lt;math&amp;gt;&lt;br /&gt;
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2 &lt;br /&gt;
&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;— центр масс векторов кластера &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Произвольным образом инициализируем центры кластеров &amp;lt;math&amp;gt;c_1, c_2, ..., c_k&amp;lt;/math&amp;gt;, после чего начинается основная часть алгоритма.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Каждый вектор &amp;lt;math&amp;gt;u_l&amp;lt;/math&amp;gt; определяем в ближайший к нему кластер &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, то есть такой, что &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n)&lt;br /&gt;
&amp;lt;/math&amp;gt;.&amp;lt;/li&amp;gt; Хранить информацию о принадлежности векторов кластерам будем в матрице &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; следующего вида:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
m_{ij} = \begin{cases}&lt;br /&gt;
1, &amp;amp; u_j \in C_i \\&lt;br /&gt;
0, &amp;amp; \text{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Считаем значение функции &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; и прекращаем работу алгоритма, если это значение меньше заданного порога &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt;, либо значение мало изменилось после текущей итерации.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Обновляем значения центров кластеров&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Возвращаемся к распределению векторов по кластерам.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%9D%D0%B5%D1%87%D1%91%D1%82%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85/&amp;diff=5883</id>
		<title>Участник:Ян-Мартин Тамм/Нечёткий алгоритм С средних/</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:%D0%AF%D0%BD-%D0%9C%D0%B0%D1%80%D1%82%D0%B8%D0%BD_%D0%A2%D0%B0%D0%BC%D0%BC/%D0%9D%D0%B5%D1%87%D1%91%D1%82%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85/&amp;diff=5883"/>
		<updated>2016-10-12T16:44:43Z</updated>

		<summary type="html">&lt;p&gt;Darel: Новая страница: «.»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.&lt;/div&gt;</summary>
		<author><name>Darel</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:Darel/%D0%9D%D0%B5%D1%87%D0%B5%D1%82%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=5882</id>
		<title>Участник:Darel/Нечеткий алгоритм С средних</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:Darel/%D0%9D%D0%B5%D1%87%D0%B5%D1%82%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A1_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D1%85&amp;diff=5882"/>
		<updated>2016-10-12T16:41:33Z</updated>

		<summary type="html">&lt;p&gt;Darel: Новая страница: «g»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;g&lt;/div&gt;</summary>
		<author><name>Darel</name></author>
	</entry>
</feed>