手机
当前位置:查字典教程网 >编程开发 >C语言 >用C++实现DBSCAN聚类算法
用C++实现DBSCAN聚类算法
摘要:这几天由于工作需要,对DBSCAN聚类算法进行了C++的实现。时间复杂度O(n^2),主要花在算每个点领域内的点上。算法很简单,现共享大家参...

这几天由于工作需要,对DBSCAN聚类算法进行了C++的实现。时间复杂度O(n^2),主要花在算每个点领域内的点上。算法很简单,现共享大家参考,也希望有更多交流。

数据点类型描述如下:

复制代码 代码如下:

#include <vector>

using namespace std;

const int DIME_NUM=2; //数据维度为2,全局常量

//数据点类型

class DataPoint

{

private:

unsigned long dpID; //数据点ID

double dimension[DIME_NUM]; //维度数据

long clusterId; //所属聚类ID

bool isKey; //是否核心对象

bool visited; //是否已访问

vector<unsigned long> arrivalPoints; //领域数据点id列表

public:

DataPoint(); //默认构造函数

DataPoint(unsigned long dpID,double* dimension , bool isKey); //构造函数

unsigned long GetDpId(); //GetDpId方法

void SetDpId(unsigned long dpID); //SetDpId方法

double* GetDimension(); //GetDimension方法

void SetDimension(double* dimension); //SetDimension方法

bool IsKey(); //GetIsKey方法

void SetKey(bool isKey); //SetKey方法

bool isVisited(); //GetIsVisited方法

void SetVisited(bool visited); //SetIsVisited方法

long GetClusterId(); //GetClusterId方法

void SetClusterId(long classId); //SetClusterId方法

vector<unsigned long>& GetArrivalPoints(); //GetArrivalPoints方法

};

这是实现:

复制代码 代码如下:

#include "DataPoint.h"

//默认构造函数

DataPoint::DataPoint()

{

}

//构造函数

DataPoint::DataPoint(unsigned long dpID,double* dimension , bool isKey):isKey(isKey),dpID(dpID)

{

//传递每维的维度数据

for(int i=0; i<DIME_NUM;i++)

{

this->dimension[i]=dimension[i];

}

}

//设置维度数据

void DataPoint::SetDimension(double* dimension)

{

for(int i=0; i<DIME_NUM;i++)

{

this->dimension[i]=dimension[i];

}

}

//获取维度数据

double* DataPoint::GetDimension()

{

return this->dimension;

}

//获取是否为核心对象

bool DataPoint::IsKey()

{

return this->isKey;

}

//设置核心对象标志

void DataPoint::SetKey(bool isKey)

{

this->isKey = isKey;

}

//获取DpId方法

unsigned long DataPoint::GetDpId()

{

return this->dpID;

}

//设置DpId方法

void DataPoint::SetDpId(unsigned long dpID)

{

this->dpID = dpID;

}

//GetIsVisited方法

bool DataPoint::isVisited()

{

return this->visited;

}

//SetIsVisited方法

void DataPoint::SetVisited( bool visited )

{

this->visited = visited;

}

//GetClusterId方法

long DataPoint::GetClusterId()

{

return this->clusterId;

}

//GetClusterId方法

void DataPoint::SetClusterId( long clusterId )

{

this->clusterId = clusterId;

}

//GetArrivalPoints方法

vector<unsigned long>& DataPoint::GetArrivalPoints()

{

return arrivalPoints;

}

DBSCAN算法类型描述:

复制代码 代码如下:

#include <iostream>

#include <cmath>

using namespace std;

//聚类分析类型

class ClusterAnalysis

{

private:

vector<DataPoint> dadaSets; //数据集合

unsigned int dimNum; //维度

double radius; //半径

unsigned int dataNum; //数据数量

unsigned int minPTs; //邻域最小数据个数

double GetDistance(DataPoint& dp1, DataPoint& dp2); //距离函数

void SetArrivalPoints(DataPoint& dp); //设置数据点的领域点列表

void KeyPointCluster( unsigned long i, unsigned long clusterId ); //对数据点领域内的点执行聚类操作

public:

ClusterAnalysis(){} //默认构造函数

bool Init(char* fileName, double radius, int minPTs); //初始化操作

bool DoDBSCANRecursive(); //DBSCAN递归算法

bool WriteToFile(char* fileName); //将聚类结果写入文件

};

聚类实现:

复制代码 代码如下:

#include "ClusterAnalysis.h"

#include <fstream>

#include <iosfwd>

#include <math.h>

/*

函数:聚类初始化操作

说明:将数据文件名,半径,领域最小数据个数信息写入聚类算法类,读取文件,把数据信息读入写进算法类数据集合中

参数:

char* fileName; //文件名

double radius; //半径

int minPTs; //领域最小数据个数

返回值: true; */

bool ClusterAnalysis::Init(char* fileName, double radius, int minPTs)

{

this->radius = radius; //设置半径

this->minPTs = minPTs; //设置领域最小数据个数

this->dimNum = DIME_NUM; //设置数据维度

ifstream ifs(fileName); //打开文件

if (! ifs.is_open()) //若文件已经被打开,报错误信息

{

cout << "Error opening file"; //输出错误信息

exit (-1); //程序退出

}

unsigned long i=0; //数据个数统计

while (! ifs.eof() ) //从文件中读取POI信息,将POI信息写入POI列表中

{

DataPoint tempDP; //临时数据点对象

double tempDimData[DIME_NUM]; //临时数据点维度信息

for(int j=0; j<DIME_NUM; j++) //读文件,读取每一维数据

{

ifs>>tempDimData[j];

}

tempDP.SetDimension(tempDimData); //将维度信息存入数据点对象内

//char date[20]="";

//char time[20]="";

////double type; //无用信息

//ifs >> date;

//ifs >> time; //无用信息读入

tempDP.SetDpId(i); //将数据点对象ID设置为i

tempDP.SetVisited(false); //数据点对象isVisited设置为false

tempDP.SetClusterId(-1); //设置默认簇ID为-1

dadaSets.push_back(tempDP); //将对象压入数据集合容器

i++; //计数+1

}

ifs.close(); //关闭文件流

dataNum =i; //设置数据对象集合大小为i

for(unsigned long i=0; i<dataNum;i++)

{

SetArrivalPoints(dadaSets[i]); //计算数据点领域内对象

}

return true; //返回

}

/*

函数:将已经过聚类算法处理的数据集合写回文件

说明:将已经过聚类结果写回文件

参数:

char* fileName; //要写入的文件名

返回值: true */

bool ClusterAnalysis::WriteToFile(char* fileName )

{

ofstream of1(fileName); //初始化文件输出流

for(unsigned long i=0; i<dataNum;i++) //对处理过的每个数据点写入文件

{

for(int d=0; d<DIME_NUM ; d++) //将维度信息写入文件

of1<<dadaSets[i].GetDimension()[d]<<'t';

of1 << dadaSets[i].GetClusterId() <<endl; //将所属簇ID写入文件

}

of1.close(); //关闭输出文件流

return true; //返回

}

/*

函数:设置数据点的领域点列表

说明:设置数据点的领域点列表

参数:

返回值: true; */

void ClusterAnalysis::SetArrivalPoints(DataPoint& dp)

{

for(unsigned long i=0; i<dataNum; i++) //对每个数据点执行

{

double distance =GetDistance(dadaSets[i], dp); //获取与特定点之间的距离

if(distance <= radius && i!=dp.GetDpId()) //若距离小于半径,并且特定点的id与dp的id不同执行

dp.GetArrivalPoints().push_back(i); //将特定点id压力dp的领域列表中

}

if(dp.GetArrivalPoints().size() >= minPTs) //若dp领域内数据点数据量> minPTs执行

{

dp.SetKey(true); //将dp核心对象标志位设为true

return; //返回

}

dp.SetKey(false); //若非核心对象,则将dp核心对象标志位设为false

}

/*

函数:执行聚类操作

说明:执行聚类操作

参数:

返回值: true; */

bool ClusterAnalysis::DoDBSCANRecursive()

{

unsigned long clusterId=0; //聚类id计数,初始化为0

for(unsigned long i=0; i<dataNum;i++) //对每一个数据点执行

{

DataPoint& dp=dadaSets[i]; //取到第i个数据点对象

if(!dp.isVisited() && dp.IsKey()) //若对象没被访问过,并且是核心对象执行

{

dp.SetClusterId(clusterId); //设置该对象所属簇ID为clusterId

dp.SetVisited(true); //设置该对象已被访问过

KeyPointCluster(i,clusterId); //对该对象领域内点进行聚类

clusterId++; //clusterId自增1

}

//cout << "孤立点T" << i << endl;

}

cout <<"共聚类" <<clusterId<<"个"<< endl; //算法完成后,输出聚类个数

return true; //返回

}

/*

函数:对数据点领域内的点执行聚类操作

说明:采用递归的方法,深度优先聚类数据

参数:

unsigned long dpID; //数据点id

unsigned long clusterId; //数据点所属簇id

返回值: void; */

void ClusterAnalysis::KeyPointCluster(unsigned long dpID, unsigned long clusterId )

{

DataPoint& srcDp = dadaSets[dpID]; //获取数据点对象

if(!srcDp.IsKey()) return;

vector<unsigned long>& arrvalPoints = srcDp.GetArrivalPoints(); //获取对象领域内点ID列表

for(unsigned long i=0; i<arrvalPoints.size(); i++)

{

DataPoint& desDp = dadaSets[arrvalPoints[i]]; //获取领域内点数据点

if(!desDp.isVisited()) //若该对象没有被访问过执行

{

//cout << "数据点t"<< desDp.GetDpId()<<"聚类ID为t" <<clusterId << endl;

desDp.SetClusterId(clusterId); //设置该对象所属簇的ID为clusterId,即将该对象吸入簇中

desDp.SetVisited(true); //设置该对象已被访问

if(desDp.IsKey()) //若该对象是核心对象

{

KeyPointCluster(desDp.GetDpId(),clusterId); //递归地对该领域点数据的领域内的点执行聚类操作,采用深度优先方法

}

}

}

}

//两数据点之间距离

/*

函数:获取两数据点之间距离

说明:获取两数据点之间的欧式距离

参数:

DataPoint& dp1; //数据点1

DataPoint& dp2; //数据点2

返回值: double; //两点之间的距离 */

double ClusterAnalysis::GetDistance(DataPoint& dp1, DataPoint& dp2)

{

double distance =0; //初始化距离为0

for(int i=0; i<DIME_NUM;i++) //对数据每一维数据执行

{

distance += pow(dp1.GetDimension()[i] - dp2.GetDimension()[i],2); //距离+每一维差的平方

}

return pow(distance,0.5); //开方并返回距离

}

算法调用就简单了:

复制代码 代码如下:

#include "ClusterAnalysis.h"

#include <cstdio>

using namespace std;

int main()

{

ClusterAnalysis myClusterAnalysis; //聚类算法对象声明

myClusterAnalysis.Init("D:1108XY.txt",500,9); //算法初始化操作,指定半径为15,领域内最小数据点个数为3,(在程序中已指定数据维度为2)

myClusterAnalysis.DoDBSCANRecursive(); //执行聚类算法

myClusterAnalysis.WriteToFile("D:1108XYResult.txt");//写执行后的结果写入文件

system("pause"); //显示结果

return 0; //返回

}

【用C++实现DBSCAN聚类算法】相关文章:

分享C++面试中string类的一种正确写法

C++中事件机制的简洁实现及需要放弃的特性

自己实现strcpy函数的实现方法

c++二叉树的几种遍历算法

用C语言实现单链表的各种操作(二)

解决C++中重定义的方法总结

用C++实现,将一句话里的单词进行倒置的方法详解

使用map实现单词转换的实例分析

C/C++实现矩阵的转置(示例代码)

C++实现strcmp字符串比较的深入探讨

精品推荐
分类导航