Гольф алгоритм K-означає


10

K-засоби - це стандартний алгоритм кластеризації без нагляду, який, задавши набір "точок" та декілька кластерів K, призначить кожну "точку" одному з кластерів K.

Псевдокодекс К-засобів

Зауважте, що існує багато варіантів K-засобів. Ви повинні реалізувати алгоритм, який я описую нижче. Можливо, у алгоритмі є певна зміна або використовувати вбудовані модулі до тих пір, поки ви отримаєте такий самий результат, як і цей алгоритм, даючи ті самі початкові точки.

У цьому виклику всі входи будуть точками на 2D площині (кожна точка представлена ​​своїми координатами у x та y).

Inputs: K, the number of clusters
        P, the set of points

Choose K points of P uniformly at random
Each chosen point is the initial centroid of its cluster

Loop:
     For each point in P:
         Assign to the cluster whose centroid is the nearest (Euclidean distance)
         In case of a tie, any of the tied cluster can be chosen

     Recompute the centroid of each cluster:
         Its x coordinate is the average of all x's of the points in the cluster
         Its y coordinate is the average of all y's of the points in the cluster

Until the clusters don't change from one iteration to the next

Output: the set of clusters    

Входи та виходи

  • Ви можете приймати K і P через STDIN, або як аргумент функції тощо.
  • P і точки P можуть бути представлені за допомогою будь-якої структури, природної для набору / списків на обраній вами мові.
  • K - суворо додатне ціле число.
  • Ви можете припустити, що дані є дійсними.
  • Завжди буде не менше K балів у P.
  • Ви можете вивести кластери STDOUT, повернути їх з функції тощо.
  • Впорядкованість кластерів і впорядкування всередині кластерів є неважливими. -Ви можете повернути групи точок, які представляють кластери, або кожну точку, позначену ідентифікатором кластеру (наприклад, цілим числом).

Тестові справи

Оскільки отримані кластери залежать від того, які точки були обрані спочатку, ви не зможете отримувати однакові результати (або один і той же результат щоразу, коли запускаєте код).

Отже, візьміть лише результат як приклад виведення.

Input:
  K = 1
  P = [[1,2.5]]
Output:
  [[[1,2.5]]]

Input:
  K = 3
  P = [[4,8], [15,16], [23,42], [-13.37,-12.1], [666,-666]]
Output:
  [[[666,-666]],[[-13.37,-12.1],[4,8]],[[15,16],[23,42]]]

Input:
  K = 2
  P = [[1,1], [1,1], [1,1]]
Output:
  [[[1,1]],[[1,1],[1,1]]]

Оцінка балів

Це , тому найкоротша відповідь у байтах виграє.


Чи вбудовані дозволені, коли результати не відрізняються від вашого алгоритму?
Мартін Ендер

@ MartinBüttner, якщо ви можете обґрунтувати те, що дані однакові початкові бали, це сходиться до того ж результату, так.
Фаталізувати

Чи було б також прийнятним виводити мітки про членство в кластері для кожної точки? (Наприклад, усі точки першого кластера мають мітку 1, всі точки другого кластера мають мітку 2тощо)
flawr

@flawr Так, це прийнятно.
Фаталізувати

Degenerate тест: K=2, P = [[1,1], [1,1], [1,1]].
Пітер Тейлор

Відповіді:


4

Матлаб, 25 байт

@(x,k)kmeans(x,k,'S','u')

З огляду на n x 2матрицю (один рядок на точку, наприклад [[4,8]; [15,16]; [23,42]; [-13.37,-12.1]; [666,-666]]), ці функції повертають список міток для кожної точки введення.


5

C ++, 479 474 байт

Тільки ~ 20x стільки, скільки Matlab!

Гольф

#define V vector<P>
#define f float
struct P{f x,y,i=0;f d(P&p){return(p.x-x)*(p.x-x)+(p.y-y)*(p.y-y);}f n(P&p){return i?x/=i,y/=i,d(p):x=p.x,y=p.y,0;}f a(P&p){x+=p.x,y+=p.y,i++;}};P z;int l(P a,P b){return a.d(z)<b.d(z);}f m(f k,V&p){f s=p.size(),i,j=0,t=1;V c(k),n=c,d;for(random_shuffle(p.begin(),p.end());j<k;c[j].i=j++)c[j]=p[j];for(;t;c=n,n=V(k)){for(i=0;i<s;i++)d=c,z=p[i],sort(d.begin(),d.end(),l),j=d[0].i,p[i].i=j,n[j].a(p[i]);for(j=t=0;j<k;j++)t+=n[j].n(c[j]);}}

Вхід / вихід в алгоритм - це набір точок ( struct P) з xі y; і вихід є тим самим набором, з їх iнабором для позначення індексу кластера виводу, на якому точка закінчується.

Цей додатковий iтакож використовується для ідентифікації кластерів. У головному циклі найближчий центроїд до кожної точки виявляється шляхом сортування копії поточних центроїдів за близькістю до цієї точки.

Це обробляє вироджені випадки (порожні кластери), зберігаючи попереднє положення відповідних центроїдів (див. Визначення P::n, яке також повертає відстань до попереднього центру). Кілька символів можна було б врятувати, якщо припустити, що вони не з'являться.

Безумовно, з головним

#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;

#define V vector<P>
#define f float
struct P{
    f x,y,i=0;
    f d(P&p){return(p.x-x)*(p.x-x)+(p.y-y)*(p.y-y);} // distance squared
    f n(P&p){return i?x/=i,y/=i,d(p):x=p.x,y=p.y,0;} // normalize-or-reset
    f a(P&p){x+=p.x,y+=p.y,i++;}                     // add coordinates
};
P z;int l(P a,P b){return a.d(z)<b.d(z);}            // closer-to-z comparator 
f m(f k,V&p){
    f s=p.size(),i,j=0,t=1;V c(k),n=c,d;
    for(random_shuffle(p.begin(),p.end());j<k;c[j].i=j++)
        c[j]=p[j];                                // initial random assignment
    for(;t;c=n,n=V(k)){                           
        for(i=0;i<s;i++)                          // assign to clusters
            d=c,z=p[i],sort(d.begin(),d.end(),l),
            j=d[0].i,p[i].i=j,n[j].a(p[i]);       // and add those coords
        for(j=t=0;j<k;j++)t+=n[j].n(c[j]);        // normalize & count changes
    }        
}

int main(int argc, char **argv) {
    srand((unsigned long)time(0));

    int k;
    V p;
    sscanf(argv[1], "%d", &k);
    printf("Input:\n");
    for (int i=2,j=0; i<argc; i+=2, j++) {
        P n;
        sscanf(argv[i], "%f", &(n.x));
        sscanf(argv[i+1], "%f", &(n.y));
        p.push_back(n);
        printf("%d : %f,%f\n", j, p[j].x, p[j].y);
    }

    m(k,p);
    printf("Clusters:\n");
    for (int q=0; q<k; q++) {
        printf("%d\n", q);
        for (unsigned int i=0; i<p.size(); i++) {
            if (p[i].i == q) printf("\t%f,%f (%d)\n", p[i].x, p[i].y, i);
        }
    }
    return 0;
}

Я знаю, що я можу запізнитися на цей коментар, але ви могли б визначити макрос #define R p){returnі змінити другий аргумент lна, pщоб ви могли використовувати його втричі?
Zacharý

4

J, 60 54 байти

p=:[:(i.<./)"1([:+/&.:*:-)"1/
]p](p(+/%#)/.[)^:_(?#){]

Визначає дієслово-помічник, pяке бере список точок та центроїдів та класифікує кожну точку за індексом найближчого центроїда. Потім він використовує це для повторення процесу вибору нового центроїда шляхом взяття середніх точок у кожному кластері до його зближення, а потім для розділення точок для виведення.

Використання

Значення k задається як ціле число на LHS. Список точок подається у вигляді 2d масиву на RHS. Тут він задається у вигляді списку точок, які переставлені у 2d масив розміром 5 х 2. Вихід буде міткою, для якої кластерна кожна точка належить в тому ж порядку, що і вхідний.

Якщо ви хочете використовувати фіксоване насіння для відтворюваності результатів, замінити ?з ?.на (?#).

   p =: [:(i.<./)"1([:+/&.:*:-)"1/
   f =: ]p](p(+/%#)/.[)^:_(?#){]
   3 f (5 2 $ 4 8 15 16 23 42 _13.37 _12.1 666 _666)
0 1 1 0 2

Пояснення

[:(i.<./)"1([:+/&.:*:-)"1/  Input: points on LHS, centroids on RHS
           (          )"1/  Form a table between each point and centroid and for each
                     -        Find the difference elementwise
            [:     *:         Square each
              +/&.:           Reduce using addition
                              Apply the inverse of square (square root) to that sum
[:(     )"1                 For each row of that table
     <./                      Reduce using min
   i.                         Find the index of the minimum in that row
                            Returns a list of indices for each point that shows
                            which centroid it belongs to

]p](p(+/%#)/.[)^:_(?#){]  Input: k on LHS, points on RHS
                    #     Count the number of points
                   ?      Choose k values in the range [0, len(points))
                          without repetition
                       ]  Identity function, get points
                      {   Select the points at the indices above
  ]                       Identity function, get points
   (         )^:_         Repeat until convergence
    p                       Get the labels for each point
             [              Identity function, get points
           /.               Partition the points using the labels and for each
      +/                      Take the sums of points elementwise
         #                    Get the number of points
        %                     Divide sum elementwise by the count
                            Return the new values as the next centroids
]                         Identity function, get points
 p                        Get the labels for each point and return it

Я дав би +1, але я боюся, що зламати твій 3к мене проклинає.
NoOneIsHere

3

CJam (60 байт)

{:Pmr<1/2P,#{:z{_:+\,/}f%:C,{P{C\f{.-Yf#:+}_:e<#1$=},\;}%}*}

Це функція, яка приймає свій вхід у формі k pна стеку. Передбачається, що точки представлені парними, а не вставними. Він не передбачає неявно нічого про розмірність точок, тому він би кластеризувався однаково добре в 6-D евклідовому просторі, як у зазначеному 2-D.

Демонстрація в Інтернеті


2

Mathematica 14 12 байт

Оскільки вбудовані дозволені, це слід робити.

FindClusters

Приклад

FindClusters[{{4, 8}, {15, 16}, {23, 42}, {-13.37, -12.1}, {666, -666}}, 3]

{{{4, 8}, {-13.37, -12.1}}, {{15, 16}, {23, 42}}, {{666, -666}}}


Дужки вам не потрібні. f = FindClusters, f[something].
NoOneIsHere

добре, дякую, я не був впевнений.
DavidC

1

Желе , 24 байти

_ÆḊ¥þ³i"Ṃ€$
ẊḣµÇÆmƙ³µÐLÇ

Спробуйте в Інтернеті!

Використовує функції, які були реалізовані після публікації цього виклику. Нібито, це вже не є змагальним .

Пояснення

_ÆḊ¥þ³i"Ṃ€$  Helper link. Input: array of points
             (Classify) Given a size-k array of points, classifies
             each point in A to the closet point in the size-k array
    þ        Outer product with
     ³       All points, P
   ¥         Dyadic chain
_              Subtract
 ÆḊ            Norm
          $  Monadic chain
      i"     Find first index, vectorized
        Ṃ€   Minimum each

ẊḣµÇÆmƙ³µÐLÇ  Main link. Input: array of points P, integer k
  µ           Start new monadic chain
Ẋ               Shuffle P
 ḣ              Take the first k
        µ     Start new monadic chain
   Ç            Call helper (Classify)
      ƙ         Group with those values the items of
       ³        All points, P
    Æm            Take the mean of each group
         ÐL   Repeat that until the results converge
           Ç  Call helper (Classify)

1

R , 273 байт

function(K,P,C=P[sample(nrow(P),K),]){while(T){D=C
U=sapply(1:nrow(P),function(i)w(dist(rbind(P[i,],C))[1:K]))
C=t(sapply(1:K,function(i)colMeans(P[U==i,,drop=F])))
T=isTRUE(all.equal(C,D))}
cbind(U,P)}
w=function(x,y=seq_along(x)[x==min(x)])"if"(length(y)>1,sample(y,1),y)

Спробуйте в Інтернеті!

Приймається Pяк матриця, з xі yкоординатами у першому та другому стовпцях відповідно. Повертається Pіз додаванням першого стовпця, який вказує індекс кластера (ціле число).

Мені довелося переосмислити w, скопіювавши джерело, nnet::which.is.maxщоб відповідати вимозі, що кластер вибирається випадковим чином у разі зв’язків. Інакше я б використав which.minіз baseзагальної кількості 210 байт. Є ще місце для гри в гольф, але я не хотів його багато придушувати, щоб дати іншим шанс помітити можливі проблеми в моєму коді.


0

Юлія 213 байт

function f(p,k)
A=0
P=size(p,1)
c=p[randperm(P)[1:k],:]
while(true)
d=[norm(c[i]-p[j]) for i in 1:k, j in 1:P]
a=mapslices(indmin,d,1)
a==A&&return a
A=a
c=[mean(p[vec(a.==i),:],1) for i in 1:k]
end
end

Повертає масив тієї ж довжини p, що і цілими числами, які вказують, до якого кластеру належить відповідний елемент p.

Я думаю, що є ще досить великий обсяг для оптимізації відліку символів.

(Звичайно, я можу просто скористатися пакетом Clustering.jl, щоб це зробити тривіально)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.