相較於前一篇所實作的KNN「分類」演算法,本篇所要實作的則是K-means「分群」演算法(Unsupervised learning),兩者同樣都是使用Euclidean distance來決定和中心點的距離計算,只不過K-means還需要反覆不斷的修正K個中心點,直到K個點慢慢地趨於穩定才算完成,詳細的程式如下:
(本範例K值為3,所以一開始在畫面上點擊滑鼠左鍵會先依序分配3個分群的中心點,接著再不斷地點擊滑鼠左鍵就可以觀察到K點的變化,玩玩看吧>>kmeans)
Kmeans.as
package { import flash.display.*; import flash.events.*; import flash.geom.Point; import flash.utils.*; public class Kmeans extends MovieClip { private var k:uint = 3; private var count:uint = 0; private var _r:uint = 0; private var _g:uint = 0; private var _b:uint = 0; private var k_center:Array = new Array(); private var k_means:Array = new Array(); public function Kmeans() { stage.addEventListener(MouseEvent.MOUSE_UP,put); init(); } public function put(m:MouseEvent):void { if(count < k) { k_means[count] = new Array(); var c:Circle = null; if(count == 0) { c = new Circle(0x0000FF); }else if(count == 1) { c = new Circle(0x00FF00); }else{ c = new Circle(0xFF0000); } c.x = stage.mouseX; c.y = stage.mouseY; addChild(c); k_center[count] = c; count++; return; } for(var w = 0 ; w < 20 ; w++) { var cc2:Circle = this.getChildAt(w) as Circle; var k_arr:Array = new Array(); var k_dict:Dictionary = new Dictionary(true); for(var v = 0 ; v < k ;v++) { var k_c = k_center[v]; var dist2 = Point.distance(new Point(k_c.x,k_c.y),new Point(cc2.x,cc2.y)); k_dict[dist2] = k_c; k_arr.push(dist2); } k_arr.sort(Array.NUMERIC); var min = k_arr[0]; var color:uint = k_dict[min].getColor(); cc2.reDraw(color); if(color == 0x0000FF) { k_means[0][_b] = new Point(cc2.x,cc2.y); _b++; }else if(color == 0x00FF00) { k_means[1][_g] = new Point(cc2.x,cc2.y); _g++; }else{ k_means[2][_r] = new Point(cc2.x,cc2.y); _r++; } } trace(_r+" "+_g+" "+_b); var xx=0,yy=0; for(var bb = 0 ; bb < _b ; bb++) { xx += k_means[0][bb].x; yy += k_means[0][bb].y; } k_center[0].x = xx/_b; k_center[0].y = yy/_b; xx=0;yy=0; for(var gg = 0 ; gg < _g ; gg++) { xx += k_means[1][gg].x; yy += k_means[1][gg].y; } k_center[1].x = xx/_g; k_center[1].y = yy/_g; xx=0;yy=0; for(var rr = 0 ; rr < _r ; rr++) { xx += k_means[2][rr].x; yy += k_means[2][rr].y; } k_center[2].x = xx/_r; k_center[2].y = yy/_r; trace(k_center[0].x+" "+k_center[0].y); } public function init():void { for(var i = 0 ; i < 10 ; i++) { var c:Circle = new Circle(0x777777); c.x = Math.random()*500; c.y = Math.random()*500; addChild(c); } for(i = 0 ; i < 10 ; i++) { var c2:Circle = new Circle(0x777777); c2.x = Math.random()*500; c2.y = Math.random()*500; addChild(c2); } } } }
Circle.as
package { import flash.display.*; public class Circle extends Sprite { private var color:uint = 0; public function Circle(c:uint) { this.color = c; this.graphics.lineStyle(5); this.graphics.beginFill(this.color); this.graphics.drawCircle(0,0,20); this.graphics.endFill(); } public function getColor():uint { return this.color; } public function reDraw(color:uint):void { this.graphics.clear(); this.graphics.lineStyle(5); this.graphics.beginFill(color); this.graphics.drawCircle(0,0,20); this.graphics.endFill(); this.alpha = .5; } } }