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算法處理一下非凸面的簇時。
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