刚刚研究了Kmeans。Kmeans是一种十分简单的聚类算法。可是他十分依赖于用户最初给定的k值。它无法发现随意形状和大小的簇。最适合于发现球状簇。他的时间复杂度为O(tkn)。kmeans算法有两个核心点:计算距离的公式&推断迭代停止的条件。一般距採用欧式距离等能够随意。推断迭代停止的条件能够有:

1) 每一个簇的中心点不再变化则停止迭代

2)全部簇的点与这个簇的中心点的误差平方和(SSE)的全部簇的总和不再变化

3)设定人为的迭代次数。观察实验效果。

当初始簇心选择不好的时候聚类的效果会非常差。

所以后来又有一个人提出了二分k均值(bisectingkmeans),其核心思路是:将初始的一个簇一分为二计算出误差平方和最大的那个簇,对他进行再一次的二分。直至切分的簇的个数为k个停止。

事实上质就是不断的对选中的簇做k=2的kmeans切分。

由于聚类的误差平方和可以衡量聚类性能,该值越小表示数据点月接近于它们的质心。聚类效果就越好。所以我们就须要对误差平方和最大的簇进行再一次的划分。由于误差平方和越大,表示该簇聚类越不好,越有可能是多个簇被当成一个簇了。所以我们首先须要对这个簇进行划分。

以下是代码,kmeans的原始代码来源于http://blog.csdn.net/cyxlzzs/article/details/7416491,我稍作了一些改动。

package org.algorithm;

import java.util.ArrayList;
import java.util.List; /**
* 二分k均值。实际上是对一个集合做多次的k=2的kmeans划分。 每次划分后会对sse值较大的簇再进行二分。 终于使得或分出来的簇的个数为k个则停止
*
* 这里利用之前别人写好的一个kmeans的java实现作为基础类。 *
* @author l0979365428
*
*/
public class BisectingKmeans { private int k;// 分成多少簇
private List<float[]> dataSet;// 当前要被二分的簇
private List<ClusterSet> cluster; // 簇 /**
* @param args
*/
public static void main(String[] args) { // 初始化一个Kmean对象,将k置为10
BisectingKmeans bkm = new BisectingKmeans(5);
// 初始化试验集
ArrayList<float[]> dataSet = new ArrayList<float[]>(); dataSet.add(new float[] { 1, 2 });
dataSet.add(new float[] { 3, 3 });
dataSet.add(new float[] { 3, 4 });
dataSet.add(new float[] { 5, 6 });
dataSet.add(new float[] { 8, 9 });
dataSet.add(new float[] { 4, 5 });
dataSet.add(new float[] { 6, 4 });
dataSet.add(new float[] { 3, 9 });
dataSet.add(new float[] { 5, 9 });
dataSet.add(new float[] { 4, 2 });
dataSet.add(new float[] { 1, 9 });
dataSet.add(new float[] { 7, 8 });
// 设置原始数据集
bkm.setDataSet(dataSet);
// 运行算法
bkm.execute();
// 得到聚类结果
// ArrayList<ArrayList<float[]>> cluster = bkm.getCluster();
// 查看结果
// for (int i = 0; i < cluster.size(); i++) {
// bkm.printDataArray(cluster.get(i), "cluster[" + i + "]");
// } } public BisectingKmeans(int k) {
// 比2还小有啥要划分的意义么
if (k < 2) {
k = 2;
}
this.k = k; } /**
* 设置需分组的原始数据集
*
* @param dataSet
*/ public void setDataSet(ArrayList<float[]> dataSet) {
this.dataSet = dataSet;
} /**
* 运行算法
*/
public void execute() {
long startTime = System.currentTimeMillis();
System.out.println("BisectingKmeans begins");
BisectingKmeans();
long endTime = System.currentTimeMillis();
System.out.println("BisectingKmeans running time="
+ (endTime - startTime) + "ms");
System.out.println("BisectingKmeans ends");
System.out.println();
} /**
* 初始化
*/
private void init() { int dataSetLength = dataSet.size();
if (k > dataSetLength) {
k = dataSetLength;
}
} /**
* 初始化簇集合
*
* @return 一个分为k簇的空数据的簇集合
*/
private ArrayList<ArrayList<float[]>> initCluster() {
ArrayList<ArrayList<float[]>> cluster = new ArrayList<ArrayList<float[]>>();
for (int i = 0; i < k; i++) {
cluster.add(new ArrayList<float[]>());
} return cluster;
} /**
* Kmeans算法核心过程方法
*/
private void BisectingKmeans() {
init(); if (k < 2) {
// 小于2 则原样输出数据集被觉得是仅仅分了一个簇
ClusterSet cs = new ClusterSet();
cs.setClu(dataSet);
cluster.add(cs);
}
// 调用kmeans进行二分
cluster = new ArrayList(); while (cluster.size() < k) {
List<ClusterSet> clu = kmeans(dataSet); for (ClusterSet cl : clu) { cluster.add(cl); } if (cluster.size() == k)
break;
else// 顺序计算他们的误差平方和
{ float maxerro=0f;
int maxclustersetindex=0;
int i=0;
for (ClusterSet tt : cluster) {
//计算误差平方和并得出误差平方和最大的簇
float erroe = CommonUtil.countRule(tt.getClu(), tt
.getCenter());
tt.setErro(erroe); if(maxerro<erroe)
{
maxerro=erroe;
maxclustersetindex=i;
}
i++;
} dataSet=cluster.get(maxclustersetindex).getClu();
cluster.remove(maxclustersetindex); }
}
int i=0;
for(ClusterSet sc:cluster)
{
CommonUtil.printDataArray(sc.getClu(),"cluster"+i);
i++;
} } /**
* 调用kmeans得到两个簇。
*
* @param dataSet
* @return
*/
private List<ClusterSet> kmeans(List<float[]> dataSet) {
Kmeans k = new Kmeans(2); // 设置原始数据集
k.setDataSet(dataSet);
// 运行算法
k.execute();
// 得到聚类结果
List<List<float[]>> clus = k.getCluster(); List<ClusterSet> clusterset = new ArrayList<ClusterSet>(); int i = 0;
for (List<float[]> cl : clus) {
ClusterSet cs = new ClusterSet();
cs.setClu(cl);
cs.setCenter(k.getCenter().get(i));
clusterset.add(cs);
i++;
} return clusterset;
} class ClusterSet {
private float erro;
private List<float[]> clu;
private float[] center; public float getErro() {
return erro;
} public void setErro(float erro) {
this.erro = erro;
} public List<float[]> getClu() {
return clu;
} public void setClu(List<float[]> clu) {
this.clu = clu;
} public float[] getCenter() {
return center;
} public void setCenter(float[] center) {
this.center = center;
} }
}
package org.algorithm;

import java.util.List;

/**
* 把计算距离和误差的公式抽离出来
* @author l0979365428
*
*/
public class CommonUtil { /**
* 计算两个点之间的距离
*
* @param element
* 点1
* @param center
* 点2
* @return 距离
*/
public static float distance(float[] element, float[] center) {
float distance = 0.0f;
float x = element[0] - center[0];
float y = element[1] - center[1];
float z = x * x + y * y;
distance = (float) Math.sqrt(z); return distance;
}
/**
* 求两点误差平方的方法
*
* @param element
* 点1
* @param center
* 点2
* @return 误差平方
*/
public static float errorSquare(float[] element, float[] center) {
float x = element[0] - center[0];
float y = element[1] - center[1]; float errSquare = x * x + y * y; return errSquare;
}
/**
* 计算误差平方和准则函数方法
*/
public static float countRule( List<float[]> cluster,float[] center) {
float jcF = 0; for (int j = 0; j < cluster.size(); j++) {
jcF += CommonUtil.errorSquare(cluster.get(j), center); } return jcF;
}
/**
* 打印数据。測试用
*
* @param dataArray
* 数据集
* @param dataArrayName
* 数据集名称
*/
public static void printDataArray(List<float[]> dataArray, String dataArrayName) {
for (int i = 0; i < dataArray.size(); i++) {
System.out.println("print:" + dataArrayName + "[" + i + "]={"
+ dataArray.get(i)[0] + "," + dataArray.get(i)[1] + "}");
}
System.out.println("===================================");
}
}
package org.algorithm;

import java.util.ArrayList;
import java.util.List;
import java.util.Random; /**
* K均值聚类算法
*/
public class Kmeans {
private int k;// 分成多少簇
private int m;// 迭代次数
private int dataSetLength;// 数据集元素个数,即数据集的长度
private List<float[]> dataSet;// 数据集链表
private List<float[]> center;// 中心链表
private List<List<float[]>> cluster; // 簇
private List<Float> jc;// 误差平方和,k越接近dataSetLength,误差越小
private Random random; public static void main(String[] args) {
// 初始化一个Kmean对象,将k置为10
Kmeans k = new Kmeans(5);
// 初始化试验集
ArrayList<float[]> dataSet = new ArrayList<float[]>(); dataSet.add(new float[] { 1, 2 });
dataSet.add(new float[] { 3, 3 });
dataSet.add(new float[] { 3, 4 });
dataSet.add(new float[] { 5, 6 });
dataSet.add(new float[] { 8, 9 });
dataSet.add(new float[] { 4, 5 });
dataSet.add(new float[] { 6, 4 });
dataSet.add(new float[] { 3, 9 });
dataSet.add(new float[] { 5, 9 });
dataSet.add(new float[] { 4, 2 });
dataSet.add(new float[] { 1, 9 });
dataSet.add(new float[] { 7, 8 });
// 设置原始数据集
k.setDataSet(dataSet);
// 运行算法
k.execute();
// 得到聚类结果
List<List<float[]>> cluster = k.getCluster();
// 查看结果
for (int i = 0; i < cluster.size(); i++) {
CommonUtil.printDataArray(cluster.get(i), "cluster[" + i + "]");
} } /**
* 设置需分组的原始数据集
*
* @param dataSet
*/ public void setDataSet(List<float[]> dataSet) {
this.dataSet = dataSet;
} /**
* 获取结果分组
*
* @return 结果集
*/ public List<List<float[]>> getCluster() {
return cluster;
} /**
* 构造函数,传入须要分成的簇数量
*
* @param k
* 簇数量,若k<=0时,设置为1,若k大于数据源的长度时,置为数据源的长度
*/
public Kmeans(int k) {
if (k <= 0) {
k = 1;
}
this.k = k;
} /**
* 初始化
*/
private void init() {
m = 0;
random = new Random();
if (dataSet == null || dataSet.size() == 0) {
initDataSet();
}
dataSetLength = dataSet.size();
if (k > dataSetLength) {
k = dataSetLength;
}
center = initCenters();
cluster = initCluster();
jc = new ArrayList<Float>();
} /**
* 假设调用者未初始化数据集,则採用内部測试数据集
*/
private void initDataSet() {
dataSet = new ArrayList<float[]>();
// 当中{6,3}是一样的,所以长度为15的数据集分成14簇和15簇的误差都为0
float[][] dataSetArray = new float[][] { { 8, 2 }, { 3, 4 }, { 2, 5 },
{ 4, 2 }, { 7, 3 }, { 6, 2 }, { 4, 7 }, { 6, 3 }, { 5, 3 },
{ 6, 3 }, { 6, 9 }, { 1, 6 }, { 3, 9 }, { 4, 1 }, { 8, 6 } }; for (int i = 0; i < dataSetArray.length; i++) {
dataSet.add(dataSetArray[i]);
}
} /**
* 初始化中心数据链表,分成多少簇就有多少个中心点
*
* @return 中心点集
*/
private ArrayList<float[]> initCenters() {
ArrayList<float[]> center = new ArrayList<float[]>();
int[] randoms = new int[k];
boolean flag;
int temp = random.nextInt(dataSetLength);
randoms[0] = temp;
for (int i = 1; i < k; i++) {
flag = true;
while (flag) {
temp = random.nextInt(dataSetLength);
int j = 0; while (j < i) {
if (temp == randoms[j]) {
break;
}
j++;
}
if (j == i) {
flag = false;
}
}
randoms[i] = temp;
} for (int i = 0; i < k; i++) {
center.add(dataSet.get(randoms[i]));// 生成初始化中心链表
}
return center;
} /**
* 初始化簇集合
*
* @return 一个分为k簇的空数据的簇集合
*/
private List<List<float[]>> initCluster() {
List<List<float[]>> cluster = new ArrayList();
for (int i = 0; i < k; i++) {
cluster.add(new ArrayList<float[]>());
} return cluster;
} /**
* 获取距离集合中最小距离的位置
*
* @param distance
* 距离数组
* @return 最小距离在距离数组中的位置
*/
private int minDistance(float[] distance) {
float minDistance = distance[0];
int minLocation = 0;
for (int i = 1; i < distance.length; i++) {
if (distance[i] < minDistance) {
minDistance = distance[i];
minLocation = i;
} else if (distance[i] == minDistance) // 假设相等,随机返回一个位置
{
if (random.nextInt(10) < 5) {
minLocation = i;
}
}
} return minLocation;
} /**
* 核心,将当前元素放到最小距离中心相关的簇中
*/
private void clusterSet() {
float[] distance = new float[k];
for (int i = 0; i < dataSetLength; i++) {
for (int j = 0; j < k; j++) {
distance[j] = CommonUtil
.distance(dataSet.get(i), center.get(j)); }
int minLocation = minDistance(distance); cluster.get(minLocation).add(dataSet.get(i));// 核心,将当前元素放到最小距离中心相关的簇中 }
} /**
* 计算误差平方和准则函数方法
*/
private void countRule() {
float jcF = 0;
for (int i = 0; i < cluster.size(); i++) {
for (int j = 0; j < cluster.get(i).size(); j++) {
jcF += CommonUtil.errorSquare(cluster.get(i).get(j), center
.get(i)); }
}
jc.add(jcF);
} /**
* 设置新的簇中心方法
*/
private void setNewCenter() {
for (int i = 0; i < k; i++) {
int n = cluster.get(i).size();
if (n != 0) {
float[] newCenter = { 0, 0 };
for (int j = 0; j < n; j++) {
newCenter[0] += cluster.get(i).get(j)[0];
newCenter[1] += cluster.get(i).get(j)[1];
}
// 设置一个平均值
newCenter[0] = newCenter[0] / n;
newCenter[1] = newCenter[1] / n;
center.set(i, newCenter);
}
}
} public List<float[]> getCenter() {
return center;
} public void setCenter(List<float[]> center) {
this.center = center;
} /**
* Kmeans算法核心过程方法
*/
private void kmeans() {
init(); // 循环分组。直到误差不变为止
while (true) {
clusterSet();
countRule(); if (m != 0) {
if (jc.get(m) - jc.get(m - 1) == 0) {
break;
}
} setNewCenter(); m++;
cluster.clear();
cluster = initCluster();
} } /**
* 运行算法
*/
public void execute() {
long startTime = System.currentTimeMillis();
System.out.println("kmeans begins");
kmeans();
long endTime = System.currentTimeMillis();
System.out.println("kmeans running time=" + (endTime - startTime)
+ "ms");
System.out.println("kmeans ends");
System.out.println();
}
}

分别运行两种聚类算法都使得k=5结果例如以下:

Kmeans:

print:cluster[0]={5.0,6.0}
print:cluster[1]={4.0,5.0}
print:cluster[2]={6.0,4.0}
===================================
print:cluster[0]={1.0,2.0}
print:cluster[1]={3.0,3.0}
print:cluster[2]={3.0,4.0}
print:cluster[3]={4.0,2.0}
===================================
print:cluster[0]={7.0,8.0}
===================================
print:cluster[0]={8.0,9.0}
===================================
print:cluster[0]={3.0,9.0}
print:cluster[1]={5.0,9.0}
print:cluster[2]={1.0,9.0}
===================================
BisectingKmeans:
print:cluster0[0]={8.0,9.0}
print:cluster0[1]={7.0,8.0}
===================================
print:cluster1[0]={3.0,4.0}
print:cluster1[1]={5.0,6.0}
print:cluster1[2]={4.0,5.0}
print:cluster1[3]={6.0,4.0}
===================================
print:cluster2[0]={1.0,2.0}
print:cluster2[1]={3.0,3.0}
print:cluster2[2]={4.0,2.0}
===================================
print:cluster3[0]={1.0,9.0}
===================================
print:cluster4[0]={3.0,9.0}
print:cluster4[1]={5.0,9.0}
===================================

如上有理解问题还请指正。

參考文献:

http://blog.csdn.net/zouxy09/article/details/17590137

http://wenku.baidu.com/link?url=e6sXeX_txPMnNnYy8W28mP-HSD2Lk8cQGbW-4esipqu95r-P4Ke2QPeHLhfBtoie6agplav6VtVwxlyg-jf_5byHJ_Ce93ARqA6U9rn6XKK

《机器学习实战》

二分Kmeans的java实现的更多相关文章

  1. 二分K-means算法

    二分K-means聚类(bisecting K-means) 算法优缺点: 由于这个是K-means的改进算法,所以优缺点与之相同. 算法思想: 1.要了解这个首先应该了解K-means算法,可以看这 ...

  2. k-means聚类JAVA实例

    <mahout in action>第六章. datafile/cluster/simple_k-means.txt数据集例如以下: 1 1 2 1 1 2 2 2 3 3 8 8 8 9 ...

  3. K-Means 算法(Java)

    kMeans算法原理见我的上一篇文章.这里介绍K-Means的Java实现方法,参考了Python的实现方法. 一.数据点的实现 package com.meachine.learning.kmean ...

  4. 二分查找问题(Java版)

    二分查找问题(Java版)   1.一般实现 package search;   /**  * @author lei 2011-8-17  */ public class BinarySearch ...

  5. 3.聚类–K-means的Java实现

    K-means的步骤 输入: 含n 个样本的数据集,簇的数据K 输出: K 个簇 算法步骤: 1.初始化K个簇类中心C1,C2,---Ck (通常随机选择) 2.repeat 步骤3,4 3,将数据集 ...

  6. 算法:时间复杂度+二分查找法(Java/Go/Python)实现

    导读 曾几何时学好数据结构与算法是我们从事计算机相关工作的基本前提,然而现在很多程序员从事的工作都是在用高级程序设计语言(如Java)开发业务代码,久而久之,对于数据结构和算法就变得有些陌生了,由于长 ...

  7. 【Java】K-means算法Java实现以及图像切割

    1.K-means算法简述以及代码原型 数据挖掘中一个重要算法是K-means.我这里就不做具体介绍.假设感兴趣的话能够移步陈皓的博客: http://www.csdn.net/article/201 ...

  8. 递归分治算法之二维数组二分查找(Java版本)

    [java] /** * 递归分治算法学习之二维二分查找 * @author Sking 问题描述: 存在一个二维数组T[m][n],每一行元素从左到右递增, 每一列元素从上到下递增,现在需要查找元素 ...

  9. k-means算法Java一维实现

    这里的程序稍微有点变形.k_means方法返回K-means聚类的若干中心点.代码: import java.util.ArrayList; import java.util.Collections; ...

随机推荐

  1. MySQL读写分离-架构

    MySQL读写分离-架构 简介 对于很多大型网站(pv值百万.千万)来说,在所处理的业务中,其中有70%的业务是查询(select)相关的业务操作(新闻网站,插入一条新闻.查询操作),剩下的则是写(i ...

  2. Python的网络编程[1] -> FTP 协议[1] -> 使用 pyftplib 建立 FTP 服务器

    使用 pyftplib 建立 FTP 服务器 pyftplib 主要用于建立 FTP Server,与 ftplib 建立的 Client 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 服 ...

  3. 数学【p2117】 小z的矩阵

    题目描述-->p2117 小z的矩阵 分析: 题目给定我们一个正方形. 容易想到,正方形是对称的. 推敲一下 如果我们的矩阵是这样的↓ 闭眼瞎敲出来的. \[\begin{bmatrix} {0 ...

  4. 理解竞争条件( Race condition)漏洞

    这几天一个叫做"Dirty COW"的linux内核竞争条件漏洞蛮火的,相关公司不但给这个漏洞起了个洋气的名字,还给它设计了logo(见下图),首页,Twitter账号以及网店.恰 ...

  5. 【strork】HAOI2017旅游记

    乘着大巴踏上归程,看着赛后落寞的一些人,赛后轻松的一些人,感触颇多. [爪机被lsj坑了,照片等周末再放...] 4月22: 早上起床不用跑操,到了教室我就看见LSJ跑过来,不用想,果然:“早上nin ...

  6. Bluetooth篇 开发实例之五 为什么无线信号(RSSI)是负值?

    原文:http://www.cnblogs.com/lele/articles/2832885.html   为什么无线信号(RSSI)是负值 答:其实归根到底为什么接收的无线信号是负值,这样子是不是 ...

  7. C++ development cross platforms

    1. target platforms: linux suse, windows server, both use vmware virtual machine on lab server. 2. c ...

  8. html 基础二

    HTMLCSS基础2 一.HTML中的标签 1.1标签的特点 给文本加上含有语义的标签 应该学习更多具体语义标签 标签:用“<>”包起来的内容 2.1 更多的标签 2.1.1 h系列的标签 ...

  9. Volley缓存说明——一个请求两次回调

    从上一篇文章Android 异步网络请求框架-Volley了解volley的一些出来过程,当然也包含网络请求和缓存处理的流程,但是在此需要单独做一些说明. 我在使用过程中忽略了一个事情,就是一个网络请 ...

  10. numpy 多维数组的存取

    多维数组的存取和一维数组类似,由于多维数组有多个轴,所以他的下标需要多个值来表示.这里讨论的主要是二维数组.二维数组0轴以行为单位,1轴以列为单位,存取数组使用元组作为下标,需要注意的是,python ...