数据挖掘算法:k-means算法的C++实现
(期末考试要到了,所以比较粗糙,请各位读者理解。。)
一、 概念
k-means是基于原型的、划分的聚类技术。它试图发现用户指定个数(K)的簇(由质心代表)。K-means算法接受输入量K,然后将N个数据对象划分为K个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得的均值所获得一个“中心对象”(引力中心)来进行计算的。
二、 伪代码
1 选择K个点作为初始质心。
2 Repeat
3 将每个点指派到最近的质心,形成K个簇。
4 重新计算每个簇的质心。
5 Until 质心不发生变化。
三、 重要数据结构
1 定义簇数、点数和维数
#define K 3 // K为簇数
#define N 50 // N为点数
#define D 2 // D为维数
2 各类数组
double point[N][D]; // N个D维点
double barycenter_initial[K][D]; // K个D维初始质心位置
double barycenter_before[K][D]; // 记录每次变换前质心的位置
double barycenter_finished[K][D]; // 最终得到的质心位置
double O_Distance[K]; // 记录某点对于各质心的欧几里得距离
int belongWhichBC[N]; // 记录每个点属于哪一个簇
double mid[D]; // 记录中间值
3 随机生成数据点
// 初始化数据点(坐标值均在0-100之间)
void CoordinateDistribution(int n, int d) {
srand((unsigned)time(NULL)); // 保证随机性
for(int i=0; i<n; i++) {
for(int j=0; j<d; j++) {
point[i][j] = rand() % 101;
}
}
}
四、 源代码
// k-means算法的C++实现
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <fstream>
using namespace std;
#define K 3 // K为簇数
#define N 20 // N为点数
#define D 2 // D为维数
double point[N][D]; // N个D维点
double barycenter_initial[K][D]; // K个D维初始质心位置
double barycenter_before[K][D]; // 记录每次变换前质心的位置
double barycenter_finished[K][D]; // 最终得到的质心位置
double O_Distance[K]; // 记录某点对于各质心的欧几里得距离
int belongWhichBC[N]; // 记录每个点属于哪一个簇
double mid[D]; // 记录中间值
// 初始化数据点(坐标值均在0-100之间)
void CoordinateDistribution(int n, int d) {
srand((unsigned)time(NULL)); // 保证随机性
for(int i=0; i<n; i++) {
for(int j=0; j<d; j++) {
point[i][j] = rand() % 101;
}
}
}
// 初始化质心(坐标值均在0-100之间)
void initBarycenter(int k, int d) {
for(int i=0; i<k; i++) {
for(int j=0; j<d; j++) {
barycenter_initial[i][j] = rand() % 101;
}
}
}
int main(int argc, char** argv) {
// 为N个点随机分配D维坐标
int n = N, d = D;
CoordinateDistribution(n, d);
// 首先输出K, N, D的值
cout<<"簇数 K = "<<K<<endl<<"点数 N = "<<N<<endl<<"维数 D = "<<D<<endl<<endl;
// 输出N个坐标点
cout<<"系统生成的N个点如下:"<<endl;
for(int i=0; i<n; i++) {
cout<<"第"<<i+1<<"个"<<"\t";
for(int j=0; j<d; j++) {
cout<<point[i][j]<<"\t";
}
cout<<endl;
}
cout<<endl;
// 选择K个初始质心
int k = K;
initBarycenter(k, d);
// 输出系统生成的初始质心
cout<<"系统生成的K个初始质心如下:" <<endl;
for(int i=0; i<k; i++) {
cout<<"第"<<i+1<<"个"<<"\t";
for(int j=0; j<d; j++) {
cout<<barycenter_initial[i][j]<<"\t";
}
cout<<endl;
}
cout<<endl;
// 将“首次变换前质点”的位置,初始化为initial时的位置
// 将“最终得到的质点”的位置均初始化为(-1, -1),使其与首次变换前的位置不相同
for(int i=0; i<k; i++) {
for(int j=0; j<d; j++) {
barycenter_before[i][j] = barycenter_initial[i][j];
barycenter_finished[i][j] = -1;
}
}
int times = 0; // 定义循环进行到第几次
// 循环计算
while(true) {
for(int i=0; i<n; i++) { // 对于每一个点
for(int j=0; j<k; j++) { // 求对于K个簇,每个簇的欧氏距离
double sum = 0;
for(int x=0; x<d; x++) {
sum = sum + pow(point[i][x]-barycenter_before[j][x], 2);
}
// O_Distance[j] = sqrt(sum); // 因为sum和sqrt(sum)是正相关,所以要比较sqrt(sum)的大小,只需比较sum的大小
O_Distance[j] = sum;
}
int x = 0, temp = x; // temp里面保存的是:某点所对应的欧氏距离最小的簇序号
while(x<k) {
if(O_Distance[x] < O_Distance[temp]) {
temp = x;
x++;
}
else {
x++;
}
}
belongWhichBC[i] = temp;
}
for(int j=0; j<k; j++) {
// 将a[]内全部元素置0
for(int i=0; i<d; i++) {
mid[i] = 0;
}
int number = 0; // 计算某簇中共有多少个点
for(int i=0; i<n; i++) {
if(belongWhichBC[i] == j) { // 某点所述簇的序号匹配
number++;
for(int y=0; y<d; y++) {
mid[y] = mid[y] + point[i][y];
}
}
}
for(int y=0; y<d; y++) {
barycenter_finished[j][y] = mid[y] / number;
}
}
// flag=0,表示barycenter_before与barycenter_finished内元素完全一致,退出循环
// flag=1,表示二者内元素不完全一致,仍需继续循环
int flag = 0;
for(int i=0; i<k; i++) {
for(int j=0; j<d; j++) {
if(barycenter_before[i][j] - barycenter_finished[i][j] <= 0.0001) {
flag = 0;
continue;
}
else {
flag = 1;
break;
}
}
if(flag == 0) {
continue;
}
else {
break;
}
}
if(flag == 0) {
times++;
cout<<"第"<<times<<"轮循环后,得到的K个质心如下:"<<endl;
for(int m=0; m<k; m++) {
cout<<"第"<<m+1<<"个"<<"\t";
for(int n=0; n<d; n++) {
cout<<barycenter_finished[m][n]<<"\t";
}
cout<<endl;
}
break;
}
else {
times++;
cout<<"第"<<times<<"轮循环后,得到的K个质心如下:"<<endl;
for(int m=0; m<k; m++) {
cout<<"第"<<m+1<<"个"<<"\t";
for(int n=0; n<d; n++) {
cout<<barycenter_finished[m][n]<<"\t";
}
cout<<endl;
}
// 若要继续循环,则应该把barycenter_finished中的元素作为下一个循环中barycenter_before中的元素
for(int i=0; i<k; i++) {
for(int j=0; j<d; j++) {
barycenter_before[i][j] = barycenter_finished[i][j];
}
}
continue;
}
}
cout<<endl;
// 输出最终质心位置
cout<<"经过 k-means 算法,得到各簇的质心如下:"<<endl;
for(int i=0; i<k; i++) {
cout<<"第"<<i+1<<"个"<<"\t";
for(int j=0; j<d; j++) {
cout<<barycenter_finished[i][j]<<"\t";
}
cout<<endl;
cout<<"该簇所包含的点有:"<<endl;
for(int j=0; j<N; j++) {
if(belongWhichBC[j] == i) {
cout<<j+1<<"\t";
}
}
cout<<endl;
}
return 0;
}
五、 运行结果
注:K=3,N=20,D=2

图1 k-means算法运行结果-1

图2 k-means算法运行结果-2

图3 利用Graph作图展示k-means算法运行结果
数据挖掘算法:k-means算法的C++实现的更多相关文章
- 第4章 最基础的分类算法-k近邻算法
思想极度简单 应用数学知识少 效果好(缺点?) 可以解释机器学习算法使用过程中的很多细节问题 更完整的刻画机器学习应用的流程 distances = [] for x_train in X_train ...
- 聚类算法:K-means 算法(k均值算法)
k-means算法: 第一步:选$K$个初始聚类中心,$z_1(1),z_2(1),\cdots,z_k(1)$,其中括号内的序号为寻找聚类中心的迭代运算的次序号. 聚类中心的向量值可任意设 ...
- 分类算法----k近邻算法
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一.该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的 ...
- 机器学习(四) 机器学习(四) 分类算法--K近邻算法 KNN (下)
六.网格搜索与 K 邻近算法中更多的超参数 七.数据归一化 Feature Scaling 解决方案:将所有的数据映射到同一尺度 八.scikit-learn 中的 Scaler preprocess ...
- 机器学习(四) 分类算法--K近邻算法 KNN (上)
一.K近邻算法基础 KNN------- K近邻算法--------K-Nearest Neighbors 思想极度简单 应用数学知识少 (近乎为零) 效果好(缺点?) 可以解释机器学习算法使用过程中 ...
- python 机器学习(二)分类算法-k近邻算法
一.什么是K近邻算法? 定义: 如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别. 来源: KNN算法最早是由Cover和Hart提 ...
- KNN 与 K - Means 算法比较
KNN K-Means 1.分类算法 聚类算法 2.监督学习 非监督学习 3.数据类型:喂给它的数据集是带label的数据,已经是完全正确的数据 喂给它的数据集是无label的数据,是杂乱无章的,经过 ...
- 分类算法——k最近邻算法(Python实现)(文末附工程源代码)
kNN算法原理 k最近邻(k-Nearest Neighbor)算法是比较简单的机器学习算法.它采用测量不同特征值之间的距离方法进行分类,思想很简单:如果一个样本在特征空间中的k个最近邻(最相似)的样 ...
- 【学习笔记】分类算法-k近邻算法
k-近邻算法采用测量不同特征值之间的距离来进行分类. 优点:精度高.对异常值不敏感.无数据输入假定 缺点:计算复杂度高.空间复杂度高 使用数据范围:数值型和标称型 用例子来理解k-近邻算法 电影可以按 ...
- 【机器学习】聚类算法——K均值算法(k-means)
一.聚类 1.基于划分的聚类:k-means.k-medoids(每个类别找一个样本来代表).Clarans 2.基于层次的聚类:(1)自底向上的凝聚方法,比如Agnes (2)自上而下的分裂方法,比 ...
随机推荐
- redis介绍及在购物车项目中的应用,用户认证
1.redis 2.购物车的构建 api结构: models.py(创建完后自行添加数据) from django.db import models from django.contrib.conte ...
- zTree的核心处理逻辑
zTree 是一个前端树形结构的插件. 使用起来很简单,我们重点关注一下插件的核心代码. 首先,zTree需要如下的数据结构: let areaData = [ { "id": & ...
- Linux mongodb安装、启动、运行
1.下载 wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.2.4.tgz 2.安装 tar -zxvf ...
- ABAP术语-Data Browser
Data Browser 原文:http://www.cnblogs.com/qiangsheng/archive/2008/01/21/1046858.html Tool for displayin ...
- mysql创建表时反引号的作用
试用navicat工具查看现网mysql建表语句时,发现表名和字段名都是反引号引起来的 CREATE TABLE `tab_notice_title_tv` ( `i_id` ) NOT NULL A ...
- jquery获取周对应的日期
项目中用到按周显示的功能,找了一个,然后自己修改了一下,留着以后用: 这是代码,要是直接显示的话就把第43行去掉就行了,如果想要得到数据按照自己的想法重新渲染就保留43行,直接看51行,52行就是你要 ...
- php-7.2.3源代码和php-5.6.26源代码摘录,对比 “汇编php文件”和“执行opcode代码”
php-7.2.3 在“汇编php文件”和“执行opcode代码”上做了大量改变php-5.6.26 没见到支持抽象语法树的相关代码,php-7.2.3 见到支持抽象语法树的相关代码php-5.6.2 ...
- 微信小程序缓存
购物车数据加入缓存,相同的商品值修改数量,然后再次加入缓存中 修改购物车的数据的时候同理,都是修改缓存数据然后加入到缓存中. 具体的使用方法看官方文档,我只是提供思路
- doc命令操作数据库(下)
1.给数据表添加一组数据: 2.给数据表添加多组数据: 3.对数据进行删除和修改: 4.用select查询单个或多个数据信息: 5.去除重复值: 6.查询的各种用法: between的用法: 查询排序 ...
- x-pack本地安装方式
一.首先下载本地安装包,我使用的ELK是5.6.1版本: https://artifacts.elastic.co/downloads 二.进入到elasticsearch/bin(所有节点)和kib ...