RosettaCodeData/Task/K-means++-clustering/C/k-means++-clustering.c
Ingy döt Net db842d013d A-M baby
2013-04-10 21:29:02 -07:00

183 lines
3.9 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct { double x, y; int group; } point_t, *point;
double randf(double m)
{
return m * rand() / (RAND_MAX - 1.);
}
point gen_xy(int count, double radius)
{
double ang, r;
point p, pt = malloc(sizeof(point_t) * count);
/* note: this is not a uniform 2-d distribution */
for (p = pt + count; p-- > pt;) {
ang = randf(2 * M_PI);
r = randf(radius);
p->x = r * cos(ang);
p->y = r * sin(ang);
}
return pt;
}
inline double dist2(point a, point b)
{
double x = a->x - b->x, y = a->y - b->y;
return x*x + y*y;
}
inline int
nearest(point pt, point cent, int n_cluster, double *d2)
{
int i, min_i;
point c;
double d, min_d;
# define for_n for (c = cent, i = 0; i < n_cluster; i++, c++)
for_n {
min_d = HUGE_VAL;
min_i = pt->group;
for_n {
if (min_d > (d = dist2(c, pt))) {
min_d = d; min_i = i;
}
}
}
if (d2) *d2 = min_d;
return min_i;
}
void kpp(point pts, int len, point cent, int n_cent)
{
# define for_len for (j = 0, p = pts; j < len; j++, p++)
int i, j;
int n_cluster;
double sum, *d = malloc(sizeof(double) * len);
point p, c;
cent[0] = pts[ rand() % len ];
for (n_cluster = 1; n_cluster < n_cent; n_cluster++) {
sum = 0;
for_len {
nearest(p, cent, n_cluster, d + j);
sum += d[j];
}
sum = randf(sum);
for_len {
if ((sum -= d[j]) > 0) continue;
cent[n_cluster] = pts[j];
break;
}
}
for_len p->group = nearest(p, cent, n_cluster, 0);
free(d);
}
point lloyd(point pts, int len, int n_cluster)
{
int i, j, min_i;
int changed;
point cent = malloc(sizeof(point_t) * n_cluster), p, c;
/* assign init grouping randomly */
//for_len p->group = j % n_cluster;
/* or call k++ init */
kpp(pts, len, cent, n_cluster);
do {
/* group element for centroids are used as counters */
for_n { c->group = 0; c->x = c->y = 0; }
for_len {
c = cent + p->group;
c->group++;
c->x += p->x; c->y += p->y;
}
for_n { c->x /= c->group; c->y /= c->group; }
changed = 0;
/* find closest centroid of each point */
for_len {
min_i = nearest(p, cent, n_cluster, 0);
if (min_i != p->group) {
changed++;
p->group = min_i;
}
}
} while (changed > (len >> 10)); /* stop when 99.9% of points are good */
for_n { c->group = i; }
return cent;
}
void print_eps(point pts, int len, point cent, int n_cluster)
{
# define W 400
# define H 400
int i, j;
point p, c;
double min_x, max_x, min_y, max_y, scale, cx, cy;
double *colors = malloc(sizeof(double) * n_cluster * 3);
for_n {
colors[3*i + 0] = (3 * (i + 1) % 11)/11.;
colors[3*i + 1] = (7 * i % 11)/11.;
colors[3*i + 2] = (9 * i % 11)/11.;
}
max_x = max_y = -(min_x = min_y = HUGE_VAL);
for_len {
if (max_x < p->x) max_x = p->x;
if (min_x > p->x) min_x = p->x;
if (max_y < p->y) max_y = p->y;
if (min_y > p->y) min_y = p->y;
}
scale = W / (max_x - min_x);
if (scale > H / (max_y - min_y)) scale = H / (max_y - min_y);
cx = (max_x + min_x) / 2;
cy = (max_y + min_y) / 2;
printf("%%!PS-Adobe-3.0\n%%%%BoundingBox: -5 -5 %d %d\n", W + 10, H + 10);
printf( "/l {rlineto} def /m {rmoveto} def\n"
"/c { .25 sub exch .25 sub exch .5 0 360 arc fill } def\n"
"/s { moveto -2 0 m 2 2 l 2 -2 l -2 -2 l closepath "
" gsave 1 setgray fill grestore gsave 3 setlinewidth"
" 1 setgray stroke grestore 0 setgray stroke }def\n"
);
for_n {
printf("%g %g %g setrgbcolor\n",
colors[3*i], colors[3*i + 1], colors[3*i + 2]);
for_len {
if (p->group != i) continue;
printf("%.3f %.3f c\n",
(p->x - cx) * scale + W / 2,
(p->y - cy) * scale + H / 2);
}
printf("\n0 setgray %g %g s\n",
(c->x - cx) * scale + W / 2,
(c->y - cy) * scale + H / 2);
}
printf("\n%%%%EOF");
free(colors);
# undef for_n
# undef for_len
}
#define PTS 100000
#define K 11
int main()
{
int i;
point v = gen_xy(PTS, 10);
point c = lloyd(v, PTS, K);
print_eps(v, PTS, c, K);
// free(v); free(c);
return 0;
}