C语言实现K-Means算法

2020-01-06 18:41:23王旭

每个数据点都含有X、Y坐标,算法初始状态时,指定聚类的具体个数K,初试状态的K个聚类中心由输入文件的前K个数据点来指定。算法在每一次迭代中,需要计算各个点到K个聚类中心坐标的欧氏距离,并选择距离最近的一个聚类,用该聚类的名称标识当前数据点。当所有数据点遍历完后,计算划分到每个聚类中所有数据点X与Y的均值,并将该均值与前一次聚类中心点的坐标相比较。当X与Y的误差小于或者等于1e-6时,则结束迭代并输出收敛后的K歌聚类的中心坐标。

(二)变量和函数说明

(1)定义结构体类型,用于存储数据点坐标、所在聚类、与聚类中心距离


typedef struct point

{

float x,y;    //数据点的坐标
string className; //所属的聚类
float distance;  //距离聚类中心的距离

}Point;

(2)变量声明

vector<Point> dataVector:存储从文件读取的数据

vector<Point> classPoints:存储聚类坐标

vector<Point> &totalPoints):存储所有的数据点

(3)函数声明

字符串转换函数:将整型变量转换成字符串类型:


string converToString(int x);

读入数据函数:从文件读入坐标数据:


vector<Point> readDataFile(string fileName);

初始化数据集合函数:


void initDataset(int classNum,vector<Point> dataVector,vector<Point> &classPoints,vector<Point> &totalPoints);

计算各个数据点距离聚点中心的欧氏距离的函数:


string computerDistance(Point *p_totalPoints,vector<Point> &classPoints);

将各个点划分到相应类的函数:


void kMeansClustering(int classNum,vector<Point> totalPoints,vector<Point> classPoints);

(三)核心代码(部分)

(1)初始化数据集合函数:


void initDataset(int classNum,vector<Point>dataVector,vector<Point>&classPoints, 
         vector<Point>&totalPoints) 
{ 
  int i,j; 
  Point point; 
  for(i=0,j=1; i<dataVector.size(); i++) 
  { 
    if(j<=classNum) //classNum表示聚类的编号 
    { 
      point.x=dataVector[i].x; 
      point.y=dataVector[i].y; 
      point.distance=dataVector[i].distance; 
      point.className=converToString(j);//将整型类型转换成字符串类型 
      classPoints.push_back(point); 
      j++; 
    } 
    point.x=dataVector[i].x; 
    point.y=dataVector[i].y; 
    point.distance=dataVector[i].distance; 
    totalPoints.push_back(point); 
  } 
} 

(2)K-means函数:


void kMeansClustering(int classNum,vector<Point> totalPoints,vector<Point> classPoints) 
{ 
  float tempX=0;//计算聚类中所有数据点X的均值 
  float tempY=0;//计算聚类中所有数据点Y的均值 
  int count=0; //记录每一个类中数据点的数目 
  float errorX=INT_MAX; //假设初始时误差最大 
  float errorY=INT_MAX; 
  vector<Point>::iterator p_totalPoints; 
  vector<Point>::iterator p_classPoints; 
  Point temp; 
  int i; 
  while(errorX > 1e-6 && errorY > 1e-6) 
  { 
    for(p_totalPoints=totalPoints.begin(); p_totalPoints!=totalPoints.end(); p_totalPoints++) 
    { 
      //将所有的点就近分类 
      string className=computerDistance(p_totalPoints,classPoints); 
      (*p_totalPoints).className=className; 
    } 
    errorX=0; 
    errorY=0; 
    //按照均值重新划分聚类中心点 
    for(p_classPoints=classPoints.begin(); p_classPoints!=classPoints.end(); p_classPoints++) 
    { 
      count=0; 
      tempX=0; 
      tempY=0; 
      cout<<"Partition to cluster center "<<p_classPoints->className<<":"; 
      for(p_totalPoints=totalPoints.begin(); p_totalPoints!=totalPoints.end(); p_totalPoints++) 
      { 
        if((*p_totalPoints).className==(*p_classPoints).className) 
        { 
          cout<<" ("<<(*p_totalPoints).x<<","<<(*p_totalPoints).y<<") "; 
          count++; 
          tempX+=(*p_totalPoints).x; 
          tempY+=(*p_totalPoints).y; 
        } 
      } 
      cout<<endl; 
      tempX /=count; 
      tempY /=count; 
      errorX +=fabs(tempX - (*p_classPoints).x); 
      errorY +=fabs(tempY - (*p_classPoints).y); 
      //计算X与Y均值 
      (*p_classPoints).x=tempX; 
      (*p_classPoints).y=tempY; 
    } 
    int i=0; 
    for(p_classPoints=classPoints.begin(); p_classPoints!=classPoints.end(); p_classPoints++,i++) 
    { 
      cout<<"Cluster center "<<i+1<<": x="<<(*p_classPoints).x<<" y="<<(*p_classPoints).y<<endl; 
    } 
    cout<<"-----------------------------------------------------------------"<<endl; 
  } 
  cout<<"Result value convergence"<<endl; 
  i=0; 
  for(p_classPoints=classPoints.begin(); p_classPoints!=classPoints.end(); p_classPoints++,i++) 
  { 
    cout<<"Cluster center "<<i+1<<": x="<<(*p_classPoints).x<<" y="<<(*p_classPoints).y<<endl; 
  } 
  cout<<"-----------------------------------------------------------------"<<endl; 
}