聚类算法之MST算法 java实现版本

来源:互联网 发布:java如何获取get和post 编辑:程序博客网 时间:2024/06/06 07:22
在介绍最小生成树算法(MST)之前,简单说一下平均链接算法(average-link)的实现过程,平均链接聚类算法和单链接类似,多了计算聚类之间距离矩阵的步骤
实现步骤如下:



1,将元素各成一组,把这些组放入容器H

2,循环元素距离数组,根据两层下标得到将要比较的两个元素A,B

3,在H中分别查找含有A,B的组AH,BH。假如AH不等于BH(也就是A,B不同组), AH和BH的距离累加A,B的距离。

4,得到组间距离数组后,循环比较组间距离与阀值,小于阀值,则此两组合并成一组,合并之前把H中的两个作为比较的原始组删除。



MST算法比较有意思点,不仅用于聚类,还可以解决最短铺路成本这类问题。
我们假设一个场景:现在想在多个城市之间铺网络,怎样才是最短距离?每个城市当作一个数据点,每个点间的距离称为一个边,最短距离实际上就是求得每个点都能连成边,但是又不会回路的情况。
实现过程如下:

1,首先建立城市类和边类,如下

/** * 城市 *  * @author duyf *  */class City {private String name;// 经度private double x;// 纬度private double y;public double getX() {return x;}public void setX(double x) {this.x = x;}public double getY() {return y;}public void setY(double y) {this.y = y;}public String getName() {return name;}public void setName(String name) {this.name = name;}public boolean equals(Object obj) {if (obj == null) {return false;}if (this == obj) {return true;}City other = (City) obj;if (this.getX() == other.getX() && this.getY() == other.getY()) {return true;}return false;}}/** * 边距 包含两端数据点(城市)的索引 * @author duyf * */class Edge {        private int i;    private int j;    private double w;    Edge(int i, int j, double w) {        this.i = i;        this.j = j;        this.w = w;    }    public int getI() {        return i;    }    public int getJ() {        return j;    }    public double getW() {        return w;    }}  

2,MST核心类,Edge类表示一个边的两点和距离, 
找最短距离的边的过程是:不断的纳入最短边,并且再根据这些已知的最短边的两端寻找最短边(md 这句话我也感觉绕口 但应该是最通俗的了) 

public class MST {private List<City> data;private double[][] ds;public MST(List<City> data){this.data=data;}public List<Edge> compute(){// 距离矩阵ds = new double[data.size()][data.size()];for (int i = 0; i < data.size(); i++) {City city1 = data.get(i);for (int j = i + 1; j < data.size(); j++) {City city2 = data.get(j);ds[i][j] = getDistance(city1, city2);// 矩阵 对称性ds[j][i] = ds[i][j];}ds[i][i] = 0.0;}boolean[] isMst=new boolean[data.size()];isMst[0]=true;Edge edge=null;List<Edge> edges=new ArrayList<Edge>();        while((edge=findMinEdge(isMst))!=null){        edges.add(edge);                //标记为已知MST数据点        isMst[edge.getJ()]=true;        }return edges;}//找出 和 已知的MST数据点 最小距离的点private Edge findMinEdge(boolean[] isMst){//初始化无限大double minds = Double.POSITIVE_INFINITY;int minI=-1;int minJ=-1;Edge edge=null;for(int i=0;i<ds.length;i++){if(isMst[i]==true){for(int j=0;j<ds.length;j++){if(isMst[j]==false){    if(minds>ds[i][j]){    minds=ds[i][j];    minI=i;    minJ=j;}}}}}if(minI>-1){edge=new Edge(minI,minJ,minds);}return edge;}// 计算空间距离private double getDistance(City city1, City city2) { double  distance=Math.pow(city1.getX()-city2.getX(),2)+Math.pow(city1.getY()-city2.getY(),2); return Math.sqrt(distance); }}

第一步肯定是算出临近距离矩阵 

3,测试一下 

public static void main(String[] args) {List<City> citys = new ArrayList<City>();City city0 = new City();city0.setName("北 京");city0.setX(116.28);city0.setY(39.54);citys.add(city0);City city1 = new City();city1.setName("上 海");city1.setX(121.29);city1.setY(31.14);citys.add(city1);City city2 = new City();city2.setName("天 津");city2.setX(117.11);city2.setY(39.09);citys.add(city2);City city3 = new City();city3.setName("重 庆");city3.setX(106.32);city3.setY(29.32);citys.add(city3);City city4 = new City();city4.setName("哈尔滨");city4.setX(126.41);city4.setY(45.45);citys.add(city4);City city5 = new City();city5.setName("长 春");city5.setX(125.19);city5.setY(43.52);citys.add(city5);City city6 = new City();city6.setName("南 京");city6.setX(118.50);city6.setY(32.02);citys.add(city6);City city7 = new City();city7.setName("武 汉");city7.setX(114.21);city7.setY(30.37);citys.add(city7);City city8 = new City();city8.setName("台 北");city8.setX(121.31);city8.setY(25.03);citys.add(city8);City city9 = new City();city9.setName("香 港");city9.setX(114.10);city9.setY(22.18);citys.add(city9);MST mst=new MST(citys);List<Edge> edges=mst.compute();System.out.println("------------------线路最佳方案如下------------------");for(Edge edge:edges){City from=citys.get(edge.getI());City to=citys.get(edge.getJ());double length=edge.getW();System.out.println(edge.getI()+"========>"+edge.getJ());System.out.println(from.getName()+"到"+to.getName()+",全长"+length);}}