数据挖掘算法:DBSCAN算法的C++实现
(期末考试快到了,所以比较粗糙,请各位读者理解。。)
一、 概念
DBSCAN是一种产生划分聚类的基于密度的聚类算法,簇的个数由算法自动地确定。低密度区域中的点被视为噪声而忽略,因此DBSCAN不产生完全聚类。
二、 伪代码
1 将所有点标记为核心点、边界点和噪声点。
2 删除噪声点。
3 为距离在Eps之内的所有核心点之间赋予一条边。
4 每组连通的核心点形成一个簇。
5 将每个边界点指派到一个与之关联的核心点的簇中。
三、 重要数据结构
1 定义邻域半径值、密度阈值、数据集点数
#define Eps 3 // Eps为邻域半径值
#define MinPts 3 // 邻域密度阈值
#define N 20 // 数据集包含N个对象
2 定义数组保存所有点
double point[N][2]; // 保存所有的数据点
3 定义vector保存核心点、边界点、噪声点的位置
vector<int> kernel_point; // 保存核心点在point[][]中的位置
vector<int> border_point; // 保存边界点在point[][]中的位置
vector<int> noise_point; // 保存噪声点在point[][]中的位置
4 定义vector保存最终形成的簇
vector<vector<int> > cluster; // 保存最终形成的簇,每个簇中包含点在point[][]中的位置
四、 源代码
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <cmath>
using namespace std;
#define Eps 3 // Eps为邻域半径值
#define MinPts 3 // 邻域密度阈值
#define N 20 // 数据集包含N个对象
double point[N][2]; // 保存所有的数据点
vector<int> kernel_point; // 保存核心点在point[][]中的位置
vector<int> border_point; // 保存边界点在point[][]中的位置
vector<int> noise_point; // 保存噪声点在point[][]中的位置
vector<vector<int> > mid; // 可能存在重叠的簇
vector<vector<int> > cluster; // 保存最终形成的簇,每个簇中包含点在point[][]中的位置
// 初始化N个坐标点
void init(int n) {
srand((unsigned)time(NULL));
for(int i=0; i<n; i++) {
for(int j=0; j<2; j++) {
point[i][j] = rand() % (N+1);
}
}
}
int main(int argc, char** argv) {
// 初始化数据集
int n = N;
init(n);
// 将所有点标记为核心点、边界点或噪声点
// 标记核心点
for(int i=0; i<N; i++) {
int num = 0; // 判断是否超过MinPts,若一次循环后num>=MinPts,则加入核心点
for(int j=0; j<N; j++) {
if(pow(point[i][0]-point[j][0], 2)+pow(point[i][1]-point[j][1], 2)<=pow(Eps, 2)) { // 本身也算一个
num++;
}
}
if(num>=MinPts) {
kernel_point.push_back(i);
}
}
// 标记为边界点或噪声点
for(int i=0; i<N; i++) {
// 边界点或噪声点不能是核心点
int flag = 0; // 若flag=0,则该点不是核心点,若flag=1,则该点为核心点
for(int j=0; j<kernel_point.size(); j++) {
if(i == kernel_point[j]) {
flag = 1;
break;
}
}
if(flag == 0) {
// 判断是边界点还是噪声点
int flag2 = 0; // 若flag=0,则该点为边界点,若flag=1,则该点位噪声点
for(int j=0; j<kernel_point.size(); j++) {
int s = kernel_point[j]; // 标记第j个核心点在point[][]中的位置,方便调用
if(pow(point[i][0]-point[s][0], 2)+pow(point[i][1]-point[s][1], 2)<pow(Eps, 2)) {
flag2 = 0;
border_point.push_back(i);
break;
}
else {
flag2 = 1;
continue;
}
}
if(flag2 == 1) {
// 加入噪声点
noise_point.push_back(i);
continue;
}
}
else {
continue;
}
}
// 将距离在Eps之内的核心点放在一个vector中
for(int i=0; i<kernel_point.size(); i++) {
int x = kernel_point[i];
vector<int> record; // 对于每一个点建立一个record,放入mid当中
record.push_back(x);
for(int j=i+1; j<kernel_point.size(); j++) {
int y = kernel_point[j];
if(pow(point[x][0]-point[y][0], 2)-pow(point[x][1]-point[y][1], 2)<pow(Eps, 2)) {
record.push_back(y);
}
}
mid.push_back(record);
}
// 合并vector
for(int i=0; i<mid.size(); i++) { // 对于mid中的每一行
// 判断该行是否已经添加进前面的某一行中
if(mid[i][0] == -1) {
continue;
}
// 如果没有被判断过
for(int j=0; j<mid[i].size(); j++) { // 判断其中的每一个值
// 对每一个值判断其他行中是否存在
for(int x=i+1; x<mid.size(); x++) { // 对于之后的每一行
if(mid[x][0] == -1) {
continue;
}
for(int y=0; y<mid[x].size(); y++) {
if(mid[i][j] == mid[x][y]) {
// 如果有一样的元素,应该放入一个vector中,并在循环后加入precluster,同时置该vector内所有元素值为-1
for(int a=0; a<mid[x].size(); a++) {
mid[i].push_back(mid[x][a]);
mid[x][a] = -1;
}
break;
}
}
}
}
cluster.push_back(mid[i]);
}
// 删除cluster中的重复元素
for(int i=0; i<cluster.size(); i++) { // 对于每一行
for(int j=0; j<cluster[i].size(); j++) {
for(int n=j+1; n<cluster[i].size(); n++) {
if(cluster[i][j] == cluster[i][n]) {
cluster[i].erase(cluster[i].begin()+n);
n--;
}
}
}
}
// 至此,cluster中保存了各个簇,每个簇中有点对应在point[][]中的位置
// 将每个边界点指派到一个与之相关联的核心点的簇中
for(int i=0; i<border_point.size(); i++) { // 对于每一个边界点
int x = border_point[i];
for(int j=0; j<cluster.size(); j++) { // 检查每一个簇,判断边界点与哪个簇中的核心点关联,将边界点加入到第一个核心点出现的簇中
int flag = 0; // flag=0表示没有匹配的项,flag=1表示已经匹配,退出循环
for(int k=0; k<cluster[j].size(); k++) {
int y = cluster[j][k];
if(pow(point[x][0]-point[y][0], 2)+pow(point[x][1]-point[y][1], 2)<pow(Eps, 2)) {
cluster[j].push_back(x);
flag = 1;
break;
}
}
if(flag == 1) {
break;
}
}
}
/*******************************************************************************************/
cout<<"All Points : "<<endl;
for(int i=0; i<N; i++) {
cout<<"第"<<i<<"个"<<"\t";
for(int j=0; j<2; j++) {
cout<<point[i][j]<<"\t";
}
cout<<endl;
}
cout<<endl;
cout<<"Kernel Points : "<<endl;
for(int i=0; i<kernel_point.size(); i++) {
cout<<kernel_point[i]<<"\t";
}
cout<<endl<<endl;
cout<<"Border Points : "<<endl;
for(int i=0; i<border_point.size(); i++) {
cout<<border_point[i]<<"\t";
}
cout<<endl<<endl;
cout<<"Noise Points : "<<endl;
for(int i=0; i<noise_point.size(); i++) {
cout<<noise_point[i]<<"\t";
}
cout<<endl<<endl;
cout<<"Cluster : "<<endl;
for(int i=0; i<cluster.size(); i++) {
cout<<"第"<<i<<"个"<<"\t";
for(int j=0; j<cluster[i].size(); j++) {
cout<<cluster[i][j]<<"\t";
}
cout<<endl;
}
return 0;
}
五、 运行结果

图1 DBSCAN算法的运行结果

图2 利用Graph作图展示DBSCAN算法运行结果
(其中,粉色点为噪声点,蓝色与黄色为两个簇)
数据挖掘算法:DBSCAN算法的C++实现的更多相关文章
- 聚类算法——DBSCAN算法原理及公式
聚类的定义 聚类就是对大量未知标注的数据集,按数据的内在相似性将数据集划分为多个类别,使类别内的数据相似度较大而类别间的数据相似度较小.聚类算法是无监督的算法. 常见的相似度计算方法 闵可夫斯基距离M ...
- 挑子学习笔记:DBSCAN算法的python实现
转载请标明出处:https://www.cnblogs.com/tiaozistudy/p/dbscan_algorithm.html DBSCAN(Density-Based Spatial Clu ...
- Python机器学习笔记:K-Means算法,DBSCAN算法
K-Means算法 K-Means 算法是无监督的聚类算法,它实现起来比较简单,聚类效果也不错,因此应用很广泛.K-Means 算法有大量的变体,本文就从最传统的K-Means算法学起,在其基础上学习 ...
- 【转】常用聚类算法(一) DBSCAN算法
原文链接:http://www.cnblogs.com/chaosimple/p/3164775.html#undefined 1.DBSCAN简介 DBSCAN(Density-Based Spat ...
- 常用聚类算法(一) DBSCAN算法
1.DBSCAN简介 DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度 ...
- 基于密度的聚类之Dbscan算法
一.算法概述 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法.与划分和层次 ...
- DBSCAN算法
简单的说就是根据一个根据对象的密度不断扩展的过程的算法.一个对象O的密度可以用靠近O的对象数来判断.学习DBSCAN算法,需要弄清楚几个概念: 一:基本概念 1.:对象O的是与O为中心,为半径的空间, ...
- 数据挖掘10大算法(1)——PageRank
1. 前言 这系列的文章主要讲述2006年评出的数据挖掘10大算法(见图1).文章的重点将偏向于算法的来源以及算法的主要思想,不涉及具体的实现.如果发现文中有错,希望各位指出来,一起讨论. 图1 来自 ...
- 【原创】大叔算法分享(5)聚类算法DBSCAN
一 简介 DBSCAN:Density-based spatial clustering of applications with noise is a data clustering algorit ...
随机推荐
- Oracle单行函数用法
单行函数分为五种类型:字符函数.数值函数.日期函数.转换函数.通用函数. 1.字符函数: 对于输入的字符转换为需要转为的字符或数值. upper()大写 --小写字母转为大写字母 --对于表指定的字符 ...
- React Native 中组件的生命周期(转)
概述 就像 Android 开发中的 View 一样,React Native(RN) 中的组件也有生命周期(Lifecycle).所谓生命周期,就是一个对象从开始生成到最后消亡所经历的状态,理解生命 ...
- iOS之UIImagePickerController显示中文界面
iOS开发中,我们经常遇到获取拍照.相册中图片的功能,就必然少不了UIImagePickerController,但是我们发现当我们使用它的时候,它的页面是英文的,看着很别扭,国人还是比较喜欢看中文界 ...
- python字符串常用内建函数总结
自己总结一些常用字符串函数,理解比较粗糙 1.字符串内建函数-大小写转换函数 (1)str.capitalize Help on method_descriptor: capitalize(...) ...
- Spring Boot多环境配置
在项目开发阶段,开发环境和实际生产环境是不一样,比如使用的数据库/服务连接配置等.因此,配置多个开发环境profile还是必要的 多环境的配置(yml)方式 配置其实很简单,在resource目录下, ...
- Linux入门-第六周
1.总结IP地址规划 IP地址的合理规划是网络设计中最重要的一环,在大型网络中必须对IP地址进行统一规划并得到实施.IP地址规划的好坏影响到网络路由协议算法的效率,影响到网络的性能,影响到网络的拓展, ...
- linux系统下病毒排除思路
1.top查看是否有特别吃cpu和内存的进程,病毒进程kill是杀不死的,因为ps命令被修改 2.ls -la /proc/病毒进程pid/ pwd为病毒进程程序目录 一般在/usr/bin下 3. ...
- jquery获取周对应的日期
项目中用到按周显示的功能,找了一个,然后自己修改了一下,留着以后用: 这是代码,要是直接显示的话就把第43行去掉就行了,如果想要得到数据按照自己的想法重新渲染就保留43行,直接看51行,52行就是你要 ...
- 【控制连接实现信息共享---linux和设备下ssh和远程连接telnet服务的简单搭建】
SSH的配置 空密码登陆ssh server 如果要登录ssh server通常要在server和client之间采取具有共同加密的秘钥,若每次当client想要了:连接ssh server时都要手工 ...
- QQ兴趣部落 大批量引流实战技巧
兴趣部落,犹如pc端贴吧,除去盔甲,几乎大同小异. 在文章<QQ运动,新楛的马桶还在香,营销人不应摒弃>中,阿力推推对稍微僻静的平台做过简述,和QQ运动一样,兴趣部落稍显“僻静”,执行到位 ...