聚类
用数学语言来说就是,假设N个样点构成集合A,根据欧氏距离将A划分为K个子集,则划分子集的过程就是均值聚类的实现过程。
如何实现
K均值聚类需要实现被告知分为多少个聚类,即K值为多大;
一开始需要找到K个随机点作为聚类中心参与聚类;
聚类中心扩充自己的领地,各个待聚类的点需要根据自己到各个聚类中心的距离来确定自己属于哪个聚类;
根据新的聚类结果重新计算聚类中心,重复第二、三步,直到没有点在一次聚类的过程中发生变化结束。
KMeans
k-means算法是一种基于样本间相似性度量的间接聚类方法,属于非监督学习方法。此算法以k为参数,把n 个对象分为k个簇,以使簇内具有较高的相似度,而且簇间的相似度较低。相似度的计算根据一个簇中对象的平均值(被看作簇的质心Centriod)来进行。此算法首先随机选择k个对象,每个对象代表一个聚类的质心。对于其余的每一个对象,根据该对象与各聚类质心之间的距离,把它分配到与之最相似的聚类中。然后,计算每个聚类的新质心。重复上述过程,直到准则函数会聚。k-means算法是一种较典型的逐点修改迭代的动态聚类算法,其要点是以误差平方和为准则函数。逐点修改类中心:一个象元样本按某一原则,归属于某一组类后,就要重新计算这个组类的均值,并且以新的均值作为凝聚中心点进行下一次象元素聚类;逐批修改类中心:在全部象元样本按某一组的类中心分类之后,再计算修改各类的均值,作为下一次分类的凝聚中心点。
越分散的数据聚类效果越好,对于样本点有200以下的数据聚类效果较好,大量数据的出现必然带来大量的干扰值,噪音点。我使用该算法处理超声波测距得到的数据,实验发现,一组数据总是在一个均值周围波动,而且偏离较大,大致可以分为三簇,取中间一簇计算均值作为zui'zh
/*
Kmeans算法实现(此处只考虑元组只有一个属性的情况)
File: Kmeans.h
Author: Wang Yize
Create: 2018-5-6
*/
#pragma once
#ifndef _KMEANS_H_
#define _KMEANS_H_
#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>
using namespace std;
#define k 3 //聚类簇数
typedef struct distance_struct
{
double distance;
double howFar;
int cluster;
} Distance;
double Kmeans(vector<Distance> distances); //算法主函数,返回最终的计算值
void getCentriods(vector<Distance> distances, Distance *means); //得到最初的中心点
int findCloseCentriod(double value, Distance *means); //找到离该点最近的中心点并返回中心点的簇标号
#endif // !_KMEANS_H_
/*
Kmeans算法实现(此处只考虑元组只有一个属性的情况)
File: Kmeans.cpp
Author: Wang Yize
Create: 2018-5-6
*/
#include "Kmeans.h"
int main(int argc, char *argv[])
{
int count = 0;
vector<Distance> distances;
Distance distance;
do
{
count++;
cin >> distance.distance;
if (distance.distance == 0)
break;
distance.howFar = 1.0E100;
distance.cluster = 0;
distances.push_back(distance);
} while (true);
cout << "样本数量: " << count - 1 << endl;
cout << Kmeans(distances) << endl;
return 0;
}
double Kmeans(vector<Distance> distances)
{
Distance means[k]; // 3个中心点
getCentriods(distances, means); // 获取最初的3个中心点
int completeFlag = 1, runTime = 0, cluster;
while (completeFlag)
{
runTime++;
completeFlag = 0;
for (vector<Distance>::size_type i = 0; i < distances.size(); i++)
{ // 循环检验是否需要重新定位该点
cluster = findCloseCentriod(distances[i].distance, means);
if (distances[i].cluster == cluster)
{
}
else
{//若需要,更新簇值,并置Flag,为下一次循环做准备
distances[i].cluster = cluster;
completeFlag = 1;
}
}
//一次循环结束后,需要将中心点的坐标值置零
for (int i = 0; i < k; i++)
{
means[i].distance = means[i].howFar = 0;
}
//重新计算中心点坐标值
for (vector<Distance>::size_type i = 0; i < distances.size(); i++)
{
means[distances[i].cluster - 1].distance += distances[i].distance;
means[distances[i].cluster - 1].howFar++;
}
for (int i = 0; i < k; i++)
{
means[i].distance /= means[i].howFar;
}
//方便调试用,已注释
/*cout << "运行次数: " << runTime << endl;
for (vector<Distance>::size_type i = 0; i < distances.size(); i++)
{
cout << distances[i].distance << "," << distances[i].cluster << endl;
}
for (int i = 0; i < k; i++)
{
cout << means[i].distance << endl;
}*/
}
return means[1].distance;
}
void getCentriods(vector<Distance> distances, Distance *means)
{//取vector内1/4、2/4、3/4处的值作为初始中心点
for (int i = 0; i < k; i++)
{
(means + i)->distance = distances[distances.size() * (i + 1) / 4 - 1].distance;
(means + i)->cluster = i + 1;
}
/*for (int i = 0; i < k; i++)
{
cout << "初始聚类中心: [" << (means + i)->cluster << "]" << (means + i)->distance << endl;
}*/
}
int findCloseCentriod(double value, Distance *means)
{
int cluster;
double distance;
for (int i = 0; i < k; i++)
{
if (i == 0)
{
distance = abs(value - (means + i)->distance);
cluster = (means+i)->cluster;
}
else
{
if (distance > abs(value - (means + i)->distance))
{
distance = abs(value - (means + i)->distance);
cluster = (means + i)->cluster;
}
}
}
return cluster;
}