K-均值聚类算法(k-means)的C++实现

k-均值聚类算法(k-means)主要用于解决 距离空间 上目标点集的自动分类问题。

本篇博文目的在于

  1. 阐述 k-means 聚类算法的数学模型
  2. 利用C++改写并封装K-Means算法,接口数据向MATLAB中kmeans函数看齐。

k-means算法的数学原理

k-means聚类算法的问题描述如下:

假设我们的研究对象可以一一映射到 dimension 维欧式空间中的一个点 P 。现在我们需要把空间中相近的目标点圈起来,看作是一类对象,一共需要分为 k 类。

先来介绍k-means算法涉及到的数据结构:

struct kmeans
{double clusterCenter[k][dimension];int clusterAssignment[targetCount];
}

clusterCenter[m] 代表第m类对象的类中心,为 dimension 维的向量,共有k个这样的聚类中心。
clusterAssignment[m] 则记录了目标集合中第m个目标点所属的类。

k-means算法是一个迭代算法,迭代的数学公式如下:
d(Pm,clusterCenteri(index))=min{d(Pm,clusterCenteri(j)),j=1,2,...,k}clusterAssignmenti+1(m)=indexclusterCenteri+1(n)=mean({Pj∣clusterAssignmenti+1(j)=n})d(P_m,clusterCenter_i(index))=min\{d(P_m,clusterCenter_i(j)),j=1,2,...,k\} \\ clusterAssignment_{i+1}(m)=index \\ clusterCenter_{i+1}(n)=mean(\{P_j|clusterAssignment_{i+1}(j)=n\}) d(Pm​,clusterCenteri​(index))=min{d(Pm​,clusterCenteri​(j)),j=1,2,...,k}clusterAssignmenti+1​(m)=indexclusterCenteri+1​(n)=mean({Pj​∣clusterAssignmenti+1​(j)=n})
其中 d 为定义在目标所在空间上的距离函数。i为迭代次数,m,n,j为下标变量。

用一般语言对迭代过程进行描述:对于任一目标点 P,设距离其最近的聚类中心为类 C 中心,把目标点 P 归于类别 C 。完成所有目标点的归类后,将同一类的目标点求其质心,作为该类的新的聚类中心。重复上述操作,直至所有目标点的分类不再发生改变。

k-means算法的c++实现

对于k-means算法的c++代码已经比较常见,本文附上的C++代码则更关注于将k-means算法进行封装,提高算法模块的独立性,便于再次开发,向MATLAB中的kmeans函数看齐。

本节所提供的c++代码主要在CSDN博主—— 忆之独秀 的博文代码基础上进行修改与封装。原文请见链接:
忆之独秀的CSDN博客

引用博主的伪代码对算法实现核心进行描述:

创建k个点作为起始质心(经常是随机选择)
当任意一个点的簇分配结果发生改变时对数据集中的每个数据点对每个质心计算质心与数据点之间的距离将数据点分配到距其最近的簇对每一个簇,计算簇中所有点的均值并将均值作为质心

以下给出封装后的KMEANS算法类中常用的几个变量与函数的解释。

template<typename T>//cluster centersvector< vector<T> > centroids;//mark which cluster the data belong tovector<tNode>  clusterAssment;//construct functionKMEANS::KMEANS(void);//load data into dataSetvoid KMEANS::loadData(vector< vector<T> > data);//running the kmeans algorithmvoid KMEANS::kmeans(int clusterCount);

成员变量 centroids 为聚类中心,规模为 k * dimensioncentroids[m] 代表第m类对象点的聚类中心。
成员变量 clusterAssment 则记录了各个目标点所属类。 clusterAssment[m].minIndex 为第m目标点所属类别, clusterAssment[m].minDist 为第m目标点与其所属类中心的距离。

KMEANS 类是k-means聚类算法本身,构造函数中不需要给定任何参数。
loadData 函数将待聚类数据 data 送入算法类中。其中需要说明: datavector< vector < T > > 型数据。类似于matlab-kmeans函数, data[m] 为一个 vector< T > 型数据,代表着一个目标点的坐标。可以认为,data为 particalCount * dimension 的矩阵。
kmeans 函数则是运行kmeans聚类算法,运行结果存入 centroidsclusterAssment 中。

KMEANS模块的流如下图所示:

loadData
kmeas
data
KMEANS类对象
clusterAssment
centroids

对应的函数调用:

KMEANS<double> YKmeans;
YKmeans.loadData(data);
YKmeans.kmeans();
//use YKmeans.Assment and YKmeans.centroids
以下附上KMEANS类的头文件与对应的例程代码。

测试范例:main.cpp

#include "YKmeans.h"
#include <vector>
#include <iostream>
#include <math.h>
using namespace std;const int pointsCount = 9;
const int clusterCount = 2;int main()
{//构造待聚类数据集vector< vector<double> > data;vector<double> points[pointsCount];for (int i = 0; i < pointsCount; i++){points[i].push_back(i);points[i].push_back(i*i);points[i].push_back(sqrt(i));data.push_back(points[i]);}//构建聚类算法KMEANS<double> kmeans;//数据加载入算法kmeans.loadData(data);//运行k均值聚类算法kmeans.kmeans(clusterCount);//输出聚类后各点所属类情况for (int i = 0; i < pointsCount; i++)cout << kmeans.clusterAssment[i].minIndex << endl;//输出类中心cout << endl << endl;for (int i = 0; i < clusterCount; i++){for (int j = 0; j < 3; j++)cout << kmeans.centroids[i][j] << ',' << '\t' ;cout << endl;}return(0);
}

KMEANS类对应的头文件:YKmeans.h

#ifndef _YKMEANS_H_
#define _YKMEANS_H_#include <cstdlib> //for rand()
#include <vector> //for vector<>
#include <time.h> //for srand
#include <limits.h>   //for INT_MIN INT_MAX
using namespace std;template<typename T>
class KMEANS
{protected://colLen:the dimension of vector;rowLen:the number of vectorsint colLen, rowLen;//count to be clusteredint k;//mark the min and max value of a arraytypedef struct MinMax{T Min;T Max;MinMax(T min, T max) :Min(min), Max(max) {}}tMinMax;//distance function//reload this function if necessarydouble (*distEclud)(vector<T> &v1, vector<T> &v2);//get the min and max value in idx-dimension of dataSettMinMax getMinMax(int idx){T min, max;dataSet[0].at(idx) > dataSet[1].at(idx) ? (max = dataSet[0].at(idx), min = dataSet[1].at(idx)) : (max = dataSet[1].at(idx), min = dataSet[0].at(idx));for (int i = 2; i < rowLen; i++){if (dataSet[i].at(idx) < min)   min = dataSet[i].at(idx);else if (dataSet[i].at(idx) > max) max = dataSet[i].at(idx);else continue;}tMinMax tminmax(min, max);return tminmax;}//generate clusterCount centers randomlyvoid randCent(int clusterCount){this->k = clusterCount;//init centroidscentroids.clear();vector<T> vec(colLen, 0);for (int i = 0; i < k; i++)centroids.push_back(vec);//set values by columnsrand(time(NULL));for (int j = 0; j < colLen; j++){tMinMax tminmax = getMinMax(j);T rangeIdx = tminmax.Max - tminmax.Min;for (int i = 0; i < k; i++){/* generate float data between 0 and 1 */centroids[i].at(j) = tminmax.Min + rangeIdx * (rand() / (double)RAND_MAX);}}}//default distance function ,defined as dis = (x-y)'*(x-y)static double defaultDistEclud(vector<T> &v1, vector<T> &v2){double sum = 0;int size = v1.size();for (int i = 0; i < size; i++){sum += (v1[i] - v2[i])*(v1[i] - v2[i]);}return sum;}public:typedef struct Node{int minIndex; //the index of each nodedouble minDist;Node(int idx, double dist) :minIndex(idx), minDist(dist) {}}tNode;KMEANS(void) { k = 0; colLen = 0; rowLen = 0; distEclud = defaultDistEclud;}~KMEANS(void){}//data to be clusteredvector< vector<T> > dataSet;//cluster centersvector< vector<T> > centroids;//mark which cluster the data belong tovector<tNode>  clusterAssment;//load data into dataSetvoid loadData(vector< vector<T> > data){this->dataSet = data; //kmeans do not change the original data;this->rowLen = data.capacity();this->colLen = data.at(0).capacity();}//running the kmeans algorithmvoid kmeans(int clusterCount){this->k = clusterCount;//initial clusterAssmentthis->clusterAssment.clear();tNode node(-1, -1);for (int i = 0; i < rowLen; i++)clusterAssment.push_back(node);//initial cluster centerthis->randCent(clusterCount);bool clusterChanged = true;//the termination condition can also be the loops less than some number such as 1000while (clusterChanged){clusterChanged = false;for (int i = 0; i < rowLen; i++){int minIndex = -1;double minDist = INT_MAX;for (int j = 0; j < k; j++){double distJI = distEclud(centroids[j], dataSet[i]);if (distJI < minDist){minDist = distJI;minIndex = j;}}if (clusterAssment[i].minIndex != minIndex){clusterChanged = true;clusterAssment[i].minIndex = minIndex;clusterAssment[i].minDist = minDist;}}//step two : update the centroidsfor (int cent = 0; cent < k; cent++){vector<T> vec(colLen, 0);int cnt = 0;for (int i = 0; i < rowLen; i++){if (clusterAssment[i].minIndex == cent){++cnt;//sum of two vectorsfor (int j = 0; j < colLen; j++){vec[j] += dataSet[i].at(j);}}}//mean of the vector and update the centroids[cent]for (int i = 0; i < colLen; i++){if (cnt != 0) vec[i] /= cnt;centroids[cent].at(i) = vec[i];}}}}
};#endif // _YKMEANS_H_

后注

在本文给出的KMEANS类中给出了一个默认的距离函数:
defaultDis(x⃗,y⃗)=(x⃗−y⃗)∙(x⃗−y⃗)=∣∣x⃗−y⃗∣∣2defaultDis(\vec x,\vec y) = (\vec x-\vec y) \bullet (\vec x-\vec y) = ||\vec x-\vec y||^2 defaultDis(x,y​)=(x−y​)∙(x−y​)=∣∣x−y​∣∣2
需要注意,定义在空间中的距离函数不一定是经典的二范数的形式。距离函数可以由用户给定,本文附上的KMEANS类代码也留出了用户指定距离函数的接口。用户只需要重载KMEANS类,将用户指定的距离函数送入 KMEANS::distEclud 距离函数指针即可。

代码部分参考来源:
忆之独秀的CSDN博客

K-均值聚类算法(k-means)的C++实现相关推荐

  1. k均值聚类算法(K Means)及其实战案例

    算法说明 K均值聚类算法其实就是根据距离来看属性,近朱者赤近墨者黑.其中K表示要聚类的数量,就是说样本要被划分成几个类别.而均值则是因为需要求得每个类别的中心点,比如一维样本的中心点一般就是求这些样本 ...

  2. K-Means(K均值聚类算法)

    K-Means(K均值聚类算法) 1.前言 要学习聚类算法就要知道聚类学习算法是什么,为什么要学习聚类学习聚类学习算法,有什么用途,下面就简单的做一下介绍,并且详细的说明k-means均值聚类学习算法 ...

  3. k均值聚类算法python_K均值和其他聚类算法:Python快速入门

    k均值聚类算法python This post was originally published here 这篇文章最初发表在这里 Clustering is the grouping of obje ...

  4. 【模式识别】K均值聚类算法应用实验报告及MATLAB仿真

    一. 实验目的 1.掌握K均值聚类算法的原理和实现过程: 2.掌握K均值聚类算法的应用方法. 二. 实验内容 1.彩色图像分割 选择一幅图像,分别按三种颜色数进行彩色图像分割的结果(原图和分割图).步 ...

  5. k均值聚类算法优缺点_Grasshopper实现K均值聚类算法

    本文很长很长,有很多很多图,包含以下部分: 1.算法简介 2.如何分类平面点 3.如何分类空间点 4.如何分类多维数据 5.后记 提醒:以下内容包括:智障操作,无中生友,重复造轮子 等 1.算法简介 ...

  6. K均值聚类算法(HCM,K-Means)

    K均值聚类核心思想如下: 算法把n个向量分为个组,并求每组的聚类中心,使得非相似性(或距离)指标的价值函数(或目标函数)达到最小.当选择欧几里德距离为组j中向量与相应聚类中心间的非相似性指标时,价值函 ...

  7. K均值聚类算法(Kmeans)讲解及源码实现

    K均值聚类算法(Kmeans)讲解及源码实现 算法核心 K均值聚类的核心目标是将给定的数据集划分成K个簇,并给出每个数据对应的簇中心点.算法的具体步骤描述如下. 数据预处理,如归一化.离群点处理等. ...

  8. python(scikit-learn)实现k均值聚类算法

    k均值聚类算法原理详解 示例为链接中的例题 直接调用python机器学习的库scikit-learn中k均值算法的相关方法 from sklearn.cluster import KMeans imp ...

  9. 机器学习之无监督学习-K均值聚类算法

    机器学习之无监督学习-K均值聚类算法 对于无监督学习,有两类重要的应用,一个是聚类,一个是降维.我们今天主要学习聚类中的K均值聚类. 我们先看看下图,图a为原始的数据点,我们想要对图a的数据点进行分类 ...

  10. Thinking in SQL系列之五:数据挖掘K均值聚类算法与城市分级

    原创: 牛超   2017-02-21   Mail:10867910@qq.com 引言:前一篇文章开始不再介绍简单算法,而是转到数据挖掘之旅.感谢CSDN将我前一篇机器学习C4.5决策树算法的博文 ...

最新文章

  1. Go 面向对象编程应用
  2. 新手入门 | 上链第一步,如何完成XuperChain环境、服务部署
  3. ffmpeg的内部Video Buffer管理和传送机制
  4. 1110: 最近共同祖先(函数专题)
  5. quartz中的corn表达式(转)
  6. Flash与组件:制作Slider组件
  7. 算法——分而治之及快速排序
  8. shell :将标准输出及标准错误输出写到指定文件
  9. 云服务器obs_华为云服务器ECS挂载对象存储服务OBS教程
  10. Lumion文件结构及Benchmark in progrees问题
  11. 入门SVN基础使用教程
  12. Could not create connection to database server. Attempted reconnect 3 times. Giving up.
  13. SystemUI之通知图标控制
  14. 《LeetCode刷题》954. 二倍数对数组(java篇)
  15. 如何回答「为什么想做产品经理」【面试核心问题3】
  16. 失落的帝国:盛大业务大收缩
  17. 移动端手机网页适配iPad与折叠屏设备
  18. 使用sil9233a芯片控制海思hi3531d的hdmi输入
  19. 2022届应届生办理落户上海流程是怎样的?
  20. 电池监控芯片bq34z100-g1调试

热门文章

  1. linux编写php,Linux 下编写一个 PHP 扩展
  2. 2023款 联想小新 Pro 14 和 Thinkbook 14 +区别对比评测
  3. Vue学习笔记01——Vue开发基础
  4. kad连接不上解决方法
  5. 头条号自媒体素材在哪找?下载素材可以参考这个方法
  6. 飛飛(五)就拿胖子说事:成年男性的标准体重公式为:标准体重(kg)=身高(cm)-100,超标准20%为超重,比标准体重轻20%为超清。
  7. 奋怒的小鸟 愤怒的小鸟 PC版
  8. 微信小程序中全局变量userInfo在其它页面不可用(wx.getUserInfo为异步获取信息)
  9. 【1207】正三角形的外接圆面积
  10. 央视开年专访 | 中国移动董事长杨杰:加大5G投资,今年实现县级以上城区覆盖...