关于K-means算法的个人解读

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Meteoraki/article/details/100624986

关于K-means算法的个人解读

一、简介

K-means算法是很典型的基于距离的聚类算法,何谓聚类,大家都明白的道理时物以类聚,人以群分;相同属性相同合适性格的人会被人们归为一类,他们甚至相互吸引,其中有着某些特定的特点来对某一类人群加以区分,数据和物也是一样。K-means算法的用处就是用来找到某批数据中的不同类别,并对不同的数据加以区分。该算法是基于数据之间的抽象距离,将其具现化到一维,二维,甚至三维点云中的距离来实现聚类。该算法采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。K-means算法简单,高效,当数据是密集且按一定区域分布时,聚类效果好。
①K-means算法的优点
算法快速简单,对于大数据的聚类规模伸缩性强,较为实用于数据为球状分布的数据集合,且随着距离计算方法的优化,聚类效果会有所优化。
②K-means算法的缺点
该算法虽然简单高效但其也存在着一些条件限制,比较突出的特点是K 值的选定是非常难以估计的,且最初的随机“伪中心点”的选择如果初始在不好的位置,聚类效果会有所影响,且随着数据的愈渐庞大,算法时间开销过大。

聚类效果展示图如下:(来自百度文库)聚类效果展示图(来自百度文库

二、算法思路

K-means算法的特点和局限是必须要输入预先规定的分为K类聚簇,即设K个初始的“伪中心点”,而后根据不断的迭代更新,直到所有的中心点都不再变化成为“真中心点”。
具体算法步骤如下:
1 .首先输入 k 的值,即我们自己制定需要通过聚类获取到K个小组;
2 .从随机生成的数据中随机选取 k 个数据点作为初始的“伪中心点”;
3 .其中对于K个中心点附近的其他的数据,通过计算每个点与每个“伪中心点”的距离,来进行比较,离哪个伪点近就将该数据归为这个伪点下所聚合的类。
4 .这时每一个“伪中心点”都管理着其他的同类数据,这是再运用算法,不断更新迭代伪点,使新生成的伪点不断靠近该类的中心,最终确定出“真中心点”。
5 .如果“伪中心点”与前一个“伪中心点”之间的距离小于某一个设置的最小误差即前后点变化趋于稳定,可以认为达到期望的值,算法终止。

三、常用的距离公式(最简单易懂的)欧几里得算法

①在二维和三维空间中的欧式距离的就是两点之间的距离,二维的公式是
d = sqrt((x1-x2)2 +(y1-y2)2)

②三维的公式是
d=sqrt((x1-x2)2+(y1-y2)2+(z1-z2)2)

③推广到n维空间,欧式距离的公式是
d=sqrt( ∑(xi1-xi2)2) 这里i=1,2…n

xi1表示第一个点的第i维坐标,xi2表示第二个点的第i维坐标

四、代码测试如下

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define N 12
#define K 2
typedef struct
{
double x;
double y;
}Point;
int center[N]; /// 判断每个点属于哪个簇
Point point[N] = {
{2.0, 3.0},
{2.5, 2.2},
{8.0, 9.0},
{8.9, 9.0},
{1.0, 3.0},
{7.0, 10.0},
{1.0, 2.0},
{9.0, 9.5},
{7.0, 3.0},
{1.0, 2.1},
{8.9, 9.8},
{2.1,1.5}
};
Point mean[K]; /// 保存K个中心点
//欧几里得距离
double OustyleDistance(Point p1, Point p2)
{
double d;
d = sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.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;
double min;
double dis[N][K];
for(i = 0; i < N; ++i)
{
min = 999999.0;
for(j = 0; j < K; ++j)
{
dis[i][j] = OustyleDistance(point[i], mean[j]);
// printf("%f\n", distance[i][j]); // 可以用来测试对于每个点与3个中心点之间的距离
}
for(q = 0; q < K; ++q)
{
if(dis[i][q] < min)
{
min = dis[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[2].x;
mean[1].y = point[2].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;
}
在这里插入图片描述
代码参考blog:http://blog.csdn.net/triumph92/article/details/41128049

猜你喜欢

转载自blog.csdn.net/Meteoraki/article/details/100624986