K-meansアルゴリズムのゴルフ


10

K-meansは標準の教師なしクラスタリングアルゴリズムであり、一連の「ポイント」といくつかのクラスターKが与えられると、各「ポイント」をK個のクラスターのいずれかに割り当てます。

K平均の疑似コード

K-meansには多くのバリアントがあることに注意してください。以下で説明するアルゴリズムを実装する必要があります。同じ初期点が与えられた場合にこのアルゴリズムと同じ結果が得られる限り、アルゴリズムにいくつかのバリエーションを持たせたり、組み込みを使用したりできます。

この課題では、すべての入力が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は厳密に正の整数です。
  • 入力が有効であると想定することができます。
  • Pには常に少なくともKポイントがあります。
  • クラスタを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]]]

得点

これはなので、バイト単位の最も短い答えが優先されます。


結果がアルゴリズムと区別がつかない場合、組み込みは許可されますか?
マーティンエンダー2016年

@MartinBüttnerもしあなたが同じ初期点を与えられてそれを正当化できるなら、それは同じ結果に収束します、はい。
2016年

各ポイントのクラスターのメンバーシップのラベルを出力することもできますか?(たとえば、最初のクラスターの1すべてのポイントにラベルが付けられ、2番目のクラスターのすべてのポイントにラベルが付けられる2など)
flawr

@flawrはい、これで問題ありません。
2016年

縮退したテストケース:K=2, P = [[1,1], [1,1], [1,1]]
Peter Taylor

回答:


4

Matlab、25バイト

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

与えられたn x 2行列(点、例えばごとに1行[[4,8]; [15,16]; [23,42]; [-13.37,-12.1]; [666,-666]])この関数は、各入力点のラベルのリストを返します。


5

C ++、479 474バイト

Matlabの〜20xだけ!

ゴルフ

#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)のセットです。そして、出力は同じセットであり、のセットは、ポイントが終了する出力クラスターのインデックスを示します。xyi

その追加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、2番目の引数をlに変更して、p合計3回使用できるようにすることはできますか?
ザカリー

4

J、60 54バイト

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

pポイントと重心のリストを取得し、最も近い重心のインデックスで各ポイントを分類するヘルパー動詞を定義します。次に、それを使用して、各クラスターのポイントの平均を取り、収束するまで新しいセントロイドを選択するプロセスを繰り返し、ポイントを出力用に分割します。

使用法

kの値は、LHSで整数として指定されます。ポイントのリストは、RHSの2D配列として指定されます。ここでは、5 x 2の2次元配列に再形成されるポイントのリストとして指定されます。出力は、各ポイントが入力と同じ順序で属するクラスターのラベルになります。

再現可能な結果を​​得るために固定シードを使用する場合?は、を?.atに置き換え(?#)ます。

   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しますが、あなたの3Kを壊すと私が呪われるのではないかと怖いです。
NoOneIsHere

3

CJam(60バイト)

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

これは入力をk pスタックの形式で受け取る関数です。ポイントは、intではなくdoubleで表されると想定しています。ポイントの次元について暗黙的に何も想定していないため、指定された2次元の場合と同様に、6次元のユークリッド空間でも同じようにクラスター化します。

オンラインデモ


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 = FindClustersf[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)

オンラインでお試しください!

1列目と2列目をそれぞれ座標とPする行列として受け取ります。クラスタインデックス(整数)を示す最初の列が追加されて返されます。xyP

同点の場合にクラスターがランダムに選択されるという要件に準拠するにはw、ソースをコピーして再定義するnnet::which.is.max必要がありました。それ以外の場合は、合計210バイトのwhich.minfrom baseを使用します。ゴルフの余地はまだありますが、他の人にコード内の問題を見つける機会を与えるために、ゴルフを難読化したくありませんでした。


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パッケージを使用してそれを簡単に行うことができます)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.