k-means(k均值聚类)算法介绍及实现(c++)

来源:互联网 发布:属性数据分析引论答案 编辑:程序博客网 时间:2024/05/10 13:19

基本介绍:

k-means 算法接受输入量 k ;然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。

工作过程:

  k-means 算法的工作过程说明如下:首先从n个数据对象任意选择 k 个对象作为初始聚类中心;而对于所剩下其它对象,则根据它们与这些聚类中心的相似度(距离),分别将它们分配给与其最相似的(聚类中心所代表的)聚类;然后再计算每个所获新聚类的聚类中心(该聚类中所有对象的均值);不断重复这一过程直到标准测度函数开始收敛为止。一般都采用均方差作为标准测度函数. k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。

(1) 从 n个数据对象任意选择 k 个对象作为初始聚类中心;

  (2) 循环(3)到(4)直到每个聚类不再发生变化为止
  (3) 根据每个聚类对象的均值(中心对象),计算每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行划分;
  (4) 重新计算每个(有变化)聚类的均值(中心对象)
  k-means 算法接受输入量 k ;然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。
  k-means 算法的工作过程说明如下:首先从n个数据对象任意选择 k 个对象作为初始聚类中心;而对于所剩下其它对象,则根据它们与这些聚类中心的相似度(距离),分别将它们分配给与其最相似的(聚类中心所代表的)聚类;然后再计算每个所获新聚类的聚类中心(该聚类中所有对象的均值);不断重复这一过程直到标准测度函数开始收敛为止。一般都采用均方差作为标准测度函数. k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。
  计算复杂度:
  O(nkt), 其中t是迭代次数。
  k-means算法是一种基于样本间相似性度量的间接聚类方法,属于非监督学习方法。此算法以k为参数,把n 个对象分为k个簇,以使簇内具有较高的相似度,而且簇间的相似度较低。相似度的计算根据一个簇中对象的平均值(被看作簇的重心)来进行。此算法首先随机选择k个对象,每个对象代表一个聚类的质心。对于其余的每一个对象,根据该对象与各聚类质心之间的距离,把它分配到与之最相似的聚类中。然后,计算每个聚类的新质心。重复上述过程,直到准则函数会聚。k-means算法是一种较典型的逐点修改迭代的动态聚类算法,其要点是以误差平方和为准则函数。逐点修改类中心:一个象元样本按某一原则,归属于某一组类后,就要重新计算这个组类的均值,并且以新的均值作为凝聚中心点进行下一次象元素聚类;逐批修改类中心:在全部象元样本按某一组的类中心分类之后,再计算修改各类的均值,作为下一次分类的凝聚中心点。


[cpp] view plaincopyprint?
  1. /*kmeans算法实现(此处只考虑元组只有两个属性的情况) 
  2. *@File:k_means.cpp 
  3. *@Author:Cai0538 
  4. *@Create:2011-12-10 
  5. *@Last Modified:2011-12-10 
  6. */  
  7. #include <iostream>  
  8. #include <fstream>  
  9. #include <vector>  
  10. #include <math.h>  
  11. #define k 3  
  12. using namespace std;  
  13. //存放元组的属性信息  
  14. struct Tuple{  
  15.     float attr1;  
  16.     float attr2;  
  17. };  
  18. //计算两个元组间的欧几里距离  
  19. float getDistXY(Tuple t1, Tuple t2)   
  20. {  
  21.     return sqrt((t1.attr1 - t2.attr1) * (t1.attr1 - t2.attr1) + (t1.attr2 - t2.attr2) * (t1.attr2 - t2.attr2));  
  22. }  
  23.   
  24. //根据质心,决定当前元组属于哪个簇  
  25. int clusterOfTuple(Tuple means[],Tuple tuple){  
  26.     float dist=getDistXY(means[0],tuple);  
  27.     float tmp;  
  28.     int label=0;//标示属于哪一个簇  
  29.     for(int i=1;i<k;i++){  
  30.         tmp=getDistXY(means[i],tuple);  
  31.         if(tmp<dist) {dist=tmp;label=i;}  
  32.     }  
  33.     return label;     
  34. }  
  35. //获得给定簇集的平方误差  
  36. float getVar(vector<Tuple> clusters[],Tuple means[]){  
  37.     float var = 0;  
  38.     for (int i = 0; i < k; i++)  
  39.     {  
  40.         vector<Tuple> t = clusters[i];  
  41.         for (int j = 0; j< t.size(); j++)  
  42.         {  
  43.             var += getDistXY(t[j],means[i]);  
  44.         }  
  45.     }  
  46.     //cout<<"sum:"<<sum<<endl;  
  47.     return var;  
  48.   
  49. }  
  50. //获得当前簇的均值(质心)  
  51. Tuple getMeans(vector<Tuple> cluster){  
  52.       
  53.     int num = cluster.size();  
  54.     double meansX = 0, meansY = 0;  
  55.     Tuple t;  
  56.     for (int i = 0; i < num; i++)  
  57.     {  
  58.         meansX += cluster[i].attr1;  
  59.         meansY += cluster[i].attr2;  
  60.     }  
  61.     t.attr1 = meansX / num;  
  62.     t.attr2 = meansY / num;  
  63.     return t;  
  64.     //cout<<"sum:"<<sum<<endl;  
  65.       
  66.   
  67. }  
  68. void KMeans(vector<Tuple> tuples){  
  69.     vector<Tuple> clusters[k];  
  70.     Tuple means[k];  
  71.     int i=0;  
  72.     //默认一开始将前K个元组的值作为k个簇的质心(均值)  
  73.     for(i=0;i<k;i++){  
  74.         means[i].attr1=tuples[i].attr1;  
  75.         means[i].attr2=tuples[i].attr2;  
  76.     }  
  77.     int lable=0;  
  78.     //根据默认的质心给簇赋值  
  79.     for(i=0;i!=tuples.size();++i){  
  80.         lable=clusterOfTuple(means,tuples[i]);  
  81.         clusters[lable].push_back(tuples[i]);  
  82.     }  
  83.     //输出刚开始的簇  
  84.     for(lable=0;lable<3;lable++){  
  85.         cout<<"第"<<lable+1<<"个簇:"<<endl;  
  86.         vector<Tuple> t = clusters[lable];  
  87.         for (i = 0; i< t.size(); i++)  
  88.         {  
  89.             cout<<"("<<t[i].attr1<<","<<t[i].attr2<<")"<<"   ";  
  90.         }     
  91.         cout<<endl;  
  92.     }  
  93.     float oldVar=-1;  
  94.     float newVar=getVar(clusters,means);  
  95.     while(abs(newVar - oldVar) >= 1) //当新旧函数值相差不到1即准则函数值不发生明显变化时,算法终止  
  96.     {  
  97.           
  98.         for (i = 0; i < k; i++) //更新每个簇的中心点  
  99.         {  
  100.             means[i] = getMeans(clusters[i]);  
  101.             //cout<<"means["<<i<<"]:"<<means[i].attr1<<"  "<<means[i].attr2<<endl;  
  102.         }  
  103.         oldVar = newVar;  
  104.         newVar = getVar(clusters,means); //计算新的准则函数值  
  105.         for (i = 0; i < k; i++) //清空每个簇  
  106.         {  
  107.             clusters[i].clear();  
  108.         }  
  109.         //根据新的质心获得新的簇  
  110.         for(i=0;i!=tuples.size();++i){  
  111.             lable=clusterOfTuple(means,tuples[i]);  
  112.             clusters[lable].push_back(tuples[i]);  
  113.         }  
  114.         //输出当前的簇  
  115.         for(lable=0;lable<3;lable++){  
  116.             cout<<"第"<<lable+1<<"个簇:"<<endl;  
  117.             vector<Tuple> t = clusters[lable];  
  118.             for (i = 0; i< t.size(); i++)  
  119.             {  
  120.                 cout<<"("<<t[i].attr1<<","<<t[i].attr2<<")"<<"   ";  
  121.             }     
  122.             cout<<endl;  
  123.         }  
  124.     }  
  125.       
  126. }  
  127. int main(){  
  128.   
  129.     char fname[256];  
  130.     cout<<"请输入存放数据的文件名: ";  
  131.     cin>>fname;  
  132.     cout<<endl;  
  133.     ifstream infile;  
  134.     infile.open(fname,ios::in);  
  135.     if(!infile){  
  136.         cout<<"不能打开输入的文件"<<fname<<endl;  
  137.         return 0;  
  138.     }  
  139.     int count=0;  
  140.     vector<Tuple> tuples;  
  141.     Tuple tuple;  
  142.     //从文件流中读入数据  
  143.     while(!infile.eof()){  
  144.         count++;  
  145.         if(count%2==1) infile>>tuple.attr1;  
  146.         else {  
  147.             infile>>tuple.attr2;  
  148.             tuples.push_back(tuple);  
  149.         }  
  150.     }  
  151.     //int k;  
  152.     //cout<<"请输入期望的簇的个数:"  
  153.     //cin>>k;  
  154.     //cout<<endl;  
  155.   
  156.     //输出文件中的元组信息  
  157.     for(vector<Tuple>::size_type ix=0;ix!=tuples.size();++ix)  
  158.         cout<<"("<<tuples[ix].attr1<<","<<tuples[ix].attr2<<")"<<"    ";  
  159.     cout<<endl;  
  160.     KMeans(tuples);  
  161.     return 0;  
  162. }  

大家要执行该程序,要满足如下条件:

输入:

执行程序时,会提示输出要读入数据所在的文件,比如在程序所在的当前目录下的文件data.txt。

data.txt中的数据格式可参考下面:

1  1
2  1
1  2
2  2
4  3
5  3
4  4
5  4

将如此格式的数据粘贴在data.txt中即可。

原创粉丝点击