基于C++的K-means聚类算法实现

1.   简介:

K-MEANS算法是输入聚类个数k,以及包含 n个数据对象的数据库,输出满足方差最小标准k个聚类的一种算法。k-means 算法接受输入量 k ;然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。

聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。

2.   算法流程

1)       随机选取数据集中的k个数据点作为初始的聚类中心:

2)       分别计算每个数据点到每个中心的距离,选取距离最短的中心点作为其聚类中心:

3)       利用目前得到的聚类重新计算中心点:

4)       重复步骤2和3直到收敛(达到最大迭代次数或聚类中心不再移动)

 

 

3.   常见问题

1)      初始质心的选择

选择适当的初始质心是基本kmeans算法的关键步骤。常见的方法是随机的选取初始质心,但是这样簇的质量常常很差。处理选取初始质心问题的一种常用技术是:多次运行,每次使用一组不同的随机初始质心,然后选取具有最小SSE(误差的平方和)的簇集。这种策略简单,但是效果可能不好,这取决于数据集和寻找的簇的个数。

第二种有效的方法是,取一个样本,并使用层次聚类技术对它聚类。从层次聚类中提取K个簇,并用这些簇的质心作为初始质心。该方法通常很有效,但仅对下列情况有效:(1)样本相对较小,例如数百到数千(层次聚类开销较大);(2)K相对于样本大小较小

第三种选择初始质心的方法,随机地选择第一个点,或取所有点的质心作为第一个点。然后,对于每个后继初始质心,选择离已经选取过的初始质心最远的点。使用这种方法,确保了选择的初始质心不仅是随机的,而且是散开的。但是,这种方法可能选中离群点。此外,求离当前初始质心集最远的点开销也非常大。为了克服这个问题,通常该方法用于点样本。由于离群点很少(多了就不是离群点了),它们多半不会在随机样本中出现。计算量也大幅减少。

2)      距离的度量

  常用的距离度量方法包括:欧几里得距离和余弦相似度。两者都是评定个体间差异的大小的。欧几里得距离度量会受指标不同单位刻度的影响,所以一般需要先进行标准化,同时距离越大,个体间差异越大;空间向量余弦夹角的相似度度量不会受指标刻度的影响,余弦值落于区间[-1,1],值越大,差异越小。

       本次实验采用欧几里得距离度量。

3)      算法停止条件

计算各个聚类中心的中心向量值Zj(k+1),j=1.2.3…k,如果Zj(k+1)=Zj(k)则可以停止。

算法实现

1.   实验数据--data.txt:

实验数据为处理后的IRIS数据集。数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。

文件内容:

K=3,D=4,N=150

5.1  3.5  1.4  0.2

4.9  3     1.4  0.2

4.7  3.2  1.3  0.2

4.6  3.1  1.5  0.2

5     3.6  1.4  0.2

5.4  3.9  1.7  0.4

4.6  3.4  1.4  0.3

5     3.4  1.5  0.2

数据说明:

第一行表示分为三类,4个特征,150个数据后面依次为数据信息。

2.   输出数据—result.txt

输出数据输出实验数据聚类后的分类信息,与真实信息对比来分析程序聚类准确率。

文件内容:

0

2

0

1

0

              …

数据说明:

0,1,2依次对应表示Setosa,Versicolour,Virginica三类花。

3.   程序运行截图:

程序每次计算输出新的聚类中心点,中心向量值(即各点与聚类中心的距离和平均值),以及分类信息,当中心向量值不变,聚类完成

源代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int K,N,D;  //聚类的数目,数据量,数据的维数
float **data;  //存放数据
int *in_cluster;  //标记每个点属于哪个聚类
float **cluster_center;  //存放每个聚类的中心点

float **array(int m,int n);
void freearray(float **p);
float **loadData(int *k,int *d,int *n);
float getDistance(float avector[],float bvector[],int n);
void cluster();
float getDifference();
void getCenter(int in_cluster[]);

int  main()
{
	int i,j,count=0;
	float temp1,temp2;
	data=loadData(&K,&D,&N);
	printf("Data sets:\n");
	for(i=0;i<N;i++)
		for(j=0;j<D;j++){
			printf("%-8.2f",data[i][j]);
			if((j+1)%D==0)    putchar('\n');
		}
		printf("-----------------------------\n");

		srand((unsigned int)(time(NULL)));  //随机初始化k个中心点
		for(i=0;i<K;i++)
			for(j=0;j<D;j++)
				cluster_center[i][j]=data[(int)((double)N*rand()/(RAND_MAX+1.0))][j];

		cluster();  //用随机k个中心点进行聚类
		temp1=getDifference();  //第一次中心点和所属数据点的距离之和
		count++;
		printf("The difference between data and center is: %.2f\n\n", temp1);

		getCenter(in_cluster);
		cluster();  //用新的k个中心点进行第二次聚类
		temp2=getDifference();
		count++;
		printf("The difference between data and center is: %.2f\n\n",temp2);

		while(fabs(temp2-temp1)!=0){   //比较前后两次迭代,若不相等继续迭代
			temp1=temp2;
			getCenter(in_cluster);
			cluster();
			temp2=getDifference();
			count++;
			printf("The %dth difference between data and center is: %.2f\n\n",count,temp2);
		}

		printf("\nThe total number of cluster is: %d\n",count);  //统计迭代次数
		//system("pause");  //gcc编译需删除 
		return 0;
}


//动态创建二维数组
float **array(int m,int n)
{
	int i;
	float **p;
	p=(float **)malloc(m*sizeof(float *));
	p[0]=(float *)malloc(m*n*sizeof(float));
	for(i=1;i<m;i++)    p[i]=p[i-1]+n;
	return p;
}

//释放二维数组所占用的内存
void freearray(float **p)
{
	free(*p);
	free(p);
}

//从data.txt导入数据,要求首行格式:K=聚类数目,D=数据维度,N=数据量
float **loadData(int *k,int *d,int *n)
{
	int i,j; 
	float **arraydata;
	FILE *fp;
	if((fp=fopen("data.txt","r"))==NULL)    fprintf(stderr,"cannot open data.txt!\n");
	if(fscanf(fp,"K=%d,D=%d,N=%d\n",k,d,n)!=3)        fprintf(stderr,"load error!\n");
	arraydata=array(*n,*d);  //生成数据数组
	cluster_center=array(*k,*d);  //聚类的中心点
	in_cluster=(int *)malloc(*n * sizeof(int));  //每个数据点所属聚类的标志数组
	for(i=0;i<*n;i++)
		for(j=0;j<*d;j++)
			fscanf(fp,"%f",&arraydata[i][j]);  //读取数据点
	return arraydata;
}

//计算欧几里得距离
float getDistance(float avector[],float bvector[],int n)
{
	int i;
	float sum=0.0;
	for(i=0;i<n;i++)
		sum+=pow(avector[i]-bvector[i],2);
	return sqrt(sum);
}

//把N个数据点聚类,标出每个点属于哪个聚类
void cluster()
{
	int i,j;
	float min;
	float **distance=array(N,K);  //存放每个数据点到每个中心点的距离
	//float distance[N][K];  //也可使用C99变长数组
	FILE *fp2;
	if((fp2=fopen("result.txt","w"))==NULL) {
		printf("File cannot be opened/n");
		exit(0);
	}
	for(i=0;i<N;++i){
		min=9999.0;
		for(j=0;j<K;++j){
			distance[i][j] = getDistance(data[i],cluster_center[j],D);
			//printf("%f\n", distance[i][j]);
			if(distance[i][j]<min){
				min=distance[i][j];
				in_cluster[i]=j;
			}
		}
		printf("data[%d] 属于类-%d\n",i,in_cluster[i]);
		fprintf(fp2,"%d \n",in_cluster[i]);//写入文件
	}
	printf("-----------------------------\n");
	free(distance);
}

//计算所有聚类的中心点与其数据点的距离之和
float getDifference()
{
	int i,j;
	float sum=0.0;
	for(i=0;i<K;++i){
		for(j=0;j<N;++j){
			if(i==in_cluster[j])
				sum+=getDistance(data[j],cluster_center[i],D);
		}
	}
	return sum;
}

//计算每个聚类的中心点
void getCenter(int in_cluster[])
{
	float **sum=array(K,D);  //存放每个聚类中心点
	//float sum[K][D];  //也可使用C99变长数组
	int i,j,q,count;
	for(i=0;i<K;i++)
		for(j=0;j<D;j++)
			sum[i][j]=0.0;
	for(i=0;i<K;i++){
		count=0;  //统计属于某个聚类内的所有数据点
		for(j=0;j<N;j++){
			if(i==in_cluster[j]){
				for(q=0;q<D;q++)
					sum[i][q]+=data[j][q];  //计算所属聚类的所有数据点的相应维数之和
				count++;
			}
		}
		for(q=0;q<D;q++)
			cluster_center[i][q]=sum[i][q]/count;
	}
	printf("The new center of cluster is:\n");
	for(i = 0; i < K; i++)
		for(q=0;q<D;q++){
			printf("%-8.2f",cluster_center[i][q]);
			if((q+1)%D==0)    putchar('\n');
		}
		free(sum);
}


  • 9
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值