代码之神 2019-06-26
k均值聚类的实现中,把每个样本划分到单一的类别中,亦即是每个样本只能属于一种类别,不能属于多种类别。这样的划分,称为硬划分。
为了解决硬划分所带来的问题,因此有了称为软划分的聚类算法,这一类算法中,每个样本不再只能属于一种类别,而是对于每个样本,都有对应的隶属度数组,数组里的每一个元素代表该样本属于某种类别的程度。而该样本的隶属度数组中的总值等于1。
其目标方程以及优化的推导过程可以参照下述链接,个人认为推导过程十分详细。
模糊c均值聚类推导
根据相关的推导结果,具体实现步骤如下:
各个相关函数都有注释。
#include <time.h> #include <stdlib.h> #include <stdio.h> #include <math.h> typedef struct{ double x; double y; } Position; //随机生成二维数据点 //len:节点数量 //range:节点x、y值的范围 Position *randomPosition(int len, int range){ srand((unsigned)time(NULL)); Position *allPos = (Position *)malloc(len * sizeof(Position)); short a = 1; //人为差异量 for (int i = 0; i < len; ++i) { if (a) { allPos[i].x = (double)rand() / 2147483647 * range; allPos[i].y = (double)rand() / 2147483647 * range; a = 0; } else if (!a) { allPos[i].x = (double)rand() / 2147483647 * range+50; allPos[i].y = (double)rand() / 2147483647 * range+50; a = 1; } } return allPos; } //fcm初始化:隶属度矩阵 //posNum:节点数量 //clusterNum:聚类中心数量 double **init(int posNum, int clusterNum){ double **u = (double **)malloc(sizeof(double *) * clusterNum); for (int i = 0; i <clusterNum; ++i) { u[i] = (double *)malloc(sizeof(double) * posNum); } srand((unsigned)time(NULL)); double sum; //初始化u:sigmaU[i]=1 for (int i = 0; i < posNum; ++i) { sum=1; for (int x = 0; x < clusterNum - 1; ++x) { u[x][i] = ((double)rand() / 2147483647) * sum; sum -= u[x][i]; printf("u[%d][%d]:%f ",x,i,u[x][i]); } u[clusterNum-1][i]=sum; printf("u[%d][%d]:%f\n",clusterNum-1,i,u[clusterNum-1][i]); } return u; } //输出所有数据点 //allPos:节点数组 //len:节点数量 void outputPos(Position *allPos, int len){ for (int i = 0; i < len; ++i) printf("position %d:(%f,%f)\n", i, allPos[i].x, allPos[i].y); return; } //所有隶属于聚类中心i的点的隶属程度和 //u:隶属度矩阵 //posNum:节点数量 //i:第i个聚类中心 //m:隶属度因子 double sumUi(double** u,int posNum,int i,int m){ double res=0; for(int x=0;x<posNum;++x) res+=pow(u[i][x],m); return res; } //所有隶属于聚类中心i的点的x坐标的和 //u:隶属度矩阵 //allPos:节点数组 //posNum:节点数量 //i:第i个聚类中心 //m:隶属度因子 double sumXi(double** u,Position* allPos,int posNum,int i,int m){ double res=0; for(int x=0;x<posNum;++x) res+=allPos[x].x*pow(u[i][x],m); return res; } //所有隶属于聚类中心i的点的y坐标的和 //u:隶属度矩阵 //posNum:节点数量 //i:第i个聚类中心 //m:隶属度因子 double sumYi(double** u,Position* allPos,int posNum,int i,int m){ double res=0; for(int x=0;x<posNum;++x) res+=allPos[x].y*pow(u[i][x],m); return res; } //第j个节点到所有聚类中心距离总和 //pos:第j个节点 //cluster:聚类中心数组 //clusterNum:聚类中心数量 //m:隶属度因子 double sumDis(Position pos,Position* cluster,int clusterNum,int m){ double res=0; for(int i=0;i<clusterNum;++i) res+=(double)1/pow(pow(pos.x-cluster[i].x,2)+pow(pos.y-cluster[i].y,2),(double)1/(m-1)); return res; } //更新聚类中心 //allPos:节点数组 //cluster:聚类中心数组 //u:隶属度矩阵 //posNum:节点数量 //clusterNum:聚类中心数量 //m:隶属度因子 void updateCluster(Position* allPos,Position* cluster,double** u,int posNum,int clusterNum,int m){ for(int i=0;i<clusterNum;++i){ cluster[i].x=sumXi(u,allPos,posNum,i,m)/sumUi(u,posNum,i,m); cluster[i].y=sumYi(u,allPos,posNum,i,m)/sumUi(u,posNum,i,m); } } //更新隶属度矩阵 //allPos:节点数组 //cluster:聚类中心数组 //u:隶属度矩阵 //posNum:节点数量 //clusterNum:聚类中心数量 //m:隶属度因子 void updateU(Position* allPos,Position* cluster,double** u,int posNum,int clusterNum,int m){ double disXI; for(int i=0;i<clusterNum;++i) for(int x=0;x<posNum;++x){ disXI=pow(pow(allPos[x].x-cluster[i].x,2)+pow(allPos[x].y-cluster[i].y,2),(double)1/(m-1)); u[i][x]=(double)1/(disXI*sumDis(allPos[x],cluster,clusterNum,m)); } } //输出目标函数 //allPos:节点数组 //cluster:聚类中心数组 //u:隶属度矩阵 //posNum:节点数量 //clusterNum:聚类中心数量 //m:隶属度因子 void outpuCost_fun(Position* allPos,Position* cluster,double** u,int posNum,int clusterNum,int m){ double res=0; for(int i=0;i<clusterNum;++i) for(int x=0;x<posNum;++x) res+=(pow(u[i][x],m)*(pow(allPos[x].x-cluster[i].x,2)+pow(allPos[x].y-cluster[i].y,2))); printf("costFun:%f\n",res); } //..略,同上 void outputU(double** u,int posNum,int clusterNum){ for(int i=0;i<posNum;++i){ for(int x=0;x<clusterNum;++x) printf("u[%d][%d]:%f ",x,i,u[x][i]); printf("\n"); } } void fuzzy_Cmeans(int posNum,int clusterNum, int m, int iterTime,int range) { Position* allPos=randomPosition(posNum,range); Position* cluster=(Position*)malloc(sizeof(Position)*clusterNum); double** u=init(posNum,clusterNum); for (int i = 0; i < iterTime; ++i) { updateCluster(allPos,cluster,u,posNum,clusterNum,m); updateU(allPos,cluster,u,posNum,clusterNum,m); outpuCost_fun(allPos,cluster,u,posNum,clusterNum,m); //outputU(u,posNum,clusterNum); } } int main(){ fuzzy_Cmeans(100,10,3,500,500); return 0; }
可以看到,其目标函数的值是逐渐减少的,最后达到稳定的状态。