最近在苦于思考kmeans算法的MPI并行化,花了两天的时间把该算法看懂和实现了串行版。
聚类问题就是给定一个元素集合V,其中每个元素具有d个可观察属性,使用某种算法将V划分成k个子集,要求每个子集内部的元素之间相异度尽可能低,而不同子集的元素相异度尽可能高。
下面是google到该算法的一个流程图,表意清楚:
1、随机选取数据集中的k个数据点作为初始的聚类中心:
2、分别计算每个数据点到每个中心的距离,选取距离最短的中心点作为其聚类中心:
3、利用目前得到的聚类重新计算中心点:
4、重复步骤2和3直到收敛(达到最大迭代次数或聚类中心不再移动):
code:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <time.h> 5 6 int K,N,D; //聚类的数目,数据量,数据的维数 7 float **data; //存放数据 8 int *in_cluster; //标记每个点属于哪个聚类 9 float **cluster_center; //存放每个聚类的中心点 10 11 float **array(int m,int n); 12 void freearray(float **p); 13 float **loadData(int *k,int *d,int *n); 14 float getDistance(float avector[],float bvector[],int n); 15 void cluster(); 16 float getDifference(); 17 void getCenter(int in_cluster[]); 18 19 int main() 20 { 21 int i,j,count=0; 22 float temp1,temp2; 23 data=loadData(&K,&D,&N); 24 printf("Data sets:\n"); 25 for(i=0;i<N;i++) 26 for(j=0;j<D;j++){ 27 printf("%-8.2f",data[i][j]); 28 if((j+1)%D==0) putchar('\n'); 29 } 30 printf("-----------------------------\n"); 31 32 srand((unsigned int)(time(NULL))); //随机初始化k个中心点 33 for(i=0;i<K;i++) 34 for(j=0;j<D;j++) 35 cluster_center[i][j]=data[(int)((double)N*rand()/(RAND_MAX+1.0))][j]; 36 37 cluster(); //用随机k个中心点进行聚类 38 temp1=getDifference(); //第一次中心点和所属数据点的距离之和 39 count++; 40 printf("The difference between data and center is: %.2f\n\n", temp1); 41 42 getCenter(in_cluster); 43 cluster(); //用新的k个中心点进行第二次聚类 44 temp2=getDifference(); 45 count++; 46 printf("The difference between data and center is: %.2f\n\n",temp2); 47 48 while(fabs(temp2-temp1)!=0){ //比较前后两次迭代,若不相等继续迭代 49 temp1=temp2; 50 getCenter(in_cluster); 51 cluster(); 52 temp2=getDifference(); 53 count++; 54 printf("The %dth difference between data and center is: %.2f\n\n",count,temp2); 55 } 56 57 printf("\nThe total number of cluster is: %d\n",count); //统计迭代次数 58 //system("pause"); //gcc编译需删除 59 return 0; 60 } 61 62 63 //动态创建二维数组 64 float **array(int m,int n) 65 { 66 int i; 67 float **p; 68 p=(float **)malloc(m*sizeof(float *)); 69 p[0]=(float *)malloc(m*n*sizeof(float)); 70 for(i=1;i<m;i++) p[i]=p[i-1]+n; 71 return p; 72 } 73 74 //释放二维数组所占用的内存 75 void freearray(float **p) 76 { 77 free(*p); 78 free(p); 79 } 80 81 //从data.txt导入数据,要求首行格式:K=聚类数目,D=数据维度,N=数据量 82 float **loadData(int *k,int *d,int *n) 83 { 84 int i,j; 85 float **arraydata; 86 FILE *fp; 87 if((fp=fopen("data.txt","r"))==NULL) fprintf(stderr,"cannot open data.txt!\n"); 88 if(fscanf(fp,"K=%d,D=%d,N=%d\n",k,d,n)!=3) fprintf(stderr,"load error!\n"); 89 arraydata=array(*n,*d); //生成数据数组 90 cluster_center=array(*k,*d); //聚类的中心点 91 in_cluster=(int *)malloc(*n * sizeof(int)); //每个数据点所属聚类的标志数组 92 for(i=0;i<*n;i++) 93 for(j=0;j<*d;j++) 94 fscanf(fp,"%f",&arraydata[i][j]); //读取数据点 95 return arraydata; 96 } 97 98 //计算欧几里得距离 99 float getDistance(float avector[],float bvector[],int n) 100 { 101 int i; 102 float sum=0.0; 103 for(i=0;i<n;i++) 104 sum+=pow(avector[i]-bvector[i],2); 105 return sqrt(sum); 106 } 107 108 //把N个数据点聚类,标出每个点属于哪个聚类 109 void cluster() 110 { 111 int i,j; 112 float min; 113 float **distance=array(N,K); //存放每个数据点到每个中心点的距离 114 //float distance[N][K]; //也可使用C99变长数组 115 for(i=0;i<N;++i){ 116 min=9999.0; 117 for(j=0;j<K;++j){ 118 distance[i][j] = getDistance(data[i],cluster_center[j],D); 119 //printf("%f\n", distance[i][j]); 120 if(distance[i][j]<min){ 121 min=distance[i][j]; 122 in_cluster[i]=j; 123 } 124 } 125 printf("data[%d] in cluster-%d\n",i,in_cluster[i]+1); 126 } 127 printf("-----------------------------\n"); 128 free(distance); 129 } 130 131 //计算所有聚类的中心点与其数据点的距离之和 132 float getDifference() 133 { 134 int i,j; 135 float sum=0.0; 136 for(i=0;i<K;++i){ 137 for(j=0;j<N;++j){ 138 if(i==in_cluster[j]) 139 sum+=getDistance(data[j],cluster_center[i],D); 140 } 141 } 142 return sum; 143 } 144 145 //计算每个聚类的中心点 146 void getCenter(int in_cluster[]) 147 { 148 float **sum=array(K,D); //存放每个聚类中心点 149 //float sum[K][D]; //也可使用C99变长数组 150 int i,j,q,count; 151 for(i=0;i<K;i++) 152 for(j=0;j<D;j++) 153 sum[i][j]=0.0; 154 for(i=0;i<K;i++){ 155 count=0; //统计属于某个聚类内的所有数据点 156 for(j=0;j<N;j++){ 157 if(i==in_cluster[j]){ 158 for(q=0;q<D;q++) 159 sum[i][q]+=data[j][q]; //计算所属聚类的所有数据点的相应维数之和 160 count++; 161 } 162 } 163 for(q=0;q<D;q++) 164 cluster_center[i][q]=sum[i][q]/count; 165 } 166 printf("The new center of cluster is:\n"); 167 for(i = 0; i < K; i++) 168 for(q=0;q<D;q++){ 169 printf("%-8.2f",cluster_center[i][q]); 170 if((q+1)%D==0) putchar('\n'); 171 } 172 free(sum); 173 }
该程序支持不同维度的数据集,一个示例的数据集 data.txt如下:
K=3,D=3,N=15
-25 22.2 35.34
31.2 -14.4 23
32.02 -23 24.44
-25.35 36.3 -33.34
-20.2 27.333 -28.22
-15.66 17.33 -23.33
26.3 -31.34 16.3
-22.544 16.2 -32.22
12.2 -15.22 22.11
-41.241 25.232 -35.338
-22.22 45.22 23.55
-34.22 50.14 30.98
15.23 -30.11 20.987
-32.5 15.3 -25.22
-38.97 20.11 33.22