數據挖掘經典算法——K-means算法

算法描述

  K-means算法是一種被普遍使用的基於劃分的聚類算法,目的是將n個對象會分紅k個簇。算法的具體描述以下:html

隨機選取k個對象做爲簇中心;

Do

          計算全部對象到這k個簇中心的距離,將距離最近的納入相應的簇;

          從新計算每一個簇的中心;

          計算準則函數V;

While 準則函數的值穩定(或變化小於某個閾值)

 

  其中準則函數V的定義以下:java

         

  其中,ui表示第i個簇Si的中心。最終通過T次迭代獲取到最終的分類結果,對於第t+1次迭代以後獲得的中心,有以下定義:node

           

算法的優缺點

  優勢:算法

    1)         算法描述簡單,高效;性能優化

    2)         適合用於處理大數據,獲得的算法的複雜度大約爲O(nkt),n表示對象的數量,k是劃分的簇數量,t爲迭代次數。一般狀況下可以保證k<<n,算法的效率有必定的保障;dom

    3)         算法比較適合處理簇之間劃分明確的對象集合;ide

  缺點:函數

    1)         k值必須手動的給出,選取k值就顯得特別重要了;性能

    2)         不一樣的初始對象會帶來不一樣的劃分結果;大數據

    3)         若是對象集合內部包含一些小範圍孤立對象,這種基於局部最優的聚類劃分算法可能會產生一些錯誤的劃分;

    4)         一般判斷對象之間遠近的依據是歐拉距離,能夠儘快獲得結果,但同時也帶來了一些缺點,好比採用K-means算法處理一下非凸面的簇時。

Kmeans算法的Java實現

  1 import java.awt.BorderLayout;
  2 import java.awt.Canvas;
  3 import java.awt.Color;
  4 import java.awt.Dimension;
  5 import java.awt.Frame;
  6 import java.awt.Graphics;
  7 import java.awt.Graphics2D;
  8 import java.awt.Paint;
  9 import java.awt.event.WindowAdapter;
 10 import java.awt.event.WindowEvent;
 11 import java.util.ArrayList;
 12 import java.util.HashSet;
 13 import java.util.List;
 14 import java.util.Random;
 15 import java.util.Set;
 16 
 17 import ocr.algorithm.KMeans.Cluster;
 18 import ocr.algorithm.KMeans.KMeansNode;
 19 
 20 /**
 21  * 這是一個簡單的Kmeans算法實現
 22  * 假設前提包括:
 23  * 1.集合中每一個對象都是一個位於二維平面的點
 24  * 2.對象之間的距離斷定以歐氏距離爲標準
 25  * 3.這只是一個樣例程序,主要用於敘述K-means算法的主幹部分,一些特殊的狀況不曾考慮(數據溢出,性能優化)
 26  * @author yahokuma
 27  * @email Hazin.lu@gmail.com
 28  */
 29 public class KMeans {
 30     
 31     private List<KMeansNode> datas = new ArrayList<KMeans.KMeansNode>();
 32     
 33     public static class KMeansNode {
 34         private double x;
 35         private double y;
 36         
 37         
 38         
 39         public double getX() {
 40             return x;
 41         }
 42 
 43         public double getY() {
 44             return y;
 45         }
 46 
 47         public KMeansNode(double x,double y){
 48             this.x = x;
 49             this.y = y;
 50         }
 51         
 52         public double distance(KMeansNode n){
 53             return 
 54                 Math.pow( x - n.x , 2 ) + Math.pow( y - n.y , 2 );
 55         }
 56         
 57     }
 58     
 59     public static class Cluster{
 60         private List<KMeansNode> nodes = new ArrayList<KMeans.KMeansNode>();
 61         private KMeansNode center = null;
 62         public KMeansNode getCenter() {
 63             return center;
 64         }
 65         public void addNode(KMeansNode n){
 66             this.nodes.add(n);
 67         }
 68         public Cluster(KMeansNode c){
 69             this.center = c;
 70         }
 71         public void calculateCenter(){
 72             double x = 0,y = 0;
 73             for (KMeansNode n : nodes) {
 74                 x += n.x;
 75                 y += n.y;
 76             }
 77             this.center = new KMeansNode( x / nodes.size(), y / nodes.size());
 78         }
 79         
 80         public double criterion(){
 81             double criterion = 0;
 82             calculateCenter();
 83             for (KMeansNode n : nodes) {
 84                 criterion += center.distance(n);
 85             }
 86             
 87             return criterion;
 88         }
 89         
 90         public void clear(){
 91             this.nodes.clear();
 92         }
 93         
 94         public List<KMeansNode> getNodes(){
 95             return this.nodes;
 96         }
 97         
 98         public void print(){
 99             System.out.println("Contains "+ nodes.size() + " Nodes !");
100             System.out.println("Center Node is ( "+ getCenter().x + "," + getCenter().y + " )");
101         }
102     }
103     
104     public KMeans(List<KMeansNode> datas){
105         this.datas = datas;
106     }
107     
108     
109     private List<KMeansNode> findRandNodes(int k){
110         List<KMeansNode> rNodes = new ArrayList<KMeans.KMeansNode>();
111         Set<Integer> rIndexes = new HashSet<Integer>(); 
112         Random r = new Random();
113         Integer rInt = null;
114         for (int i = 0; i < k; i++) {
115             rInt = r.nextInt(datas.size());
116             while(rIndexes.contains(rInt))
117                 rInt = r.nextInt(datas.size());
118             
119             rIndexes.add(rInt);
120             rNodes.add( datas.get(rInt));
121         }
122         
123         return rNodes;
124     }
125     
126     private double calculateCriterion(List<Cluster> clusters){
127         double res = 0;
128         for (Cluster c : clusters) {
129             res += c.criterion();
130         }
131         return res;
132     }
133     
134     
135     
136     public List<Cluster> partition(int k){
137         List<KMeansNode> centerNodes = findRandNodes(k);
138         List<Cluster> clusters = new ArrayList<KMeans.Cluster>();
139         for (KMeansNode c : centerNodes) {
140             clusters.add(new Cluster(c));
141         }
142         
143         double minDistance = Double.MAX_VALUE , distance;
144         Cluster minCluster = null;
145         double lastCriterion , criterion= Double.MAX_VALUE;
146         
147         do{
148             for (Cluster c : clusters) {
149                 c.clear();
150             }
151             lastCriterion = criterion;
152             
153             
154             for (KMeansNode n : datas) {
155                 minDistance = Double.MAX_VALUE;
156                 for (Cluster c : clusters) {
157                     distance = c.getCenter().distance(n);
158                     if( distance < minDistance ){
159                         minDistance = distance;
160                         minCluster = c;
161                     }
162                 }
163                 minCluster.addNode(n);
164             }
165             criterion = calculateCriterion(clusters);
166             
167         }while( criterion != lastCriterion);
168         
169         return clusters;
170         
171     }
172     
173     /**
174     *隨機生成了1000個平面點,將其劃分爲4個簇(k=4)
175     *因爲點的座標都是隨機生成的,在空間上分佈均勻;
176     *從結果中能夠看出K-means對於處理這種邊界不分明的對象集合時並不能很好的進行區分;
177     *可是通常狀況,通過處理仍是會將整個平面均勻得劃分紅四個部分
178     **/
179     public static void main(String args[]){
180         Random r = new Random();
181         List<KMeansNode> nodes = new ArrayList<KMeans.KMeansNode>();
182         for (int i = 0; i < 1000; i++) {
183             nodes.add(new KMeansNode(r.nextDouble() * 1000, r.nextDouble() * 1000));
184         }
185         
186         KMeans kmeans = new KMeans(nodes);
187         List<Cluster> clusters = kmeans.partition(4);
188         for( Cluster c :  clusters){
189             c.print();
190         }
191         
192         
193          Frame frame = new Frame("K-means Test!");  
194          frame.addWindowListener(new WindowAdapter(){  
195              public void windowClosing(WindowEvent e) {  
196                  System.exit(0);  
197              }  
198          });  
199          frame.add(new KMeansCanvas(clusters),BorderLayout.CENTER);  
200          frame.pack();  
201          frame.setVisible(true);  
202     }
203     
204 }
205 
206 
207 /**
208  *  
209  * @author yahokuma
210  * @email Hazin.lu@gmail.com
211  */
212 class KMeansCanvas extends Canvas {  
213     public final static Paint[] PAINT_COLOR = {Color.BLUE,Color.RED, Color.ORANGE, Color.BLACK};
214     
215     private List<Cluster> clusters = null;
216     public KMeansCanvas(List<Cluster> clusters) {  
217         this.setBackground(Color.WHITE); 
218         this.clusters = clusters;
219     }  
220     @Override  
221     public void paint(Graphics g) {  
222         drawKarel(g);  
223     }  
224     @Override  
225     public Dimension getPreferredSize() {  
226         return new Dimension(1000,1000);  
227     }  
228     
229     
230     private void drawKarel(Graphics g) {  
231         
232     
233         Random r = new Random();
234         int i = 0 ;
235         for (Cluster c : clusters) {
236             Graphics2D g2d= (Graphics2D) g;  
237             g2d.setPaint(PAINT_COLOR[i++]);
238             for (KMeansNode n : c.getNodes()) {
239                 g2d.drawRect((int)n.getX(), (int)n.getY() , 2, 2);
240                 g2d.fillRect((int)n.getX(), (int)n.getY() , 2, 2);
241             }
242         }
243     }  
244     private static final long serialVersionUID = 1L;  
245 }  

 參考資料

http://en.wikipedia.org/wiki/K-means_clustering

http://blog.csdn.net/aladdina/article/details/4141177

http://www.cnblogs.com/jerrylead/archive/2011/04/06/2006910.html

http://zh.wikipedia.org/wiki/K%E5%B9%B3%E5%9D%87%E7%AE%97%E6%B3%95

相關文章
相關標籤/搜索