kmeans算法c語言實現,能對不同維度的數據進行聚類


  最近在苦於思考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 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM