DBSCAN是一種基於密度的聚類演算法,它的基本原理就是給定兩個參數,ξ和minp,其中 ξ可以理解為半徑,演算法將在這個半徑內尋找樣本,minp是一個以ξ為半徑尋找到的樣本個數n的限制條件,只要n>=minp,尋找到的樣本點就是核心樣本點,演算法的具體描述見參考檔案1,下邊是這個演算法的java實現:
首先定義一個Point類,代表樣本點
package com.sunzhenxing;
public class Point {
private int x;
private int y;
private boolean isKey;
private boolean isClassed;
public boolean isKey() {
return isKey;
}
public void setKey(boolean isKey) {
this.isKey = isKey;
this.isClassed=true;
}
public boolean isClassed() {
return isClassed;
}
public void setClassed(boolean isClassed) {
this.isClassed = isClassed;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Point(){
x=0;
y=0;
}
public Point(int x,int y){
this.x=x;
this.y=y;
}
public Point(String str){
String[] p=str.split(",");
this.x=Integer.parseInt(p[0]);
this.y=Integer.parseInt(p[1]);
}
public String print(){
return "<"+this.x+","+this.y+">";
}
}
然後定義一個工具類,為演算法的實現服務:
package com.sunzhenxing;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class Utility {
/**
* 測試兩個點之間的距離
* @param p 點
* @param q 點
* @return 返回兩個點之間的距離
*/
public static double getDistance(Point p,Point q){
int dx=p.getX()-q.getX();
int dy=p.getY()-q.getY();
double distance=Math.sqrt(dx*dx+dy*dy);
return distance;
}
/**
* 檢查給定點是不是核心點
* @param lst 存放點的鏈表
* @param p 待測試的點
* @param e e半徑
* @param minp 密度閾值
* @return 暫時存放訪問過的點
*/
public static List<Point> isKeyPoint(List<Point> lst,Point p,int e,int minp){
int count=0;
List<Point> tmpLst=new ArrayList<Point>();
for(Iterator<Point> it=lst.iterator();it.hasNext();){
Point q=it.next();
if(getDistance(p,q)<=e){
++count;
if(!tmpLst.contains(q)){
tmpLst.add(q);
}
}
}
if(count>=minp){
p.setKey(true);
return tmpLst;
}
return null;
}
public static void setListClassed(List<Point> lst){
for(Iterator<Point> it=lst.iterator();it.hasNext();){
Point p=it.next();
if(!p.isClassed()){
p.setClassed(true);
}
}
}
/**
* 如果b中含有a中包含的元素,則把兩個集合
合并
* @param a
* @param b
* @return a
*/
public static boolean mergeList(List<Point> a,List<Point> b){
boolean merge=false;
for(int index=0;index<b.size();++index){
if(a.contains(b.get(index))){
merge=true;
break;
}
}
if(merge){
for(int index=0;index<b.size();++index){
if(!a.contains(b.get(index))){
a.add(b.get(index));
}
}
}
return merge;
}
/**
* 返迴文本中的點集合
* @return 返迴文本中點的集合
* @throws IOException
*/
public static List<Point> getPointsList() throws IOException{
List<Point> lst=new ArrayList<Point>();
String txtPath="src\com\sunzhenxing\points.txt";
BufferedReader br=new BufferedReader(new FileReader(txtPath));
String str="";
while((str=br.readLine())!=null && str!=""){
lst.add(new Point(str));
}
br.close();
return lst;
}
}
最後在主程式中實現演算法,如下所示:
package com.sunzhenxing;
import java.io.*;
import java.util.*;
public class Dbscan {
private static List<Point> pointsList=new ArrayList<Point>();//儲存所有點的集合
private static List<List<Point>> resultList=new ArrayList<List<Point>>();//儲存DBSCAN演算法返回的結果集
private static int e=2;//e半徑
private static int minp=3;//密度閾值
/**
* 提取文本中的的所有點並儲存在pointsList中
* @throws IOException
*/
private static void display(){
int index=1;
for(Iterator<List<Point>> it=resultList.iterator();it.hasNext();){
List<Point> lst=it.next();
if(lst.isEmpty()){
continue;
}
System.out.println("-----第"+index+"個聚類-----");
for(Iterator<Point> it1=lst.iterator();it1.hasNext();){
Point p=it1.next();
System.out.println(p.print());
}
index++;
}
}
//找出所有可以直達的聚類
private static void applyDbscan(){
try {
pointsList=Utility.getPointsList();
for(Iterator<Point> it=pointsList.iterator();it.hasNext();){
Point p=it.next();
if(!p.isClassed()){
List<Point> tmpLst=new ArrayList<Point>();
if((tmpLst=Utility.isKeyPoint(pointsList, p, e, minp)) != null){
//為所有聚類完畢的點做標示
Utility.setListClassed(tmpLst);
resultList.add(tmpLst);
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//對所有可以直達的聚類進行合并,即找出間接可達的點並進行合并
private static List<List<Point>> getResult(){
applyDbscan();//找到所有直達的聚類
int length=resultList.size();
for(int i=0;i<length;++i){
for(int j=i+1;j<length;++j){
if(Utility.mergeList(resultList.get(i), resultList.get(j))){
resultList.get(j).clear();
}
}
}
return resultList;
}
/**
* 程式主函數
* @param args
*/
public static void main(String[] args) {
getResult();
display();
//System.out.println(Utility.getDistance(new Point(0,0), new Point(0,2)));
}
}