數據挖掘演算法:DBSCAN演算法的C++實現

来源:http://www.cnblogs.com/zlian2016/archive/2016/06/26/5617527.html
-Advertisement-
Play Games

(期末考試快到了,所以比較粗糙,請各位讀者理解。。) 一、 概念 DBSCAN是一種產生劃分聚類的基於密度的聚類演算法,簇的個數由演算法自動地確定。低密度區域中的點被視為雜訊而忽略,因此DBSCAN不產生完全聚類。 二、 偽代碼 1 將所有點標記為核心點、邊界點和雜訊點。 2 刪除雜訊點。 3 為距離在 ...


(期末考試快到了,所以比較粗糙,請各位讀者理解。。)

一、    概念

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演算法運行結果

(其中,粉色點為雜訊點,藍色與黃色為兩個簇)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 列印基本類型 以下列印基本的數據類型, 如int, char, float等, 最後兩行是以八進位和十六進位列印數字10 windows gcc輸出: 設置輸出寬度 設置每個整數占10個位置, 預設為右對齊 如果數字的長度比設置的寬度大, 那麼會忽略我們設置的輸出寬度 windows gcc輸出: ...
  • 英文原文:Micro Benchmarking with JMH: Measure, don’t guess!翻譯地址:使用JMH進行微基準測試:不要猜,要測試!原文作者:Antonio翻譯作者:Hollis轉載請註明出處。 很多Java開發人員都知道把一個變數聲明為null有助於垃圾回收(譯者註:... ...
  • 今天在寫框架的時候想把SaeMySQL初始化之後作為全局變數使用。但是後來發現PHP中的全局變數和Java或者OC中的全局變數還是有較大區別的。下麵記錄一下php裡面的global的使用相關註意事項。1.有些場合需要全局變數的出現,如下例子: 上面的代碼的結果為:"myname is" 。而不是期望 ...
  • 【課前思考】 1. 什麼是對象?什麼是類?什麼是包?什麼是介面?什麼是內部類? 2. 面向對象編程的特性有哪三個?它們各自又有哪些特性? 3. 你知道java語言在面向對象編程方面有何獨特的特點嗎? 難點: 1. 理解方法重載和方法重寫,不要混淆了兩者的使用。 2. 類變數和類方法的使用。 3. 接 ...
  • 前言: 目前在研究易信公眾號,想給公眾號增加一個獲取個人交通違章的查詢菜單,通過點擊返回查詢數據。以下是實施過程。 一、首先,用火狐瀏覽器打開XX省交管網,分析頁面信息: 可以看到共有4種查詢種類,我只要查詢違章數據,所以分析第一個電子警察信息查詢就好了,用firebug分別查看車牌號碼、車輛識別碼 ...
  • 搭建輕量級Java Web框架快速搭建開發框架如何載入配置文件如何實現一個簡單的 IOC 容器如何載入指定的類如何初始化框架 *註解開發 目標:打造一個輕量級的 MVC 框架,Controller 是MVC的核心,類似於 SpringMVC。通過 Controller 註解來定義 Controlle ...
  • 這是我對指針學習的歸納記載,如有疑問歡迎聯繫作者本人一起探討~ 尊重作者的勞動,轉載請記得註明來源:http://www.cnblogs.com/weifeng727/p/5584151.html ...
  • 一、什麼是字典? 字典是Python語言中唯一的映射類型。 映射類型對象里哈希值(鍵,key)和指向的對象(值,value)是一對多的的關係,通常被認為是可變的哈希表。 字典對象是可變的,它是一個容器類型,能存儲任意個數的Python對象,其中也可包括其他容器類型。 字典類型與序列類型的區別: 1. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...