kmeans算法:
1、概念
聚类分析简称聚类(clustering),是一个把数据集划分成子集的过程,每一个子集是一个簇(cluster),使得簇中的样本彼此相似,但与其他簇中的样本不相似。
聚类分析不需要事先知道样本的类别,甚至不用知道类别个数,因此它是一种无监督的学习算法,一般用于数据探索,比如群组发现和离群点检测,还可以作为其他算法的预处理步骤。
2、算法思想
Kmeans算法(k均值算法)是一种简单的聚类算法,属于划分式聚类算法,当给定一个数据集D时,
Kmeans算法的步骤如下:
若n是样本数,m是特征维数,k是簇数,t是迭代次数,则Kmeans算法的时间复杂度为O(tknm),与样本数量线性相关,所以在处理大数据集合时比较高效,伸缩性好。空间复杂度为O((n+k)*m)。
K 值选取:k的值是用户指定的,表示需要得到的簇的数目。在运用Kmeans算法时,我们一般不知道数据的分布情况,不可能知道数据的集群数目,所以一般通过枚举来确定k的值。另外,在实际应用中,由于Kmean一般作为数据预处理,或者用于辅助分类贴标签,所以k一般不会设置很大。
初始质心的选取:Kmeans算法对初始质心的选取比较敏感,选取不同的质心,往往会得到不同的结果。初始质心的选取方法,常用以下两种的简单方法:一种是随机选取,一种是用户指定。
需要注意的是,无论是随机选取还是用户指定,质心都尽量不要超过原始数据的边界,即质心每一维度上的值要落在原始数据集每一维度的最小与最大值之间。
距离度量的方法:
距离度量方法(或者说相似性度量方法)有很多种,常用的有欧氏距离,余弦相似度,街区距离,汉明距离等等。在Kmeans算法中,一般采用欧氏距离计算两个点的距离,欧氏距离如下:
质心的计算:所有点的均值
3、代码实现
//聚类算法实现(初始化聚类点,计算每个点到聚类中心的距离,通过每个点到聚类中心的距离进行新一轮的聚类,计算每一个类的平方误差函数,)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "stdafx.h"
#define N 11
#define K 3
float getdist();
void cluster();
float getE();
void getmean();
typedef struct {
float x;
float y;
}Point;
int center[N];
Point clustermean[K];
Point point[N] = { { 2.0,10.0 },
{ 2.0, 5.0 },
{ 8.0, 4.0 },
{ 5.0, 8.0 },
{ 7.0, 5.0 },
{ 6.0, 4.0 },
{ 1.0, 2.0 },
{ 4.0, 9.0 },
{ 7.0, 3.0 },
{ 1.0, 3.0 },
{ 3.0, 9.0 }
};
float getdist(Point point1, Point point2)
{
float m;
m = sqrt((point1.x - point2.x)*(point1.x - point2.x) + (point1.y - point2.y)*(point1.y - point2.y));
return m;
};
void cluster()
{
int i, j; float m1,m2;
for (i = 0; i < N; ++i)
{
m1 = 99999; n = 0;
for (j = 0; j < K; ++j)
{
m2 = getdist(point[i], point[j]);
if (m2 < m1)
{
m1 = m2; ++n;
}
}
center[i] = n;
}
};
float getE()
{
int i, j; float m1 = 0.0;
for (i = 0; i<k; ++i)
for (j = 0; j<N; ++j)
{
m = getdist(poin[i], point[j]);
m1 += m;
}
return m1;
}
void getmean(center[N])
{
int i, j;
point sum0, sum1, sum2;
for (i = 0; i<N; ++i)
{
sum0.x = sum1.x = sum2.x = sum0.y = sum1.y = sum2.y = 0;
n0 = n1 = n2 = 0
for (j = 0; j<N; ++j)
{
if (center[i] == 0)
{
sum0.x += point[i].x;
sum0.y += point[i].y;
n0++;
clustermean[0].x = sum0.x / n0;
clustermean[0].y = sum0.y / n0;
}
if (cneter[i] == 1)
{
sum1.x += point[i].x;
sum1.y += point[i].y;
n1++;
clustermean[1].x = sum0.x / n1;
clustermean[1].y = sum0.y / n1;
}
if (center[i] == 2)
{
sum2.x += point[i].x;
sum2.y += point[i].y;
n2++;
clustermean[2].x = sum2.x / n0;
clustermean[2].y = sum2.y / n0;
}
}
}
}
int main()
{
int i, j, k;
float temp1, temp2;
int n = 0;
printf("----------Data sets----------\n");
for (j = 0; j < N; ++j)
{
printf("\t( %.0f, %.0f )\n", point[i].x, point[i].y);
}
printf("-----------------------------\n");
srand((unsigned int)time(null));
//初始化均值聚类中心
for (i = 0; i<K; ++i)
{
j = rand() % K;
mean[i].x = point[j].x;
mean[i].y = point[j].y;
}
cluster();//聚类
temp1 = getE();//获取方差和
n++;
getmean(center[N]);//获取均值中心
cluster();// 根据均值重新聚类
temp2 = getE();// 获取方差和
n++;
while ((temp1 - temp2) != 0)
{
temp1 = temp2;
getmean(center[N]);
cluster();
temp2 = getE();
n++;
printf("The E%d is: %f\n", n, temp2);
}
printf("The total number of cluster is: %d\n\n", n); /// 统计出迭代次数
printf("pause")
return 0;
}
修改版:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define N 11
#define K 3
typedef struct
{
float x;
float y;
}Point;
int center[N]; /// 判断每个点属于哪个簇
Point point[N] = {
{2.0, 10.0},
{2.0, 5.0},
{8.0, 4.0},
{5.0, 8.0},
{7.0, 5.0},
{6.0, 4.0},
{1.0, 2.0},
{4.0, 9.0},
{7.0, 3.0},
{1.0, 3.0},
{3.0, 9.0}
};
Point mean[K]; /// 保存每个簇的中心点
float getDistance(Point point1, Point point2)
{
float d;
d = sqrt((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y));
return d;
}
/// 计算每个簇的中心点
void getMean(int center[N])
{
Point tep;
int i, j, count = 0;
for(i = 0; i < K; ++i)
{
count = 0;
tep.x = 0.0; /// 每算出一个簇的中心点值后清0
tep.y = 0.0;
for(j = 0; j < N; ++j)
{
if(i == center[j])
{
count++;
tep.x += point[j].x;
tep.y += point[j].y;
}
}
tep.x /= count;
tep.y /= count;
mean[i] = tep;
}
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 i, j;
float cnt = 0.0, sum = 0.0;
for(i = 0; i < K; ++i)
{
for(j = 0; j < N; ++j)
{
if(i == center[j])
{
cnt = (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 += cnt;
}
}
}
return sum;
}
/// 把N个点聚类
void cluster()
{
int i, j, q;
float min;
float distance[N][K];
for(i = 0; i < N; ++i)
{
min = 999999.0;
for(j = 0; j < K; ++j)
{
distance[i][j] = getDistance(point[i], mean[j]);
/// printf("%f\n", distance[i][j]); /// 可以用来测试对于每个点与3个中心点之间的距离
}
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");
}
int main()
{
int i, j, n = 0;
float temp1;
float temp2, t;
printf("----------Data sets----------\n");
for(i = 0; i < N; ++i)
{
printf("\t( %.0f, %.0f )\n", point[i].x, point[i].y);
}
printf("-----------------------------\n");
/*
可以选择当前时间为随机数
srand((unsigned int)time(NULL));
for(i = 0; i < K; ++i)
{
j = rand() % K;
mean[i].x = point[j].x;
mean[i].y = point[j].y;
}
*/
mean[0].x = point[0].x; /// 初始化k个中心点
mean[0].y = point[0].y;
mean[1].x = point[3].x;
mean[1].y = point[3].y;
mean[2].x = point[6].x;
mean[2].y = point[6].y;
cluster(); /// 第一次根据预设的k个点进行聚类
temp1 = getE(); /// 第一次平方误差
n++; /// n计算形成最终的簇用了多少次
printf("The E1 is: %f\n\n", temp1);
getMean(center);
cluster();
temp2 = getE(); /// 根据簇形成新的中心点,并计算出平方误差
n++;
printf("The E2 is: %f\n\n", temp2);
while(fabs(temp2 - temp1) != 0) /// 比较两次平方误差 判断是否相等,不相等继续迭代
{
temp1 = temp2;
getMean(center);
cluster();
temp2 = getE();
n++;
printf("The E%d is: %f\n", n, temp2);
}
printf("The total number of cluster is: %d\n\n", n); /// 统计出迭代次数
system("pause");
return 0;
}