Golf algoritma K-means

10

K-means adalah algoritma pengelompokan tanpa pengawasan standar, yang, diberikan satu set "titik" dan sejumlah kluster K, akan menetapkan setiap "titik" ke salah satu kluster K.

Pseudo-Code of K-means

Perhatikan bahwa ada banyak varian K-means. Anda harus mengimplementasikan algoritma yang saya jelaskan di bawah ini. Anda mungkin memiliki beberapa variasi pada algoritma atau menggunakan built-in selama Anda akan mendapatkan hasil yang sama seperti algoritma ini diberi poin awal yang sama.

Dalam tantangan ini, semua input akan menjadi titik pada bidang 2D (setiap titik diwakili oleh koordinatnya dalam x dan 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    

Masukan dan keluaran

  • Anda dapat mengambil K dan P melalui STDIN, atau sebagai argumen fungsi, dll.
  • P dan titik-titik dalam P dapat direpresentasikan menggunakan struktur apa pun yang alami untuk set / daftar dalam bahasa pilihan Anda.
  • K adalah bilangan bulat yang benar-benar positif.
  • Anda dapat berasumsi bahwa input tersebut valid.
  • Akan selalu ada setidaknya poin K di P.
  • Anda dapat menampilkan cluster untuk STDOUT, mengembalikannya dari suatu fungsi, dll.
  • Pemesanan cluster dan pemesanan di dalam cluster tidak penting. -Anda dapat mengembalikan kelompok titik untuk mewakili kelompok, atau setiap titik yang dilabeli dengan pengidentifikasi untuk gugus (misalnya bilangan bulat).

Uji kasus

Karena cluster yang dihasilkan tergantung pada titik mana yang awalnya dipilih, Anda mungkin tidak semua mendapatkan hasil yang sama (atau hasil yang sama setiap kali Anda menjalankan kode Anda).

Karena itu, ambil saja output sebagai contoh output.

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]]]

Mencetak gol

Ini adalah , jadi jawaban tersingkat dalam byte menang.

Fatalisasi
sumber
Apakah bawaan diizinkan ketika hasilnya tidak dapat dibedakan dari algoritma Anda?
Martin Ender
@ MartinBüttner jika Anda dapat membenarkan bahwa dengan memberikan poin awal yang sama, itu konvergen ke hasil yang sama, ya.
Fatalkan
Apakah itu juga dapat diterima untuk label keluaran keanggotaan cluster untuk setiap titik? (Misalnya semua titik dari cluster pertama memiliki label 1, semua poin dari yang kedua memiliki label 2dll)
flawr
@ flawr Ya, ini bisa diterima.
Fatalkan
Kasus uji merosot: K=2, P = [[1,1], [1,1], [1,1]].
Peter Taylor

Jawaban:

4

Matlab, 25 byte

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

Diberi n x 2matriks (misalnya satu baris per titik [[4,8]; [15,16]; [23,42]; [-13.37,-12.1]; [666,-666]]), fungsi ini mengembalikan daftar label untuk setiap titik input.

cacat
sumber
5

C ++, 479 474 byte

Hanya ~ 20x sebanyak Matlab!

Golf

#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]);}}

Input / output ke algoritma adalah seperangkat poin ( struct P) dengan xdan y; dan output adalah himpunan yang sama, dengan ihimpunan mereka untuk menunjukkan indeks dari kluster keluaran dimana titik berakhir.

Ekstra iitu juga digunakan untuk mengidentifikasi kelompok. Di loop utama, centroid terdekat untuk setiap titik ditemukan dengan mengurutkan salinan centroid saat ini dengan kedekatan dengan titik itu.

Ini menangani kasus degenerasi (kluster kosong) dengan menjaga posisi sentroid sebelumnya yang sesuai (lihat definisi P::n, yang juga mengembalikan jarak ke centroid sebelumnya). Beberapa karakter dapat dihemat dengan mengasumsikan bahwa ini tidak akan muncul.

Tidak disatukan, dengan utama

#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;
}
tucuxi
sumber
Saya tahu saya mungkin terlambat dalam komentar ini, tetapi bisakah Anda mendefinisikan makro #define R p){returndan mengubah argumen kedua lmenjadi psehingga Anda dapat menggunakannya tiga kali total?
Zacharý
4

J, 60 54 byte

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

Menentukan kata kerja pembantu pyang mengambil daftar titik dan centroid dan mengklasifikasikan setiap titik dengan indeks centroid terdekat. Kemudian, ia menggunakan itu untuk mengulangi proses memilih centroid baru dengan mengambil rata-rata dari titik-titik di masing-masing cluster sampai konvergen, dan kemudian untuk mempartisi poin untuk output.

Pemakaian

Nilai k diberikan sebagai bilangan bulat pada LHS. Daftar poin diberikan sebagai array 2d pada RHS. Di sini ditentukan sebagai daftar poin yang dibentuk kembali menjadi array 2d dari 5 x 2. Output akan menjadi label yang mana setiap titik cluster berada dalam urutan yang sama dengan input.

Jika Anda ingin menggunakan benih tetap untuk hasil yang dapat direproduksi, ganti ?dengan a ?.di (?#).

   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

Penjelasan

[:(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
mil
sumber
Saya akan memberi +1, tapi saya takut melanggar 3k Anda akan mengutuk saya.
NoOneIsHere
3

CJam (60 byte)

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

Ini adalah fungsi yang mengambil inputnya dalam bentuk k pdi stack. Diasumsikan bahwa poin diwakili dengan ganda, bukan int. Itu tidak secara implisit mengasumsikan apa pun tentang dimensi titik, sehingga akan mengelompok dengan baik di ruang Euclidean 6-D seperti pada 2-D yang ditentukan.

Demo online

Peter Taylor
sumber
2

Mathematica 14 12 byte

Karena built-in diizinkan, ini harus dilakukan.

FindClusters

Contoh

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}}}

DavidC
sumber
Anda tidak membutuhkan tanda kurung. f = FindClusters, f[something].
NoOneIsHere
ok, terima kasih saya tidak yakin.
DavidC
1

Jelly , 24 byte

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

Cobalah online!

Menggunakan fitur yang diterapkan setelah tantangan ini diposting. Seharusnya, ini tidak lagi bersaing .

Penjelasan

_ÆḊ¥þ³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)
mil
sumber
1

R , 273 byte

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)

Cobalah online!

Dibawa Psebagai matriks, dengan xdan ykoordinat masing-masing di kolom pertama dan kedua. Kembali Pdengan kolom pertama yang ditambahkan yang menunjukkan indeks cluster (integer).

Saya harus mendefinisikan ulang wdengan menyalin sumber dari nnet::which.is.maxagar sesuai dengan persyaratan bahwa cluster dipilih secara acak dalam kasus ikatan. Kalau tidak, saya akan menggunakan which.mindari basetotal 210 byte. Masih ada ruang untuk bermain golf, tetapi saya tidak ingin mengaburkan banyak hal untuk memberi orang lain kesempatan untuk melihat kemungkinan masalah dalam kode saya.

JayCe
sumber
0

Julia 213 byte

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

Mengembalikan array dengan panjang yang sama dengan p, dengan bilangan bulat yang menunjukkan kelompok mana elemen terkaitp milik.

Saya pikir masih ada sedikit ruang untuk mengoptimalkan penghitungan karakter.

(Tentu saja saya bisa menggunakan paket Clustering.jl untuk melakukannya secara sepele)

Lyndon White
sumber