关于k-均值算法



http://blog.csdn.net/cai0538/article/details/7061922

这是一篇关于k均值算法的小文章,程序使用的是上一个人的,不过,为程序稍微多做了一些注解,方便理解。

同时也参考了

http://www.cnblogs.com/CBDoctor/archive/2011/10/24/2222358.html

K-means算法是最简单的一种聚类算法。算法的目的是使各个样本与所在类均值的误差平方和达到最小(这也是评价K-means算法最后聚类效果的评价标准)

K-means聚类算法的一般步骤:

  1. 初始化。输入基因表达矩阵作为对象集X,输入指定聚类类数N,并在X中随机选取N个对象作为初始聚类中心。设定迭代中止条件,比如最大循环次数或者聚类中心收敛误差容限。
  2. 进行迭代。根据相似度准则将数据对象分配到最接近的聚类中心,从而形成一类。初始化隶属度矩阵。
  3. 更新聚类中心。然后以每一类的平均向量作为新的聚类中心,重新分配数据对象。
  4. 反复执行第二步和第三步直至满足中止条件。 #include <iostream>   #include <fstream>   #include <vector>   #include <math.h>   #define k 3   using namespace std;   //存放元组的属性信息   struct Tuple{   float attr1;   float attr2;   };   //计算两个元组间的欧几里距离   float getDistXY(Tuple t1Tuple t2)    {   return sqrt((t1.attr1 - t2.attr1) * (t1.attr1 - t2.attr1) + (t1.attr2 - t2.attr2) * (t1.attr2 - t2.attr2));   }   //根据质心,决定当前元组属于哪个簇, 其中means[]数组保存到额是每个簇的质心 //这个函数大概是让每一个元素与所有的质心通过距离比较,计算它应该属于哪个簇。返回簇的索引 int clusterOfTuple(Tuple means[],Tuple tuple){   float dist=getDistXY(means[0],tuple);   float tmp;   int label=0;//标示属于哪一个簇 //只有k个簇,所以对于每一个元素只需要判断它距离哪一个簇的质心最近就好了。 for(int i=1;i<k;i++){   tmp=getDistXY(means[i],tuple);   if(tmp<dist) {dist=tmp;label=i;}   }   return label;      } //获得给定簇集的平方误差   float getVar(vector<Tupleclusters[], Tuple means[]){   float var = 0;   for (int i = 0; i < ki++)   {  //计算第k个簇中的平方误差之和 vector<Tuplet = clusters[i]; //获得第i簇的所有元素, means[i]表示该簇的质心  for (int j = 0; jt.size(); j++)   {   var += getDistXY(t[j], means[i]);   }   }   //cout<<"sum:"<<sum<<endl;   return var;   } //获得当前簇的均值(质心)   Tuple getMeans(vector<Tuplecluster){   int num = cluster.size();   double meansX = 0, meansY = 0;   Tuple t;   for (int i = 0; i < numi++)   {   meansX += cluster[i].attr1;   meansY += cluster[i].attr2;   }   t.attr1 = meansX / num;   t.attr2 = meansY / num;   return t;   //cout<<"sum:"<<sum<<endl;   }   void KMeans(vector<Tupletuples){  //tuples 包含所有的数据集,而我们要把它分成k簇,没一簇包含几个数据,所以在这里定义了的是大小为k的vector,大小会自增长, //这k个簇分别对应的质心为means[k]中的元素。    vector<Tupleclusters[k];     //k个元组,表示k个簇的质心      Tuple means[k];    int i=0;      //默认一开始将前K个元组的值作为k个簇的质心(均值)      for(i=0;i<k;i++){          means[i].attr1=tuples[i].attr1;          means[i].attr2=tuples[i].attr2;      }      int lable=0;      //根据默认的质心给簇赋值。遍历,分别求出每个元素所属于的簇,并压入相应的簇的数组    for(i=0;i!=tuples.size();++i){          lable=clusterOfTuple(means,tuples[i]);          clusters[lable].push_back(tuples[i]);      }      //输出刚开始的簇      for(lable=0;lable<k;lable++){          cout<<"第"<<lable+1<<"个簇:"<<endl;          vector<Tuplet = clusters[lable];          for (i = 0; it.size(); i++)          {              cout<<"("<<t[i].attr1<<","<<t[i].attr2<<")"<<"   ";          }             cout<<endl;      }      float oldVar=-1;      //该函数求出对于所有簇,假如说k=3,也就是3个簇平方误差之和。    float newVar=getVar(clusters,means);      //第一次newVal和oldVal都是在while循环外获得的,但是第二次就是在循环内部获得的。    //从循环内跳出到执行while判决,则说明上次的簇的质心和簇都已经建立完毕    //如果判断为假,说明还要继续进入循环,修改簇的质心和簇。    while(abs(newVar - oldVar) >= 1) //当新旧函数值相差不到1即准则函数值不发生明显变化时,算法终止      {          //在更新每个簇的中心以后,需要清空每个簇。这是为了在新的中心确定以后,继续以此迭代往相应的簇中添加数据。        for (i = 0; i < ki++) //更新每个簇的中心点          {               means[i] = getMeans(clusters[i]);               //cout<<"means["<<i<<"]:"<<means[i].attr1<<"  "<<means[i].attr2<<endl;           }           oldVar = newVar;           newVar = getVar(clusters,means); //计算新的准则函数值           for (i = 0; i < ki++) //清空每个簇           {               clusters[i].clear();           }           //根据新的质心获得新的簇           for(i=0;i!=tuples.size();++i){               lable=clusterOfTuple(means,tuples[i]);               clusters[lable].push_back(tuples[i]);           }           //输出当前的簇           for(lable=0;lable<k;lable++){               cout<<"第"<<lable+1<<"个簇:"<<endl;               vector<Tuplet = clusters[lable];               for (i = 0; it.size(); i++)               {                   cout<<"("<<t[i].attr1<<","<<t[i].attr2<<")"<<"   ";               }                  cout<<endl;           }       }          }   int main(void){          char fname[256];       cout<<"请输入存放数据的文件名: ";       cin>>fname;       cout<<endl;       ifstream infile;       infile.open(fname,ios::in);       if(!infile){           cout<<"不能打开输入的文件"<<fname<<endl;           return 0;       }       int count=0;       vector<Tupletuples;       Tuple tuple;       //从文件流中读入数据       while(!infile.eof()){           count++;           if(count%2==1) infile>>tuple.attr1;           else {               infile>>tuple.attr2;               tuples.push_back(tuple);           }       }       //int k;       //cout<<"请输入期望的簇的个数:"       //cin>>k;       //cout<<endl;          //输出文件中的元组信息       for(vector<Tuple>::size_type ix=0;ix!=tuples.size();++ix)           cout<<"("<<tuples[ix].attr1<<","<<tuples[ix].attr2<<")"<<"    ";       cout<<endl;       KMeans(tuples);   cout<<"请观看结果"; int b; cin>>b; if (0 == b) { return 0; }     return 0;   }  
同时也参考了
http://www.cnblogs.com/leoo2sk/archive/2010/09/20/k-means.html
其中上文的例子生动有趣,顺便也调侃了一下中国足球,当然这又是后话了。
所谓聚类问题,就是给定一个元素集合D,其中每个元素具有n个可观察属性,使用某种算法将D划分成k个子集,要求每个子集内部的元素之间相异度尽可能低,而不同子集的元素相异度尽可能高。其中每个子集叫做一个簇。
  k均值算法的计算过程非常直观:

      1、从D中随机取k个元素,作为k个簇的各自的中心。

      2、分别计算剩下的元素到k个簇中心的相异度,将这些元素分别划归到相异度最低的簇。

      3、根据聚类结果,重新计算k个簇各自的中心,计算方法是取簇中所有元素各自维度的算术平均数。

      4、将D中全部元素按照新的中心重新聚类。

      5、重复第4步,直到聚类结果不再变化。

      6、将结果输出。

希望能够帮助读者快速的理解k-均值算法

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值