下面是K-Means算法的c语言实现
#include<stdio.h> #include <stdlib.h> #include <time.h> typedef struct { float x; float y; } Point; int center[]; //center[]是按簇存储样本点,方便判断每个簇中的样本点 Point point[]; //存放样本点 Point mean[]; //存放聚类中心点 //读取文件函数,读取M行N列的二维数组 float readfile(int M, int N) { FILE *fp; int i, j; float array[M][N]; if ((fp = fopen("data.txt", "r")) == NULL) { printf("cannot open file\n"); exit(1); } for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { fscanf(fp, "%f", &array[i][j]); // printf("%f\n", array[i][j]);//用于测试读取文件的内容 } printf("\t( %f, %f )\n", array[i][0], array[i][1]); point[i].x = array[i][0]; point[i].y = array[i][1]; } fclose(fp); return array[M][N]; } //计算两个点的欧氏距离 float getDistance(Point point1, Point point2) { float distance; distance = sqrt( (point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)); return distance; } //在第一次聚类之后,计算每个簇的中心点,是根据每个簇中所有样本点之和,除以该簇中样本点个数作为新的中心点 void getMean(int M, int K) { Point temp; int i, j, count = 0; for (i = 0; i < K; i++) { count = 0; temp.x = 0; temp.y = 0; for (j = 0; j < M; j++) { if (i == center[j]) { count++;//count记录该簇中样本点的个数 temp.x += point[j].x; temp.y += point[j].y; } } temp.x /= count; temp.x /= count; mean[i] = temp; } for (i = 0; i < K; ++i) { printf("The new center point of %d is : \t( %f, %f )\n", i + 1, mean[i].x, mean[i].y); } } //计算平方误差,是根据簇中样本点与该簇中心点的欧氏距离的平方计算的 float getE(int M, int K) { int i, j; float E = 0, sum = 0; for (i = 0; i < K; i++) { for (j = 0; j < M; j++) { if (i == center[j]) { E = (point[j].x - mean[i].x) * (point[j].x - mean[i].x) + (point[j].y - mean[i].y) * (point[j].y - mean[i].y); sum += E; } } } return sum; } //聚类 void cluster(int M, int K) { int i, j, q; float min; float distance[M][K]; //第m个样本点距第k个中心点的距离,第一行存放第一个数据点距离各簇的中心点的距离 for (i = 0; i < M; i++) { min = 999999; for (j = 0; j < K; j++) { //对每个簇的中心点循环 distance[i][j] = getDistance(point[i], mean[j]); //计算第i个样本点距离第j个中心点的欧氏距离 // printf("%f\n", distance[i][j]); //输出各个距离 } //对二维数组的每一行,也就是每个样本点离各个中心点的距离,选取距离最小的中心点作为这个数据点的簇 for (q = 0; q < K; q++) { //循环的是中心点 if (distance[i][q] < min) { min = distance[i][q]; center[i] = q; } } printf("( %.0f, %.0f )\t in cluster-%d\n", point[i].x, point[i].y, center[i] + 1); } printf("-----------------------------\n"); } //初始化聚类中心点 void initial(int M,int K){ int i,j; srand((unsigned int) time(NULL)); for (i = 0; i < K; i++) { j = rand() % M; //产生0~M-1的随机数 mean[i].x = point[j].x; mean[i].y = point[j].y; } } int main() { int i, j, n = 0, M, N, K; float temp1=0, temp2=0; printf("please input rows and columns(separated by spaces or enter):"); scanf("%d%d", &M, &N); printf("----------Data sets----------\n"); readfile(M, N); printf("-----------------------------\n"); printf("please input the number of cluster:"); scanf("%d", &K); //用随机数初始化聚类中心点 initial(M,K); cluster(M, K); /// 第一次根据预设的k个点进行聚类 temp1 = getE(M, K); /// 第一次平方误差 n++; /// n计算形成最终的簇用了多少次 printf("The E1 is: %f\n\n", temp1); do{ temp1=temp2; getMean(M,K); cluster(M,K); temp2=getE(M,K); n++; printf("The E%d is: %f\n\n",n, temp2); }while(fabs(temp2 - temp1) != 0); printf("The total number of cluster is: %d\n\n", n); /// 统计出迭代次数 }
由于不知道该怎么将读取的数据存储到结构体中,所以就定义了一个二维数组,用于存储,所以就导致只能处理具有两个属性的数据。
K-Means算法是一种基于划分的聚类算法,使用一定的划分准则,把样本集合划分为k个分区,每个分区代表一簇,
且在同一簇中,样本是相似的,不同簇的样本是相异的。
优化的目标是,使得类内个样本点的距离尽可能小,类间距离尽可能大。判断依据是使最小化平方误差越小,则簇内样本相似度就越高。
具体过程:
1、选取K个样本作为初始聚类中心
2、对每个待分类样本,计算它与每个聚类中心的距离,将其划给距离最小的聚类中心点所代表的那一类
3、重新计算每类的聚类中心
4、若达到最大迭代次数,则停止,否则跳到步骤2
选取k个初始聚类中心的方法:求以每个特征点为球心,某一正数d为半径的球型区域中的特征点个数(即该特征点的密度),选取密度最大的特征点为第一个初始聚类中心,然后,在与该中心大于某个距离d的那些特征点中选取另一个具有最大密度的特征点作为第二个初始聚类中心,直到选取k个初始类心
K均值聚类算法一定会收敛的
算法停止条件:k均值算法会在最后一次迭代结果与上一次迭代结果相同时停止迭代