[C语言] 聚类算法- K-means算法

[复制链接]
 楼主| 一路向北lm 发表于 2020-8-11 18:25 | 显示全部楼层 |阅读模式
一种典型的无监督学习算法,主要用于将相似的样本自动归到一个类别中,对于不同的相似度计算方法,会得到不同的聚类结果,常用的相似度计算方法有欧式距离法。

 楼主| 一路向北lm 发表于 2020-8-11 18:25 | 显示全部楼层
聚类算法和分类算法的最大区别:
聚类算法是无监督的学习算法,而分类算法属于监督的学习算法。
 楼主| 一路向北lm 发表于 2020-8-11 18:25 | 显示全部楼层
K-means算法:
01.        选择聚类的个数K,例如K = 3。
02.        生成K个聚类中心点。
03.        计算所有样本点到聚类中心的距离,根据远近聚类。
04.        更新质心,迭代聚类。
05.        重复第四步直到满足收敛要求。(通常是确定的中心点不再改变)
 楼主| 一路向北lm 发表于 2020-8-11 18:25 | 显示全部楼层
C++源码实现K-means 算法。
  1. #include <iostream>
  2. #include <sstream>
  3. #include <fstream>
  4. #include <string>
  5. #include <vector>
  6. #include <ctime>
  7. #include <cstdlib>
  8. #include <limits>
  9. using namespace std;


 楼主| 一路向北lm 发表于 2020-8-11 18:26 | 显示全部楼层
  1. // 数据类型转换 string 转float
  2. float StringToFloat(string str)
  3. {
  4.     stringstream  sstream;
  5.         float score = 0;
  6.         sstream<<str;
  7.         sstream>>score;
  8.         return score;
  9. }


 楼主| 一路向北lm 发表于 2020-8-11 18:26 | 显示全部楼层
  1. //读取文件
  2. vector<point_t> OpenFile(const char *dataset)
  3. {
  4.         fstream file;
  5.         vector<point_t> data;
  6.         file.open(dataset,ios::in);
  7.         while(!file.eof())
  8.         {
  9.           string temp;
  10.           file>>temp;
  11.           int split =temp.find(',');
  12.           point_t p;
  13.           p.x = StringToFloat(temp.substr(0,split));
  14.           p.y = StringToFloat(temp.substr(split+1,temp.length()-1));
  15.           p.cluster = 0;
  16.           data.push_back(p);
  17.         }
  18.         file.close();
  19.    return data;
  20. }


 楼主| 一路向北lm 发表于 2020-8-11 18:26 | 显示全部楼层
  1. //计算两点之间的距离
  2. float SquareDistance(point_t a,point_t b)
  3. {
  4.         return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
  5. }


 楼主| 一路向北lm 发表于 2020-8-11 18:27 | 显示全部楼层
  1. //随机选择K个质点
  2. vector<point_t> RandomSeceleK(vector<point_t>dataset,int k)
  3. {
  4.         unsigned int n = 1;
  5.         unsigned int len = dataset.size();
  6.         vector<point_t> centroid;
  7.         srand((int)time(0));        //使用时间设定随机种子
  8.         //随机选择质心,生成K个聚类中心点
  9.         while(n<=k)
  10.         {
  11.                 int cen = (float)rand()/(RAND_MAX+1)*len;
  12.                 point_t cp;
  13.                 cp.x = dataset[cen].x;
  14.                 cp.y = dataset[cen].y;
  15.                 cp.cluster = n;
  16.                 centroid.push_back(cp);
  17.                 n++;
  18.         }
  19.         return centroid;
  20. }


 楼主| 一路向北lm 发表于 2020-8-11 18:27 | 显示全部楼层
  1. //计算所有样本点到聚类中心的距离,根据远近聚类
  2. vector<point_t> DistanceCluster(vector<point_t>dataset,vector<point_t>centroid,int k)
  3. {
  4.         int len = dataset.size();
  5.         int n = 1;
  6.         for(int i=0;i<len;i++)
  7.         {
  8.                 n=1;
  9.                 float shortest = INT_MAX;
  10.                 int cur = dataset[i].cluster;
  11.                 while(n<=k)
  12.                 {
  13.                         float temp = SquareDistance(dataset[i],centroid[n-1]);                       
  14.                         if(temp<shortest)
  15.                         {
  16.                                 shortest = temp;
  17.                                 cur = n;
  18.                         }
  19.                         n++;
  20.                 }
  21.                 dataset[i].cluster = cur;
  22.         }
  23.         return dataset;
  24. }


 楼主| 一路向北lm 发表于 2020-8-11 18:27 | 显示全部楼层
  1. //更新质心
  2. vector<point_t> UpdateCentroid(vector<point_t>dataset,vector<point_t>centroid,int k)
  3. {
  4.     int len = dataset.size();
  5.         int *cs = new int[k];
  6.         for(int i=0;i<k;i++) cs[i] = 0;
  7.         for(int i=0;i<k;i++)
  8.         {
  9.                 centroid[i].x = 0;
  10.                 centroid[i].y = 0;
  11.                 centroid[i].cluster = i+1;
  12.         }
  13.         for(int i=0;i<len;i++)
  14.         {
  15.                 centroid[dataset[i].cluster-1].x += dataset[i].x;
  16.                 centroid[dataset[i].cluster-1].y += dataset[i].y;
  17.                 cs[dataset[i].cluster-1]++;
  18.         }
  19.         for(int i=0;i<k;i++)
  20.         {
  21.                 centroid[i].x /= cs[i];
  22.                 centroid[i].y /= cs[i];
  23.         }
  24.         return centroid;
  25. }


 楼主| 一路向北lm 发表于 2020-8-11 18:28 | 显示全部楼层
  1. //k-means 算法
  2. void k_means(vector<point_t>dataset,int k,int looptimes)
  3. {
  4.         vector<point_t> centroid;   //存放中心点
  5.         int n =1;
  6.         int len = dataset.size();
  7.         //1.随机选择K个质点
  8.         centroid = RandomSeceleK(dataset,k);
  9.         //打印中心点(质点)
  10.         for(int i=0;i<k;i++)
  11.         {
  12.           cout<<"x:"<<centroid[i].x<<"\ty:"<<centroid[i].y<<"\tc:"<<centroid[i].cluster<<endl;
  13.         }
  14.         //循环迭代
  15.         for(int i = 0;i<looptimes;i++)
  16.         {
  17.            //2.计算所有样本点到聚类中心的距离,根据远近聚类
  18.            dataset = DistanceCluster(dataset,centroid,k);          
  19.            //3.更新质心,迭代聚类
  20.            centroid = UpdateCentroid(dataset,centroid,k);   
  21.         }
  22.         //打印所有点聚类情况
  23.          for(int i=0;i<dataset.size();i++)
  24.            {
  25.              cout<<dataset[i].cluster<<endl;
  26.            }
  27.         //打印最后更新质心
  28.         for(int i=0;i<k;i++)
  29.         {
  30.                 cout<<"x:"<<centroid[i].x<<"\ty:"<<centroid[i].y<<"\tc:"<<centroid[i].cluster<<endl;
  31.         }
  32.     //写入到文件保存
  33.         fstream clustering;
  34.         clustering.open("clustering.txt",ios::out);
  35.         for(int i=0;i<len;i++)
  36.         {
  37.                 clustering<<dataset[i].x<<","<<dataset[i].y<<","<<dataset[i].cluster<<"\n";
  38.         }
  39.         clustering.close();
  40. }


 楼主| 一路向北lm 发表于 2020-8-11 18:28 | 显示全部楼层
主函数测试代码,分为两类,迭代100
  1. #include "k_means.h"

  2. int main()
  3. {
  4.     vector<point_t> data;
  5.         cout<<"k-means"<<endl;
  6.         data = OpenFile("01.txt");
  7.         k_means(data,2,100);
  8.         while(1);
  9. }


 楼主| 一路向北lm 发表于 2020-8-11 18:28 | 显示全部楼层
测试结果如下:质心不再改变。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 一路向北lm 发表于 2020-8-11 18:29 | 显示全部楼层
测试文本内容和输出文本内容:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 一路向北lm 发表于 2020-8-11 18:29 | 显示全部楼层
总结:
K-means算法运行速度快,实现简便。但K-means算法对具有变化大小,变化密度,非圆形状等特点的数据具有局限性。解决方法是增加K的大小,增加cluster数量,使得数据的特征能够更加明显。对于数据初始中心点的选择,采用随机的方式可能无法产生理想的聚类,这时可以采用二分K-means方法,或层次聚类进行处理。

wsnsyy 发表于 2020-8-12 19:51 | 显示全部楼层
这是做什么用
 楼主| 一路向北lm 发表于 2020-8-13 09:33 | 显示全部楼层

智能分类
您需要登录后才可以回帖 登录 | 注册

本版积分规则

293

主题

3837

帖子

81

粉丝
快速回复 在线客服 返回列表 返回顶部