1. 程式人生 > >山東大學模式識別實驗(java)K-means演算法

山東大學模式識別實驗(java)K-means演算法

K-means是無監督學習,也就是說事先並不知道有幾類,所有資料都是無標記的,所以雖然本實驗的紅酒資料集認為應該分為3類,但是對於k-means來說是沒有意義的。每次分類前我們首先要人為指定分成K類,然後任意選取K個點作為K個類的中心點,遍歷全集,離哪個中心近就認為是哪一堆的。接下來我們要驗證所選中心點是否是真正的中心點,計算每一堆各個特徵的平均值後得出的就是這一個類的中心,如果與我們認為的中心不同則需要繼續計算。以剛才計算出的新中心重新全集遍歷分類然後重複上面判斷是否完成的過程。最終運算結束,輸出真正的中心點。

1.封裝的紅酒類:

public class Wine {
	private double[] data;

	public double[] getData() {
		return data;
	}

	public void setData(double[] data) {
		this.data = data;
	}
	

}

2.test類:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

public class Test {
	private Wine[] wineCenter;//確定為聚集中心的點
	private List<Wine>[] wineKind;//分類後的點陣列
	private int k;
	private List<Wine> wineAll;
	
	public Test(String path){//讀取資料檔案
		wineAll=new ArrayList<>();
		try {
			BufferedReader reader=new BufferedReader(new FileReader(new File(path)));
			String s=null;
			while((s=reader.readLine())!=null){
				wineAll.add(str2Wine(s.substring(2, s.length())));
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void setK(int k){
		this.k=k;
		wineCenter=new Wine[k];
		wineKind=new List[k];
		for (int i = 0; i < k; i++) {
			wineKind[i]=new ArrayList<>();
		}
	}
	
	public Wine str2Wine(String s){
		int start=0;
		int end=0;
		double []data=new double[13];
		Wine wine=new Wine();
		for(int i=0;i<12;i++){
			while(!s.substring(end,end+1).equals(",")){
				end++;
			}
			data[i]=Double.valueOf(s.substring(start,end));
			start=end+1;
			end++;
		}
		data[12]=Double.valueOf(s.substring(start,s.length()));
		wine.setData(data);
		return wine;
	}
	
	public double getDistance(Wine a,Wine b){//計算兩個點的歐氏距離,為了方便不開根號
		double result=0;
		double[] d1=a.getData();
		double[] d2=b.getData();
		for (int i = 0; i < 13; i++) {
			double aa=d1[i]-d2[i];
			result+=aa*aa;
		}
		return result;
	}
	
	public void work(){
		int length=wineAll.size();
		int dis=length/k;
		for(int i=0;i<k;i++){
			wineCenter[i]=wineAll.get(dis*i);
		}
		calcu();
		System.out.println("K是:"+k+"\n最終確定的中心點是:");
		for (int i = 0; i < k; i++) {
			double[] dou=wineCenter[i].getData();
			for (int j = 0; j < 13; j++) {
				System.out.print(dou[j]+"   ");
			}
			System.out.println();
		}
	}
	
	private void calcu() {//迭代計算
		boolean finish=true;
		for(int i=0;i<wineAll.size();i++){
			int kind=0;
			double minDis=Double.MAX_VALUE;
			Wine currentWine=wineAll.get(i);
			for (int j = 0; j < k; j++) {
				double currentDis=getDistance(currentWine, wineCenter[j]);
				if (currentDis<minDis) {
					kind=j;
					minDis=currentDis;
				}
			}
			wineKind[kind].add(currentWine);
		}
		for (int i = 0; i < k; i++) {//計算新的聚集中心
			double [] dataNew=new double[13];
			double []dataLast=wineCenter[i].getData();
			for (int p = 0; p < 13; p++) {
				double d=0;
				for (int j = 0; j < wineKind[i].size(); j++) {
					double[] q=wineKind[i].get(j).getData();
					d+=q[p];
				}
				d /=wineKind[i].size();
				dataNew[p]=d;
				if (dataLast[p]!=d) {
					finish=false;
				}
			}
			wineCenter[i].setData(dataNew);
		}
		for (int i = 0; i < k; i++) {
			System.out.println("第"+i+"類的個數是:"+wineKind[i].size());
		}
		if (!finish) {//還沒完成中心確定,遞迴
			wineKind=new List[k];//初始化
			for (int i = 0; i < k; i++) {
				wineKind[i]=new ArrayList<>();
			}
			calcu();
		}
	}

	public static void main(String[]args){
		Test test=new Test("a.txt");
		test.setK(10);
		test.work();
		String s="qwdfwef";
		for (int i = 0; i < args.length; i=i+2) {
			
		}
	}
}

實驗結果: