kmeans聚类的实现

Kmeans算法流程

从数据中随机抽取k个点作为初始聚类的中心,由这个中心代表各个聚类
计算数据中所有的点到这k个点的距离,将点归到离其最近的聚类里
调整聚类中心,即将聚类的中心移动到聚类的几何中心(即平均值)处,也就是k-means中的mean的含义
重复第2步直到聚类的中心不再移动,此时算法收敛
最后kmeans算法时间、空间复杂度是:
时间复杂度:上限为O(tKmn),下限为Ω(Kmn)其中,t为迭代次数,K为簇的数目,m为记录数,n为维数 
空间复杂度:O((m+K)n),其中,K为簇的数目,m为记录数,n为维数


影响算法准确度的因素

数据的采集和抽象

初始的中心选择

k值的选定

最大迭代次数

收敛值

度量距离的手段


在进一步阐述初始中心点选择之前,我们应该先确定度量kmeans的算法精确度的方法。一种度量聚类效果的标准是:SSE(Sum of Square Error,误差平方和)
SSE越小表示数据点越接近于它们的质心,聚类效果也就越好。因为对误差取了平方所以更重视那些远离中心的点。
一种可以肯定降低SSE的方法是增加簇的个数。但这违背了聚类的目标。因为聚类是在保持目标簇不变的情况下提高聚类的质量。
现在思路明了了我们首先以缩小SSE为目标改进算法。


二分K均值

为了克服k均值算法收敛于局部的问题,提出了二分k均值算法。该算法首先将所有的点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续划分,选择哪个簇进行划分取决于对其划分是否可以最大程度降低SSE值。
伪代码如下:
将所有的点看成一个簇
当簇数目小于k时
对于每一个簇
计算总误差
在给定的簇上面进行K均值聚类(K=2)
计算将该簇一分为二后的总误差
选择使得误差最小的那个簇进行划分操作



// spectral-cluster.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream> 
#include<vector>
#include<time.h>
#include<cstdlib>
#include<set>

using namespace std;

//template <typename T>
class kmeans
{
private:
	vector<vector<int>>dataset;
	unsigned int k;
	unsigned int dim;
	typedef vector<double> Centroid;
	vector<Centroid> center;
	vector<set<int>>cluster_ID;
	vector<Centroid>new_center;
	vector<set<int>>new_cluster_ID;
	double threshold;
	int iter;
private:
	void init();
	void assign();
	double distance(Centroid cen, int k2);
	void split(vector<set<int>>&clusters, int kk);
	void update_centers();
	bool isfinish();
	void show_result();
	void generate_data()
	{
		dim = 2;
		threshold = 0.001;
		k = 4;
		for (int i = 0; i < 300; i++)
		{
			vector<int>data;
			data.resize(dim);
			for (int j = 0; j < dim; j++)
				data[j] = double(rand()) / double(RAND_MAX + 1.0) * 500;
			dataset.push_back(data);
		}
	}
public:
	kmeans()
	{
		time_t t;
		srand(time(&t));
	}
	void apply();
};

//template <typename T>
void kmeans::init()
{
	center.resize(k);

	set<int>bb;
	for (int i = 0; i < k; i++)
	{

		int id = double(rand()) / double(RAND_MAX + 1.0)*dataset.size();
		while (bb.find(id) != bb.end())
		{
			id = double(rand()) / double(RAND_MAX + 1.0)*dataset.size();
		}
		bb.insert(id);
		center[i].resize(dim);
		for (int j = 0; j < dim; j++)
			center[i][j] = dataset[id][j];

	}
}
bool kmeans::isfinish()
{
	double error = 0;
	for (int i = 0; i < k; i++)
	{
		for (int j = 0; j < dim; j++)
			error += pow(center[i][j] - new_center[i][j], 2);
	}
	return error < threshold ? true : false;
}
void kmeans::assign()
{

	for (int j = 0; j < dataset.size(); j++)
	{
		double mindis = 10000000;
		int belongto = -1;
		for (int i = 0; i < k; i++)
		{
			double dis = distance(center[i], j);
			if (dis < mindis)
			{
				mindis = dis;
				belongto = i;
			}
		}
		new_cluster_ID[belongto].insert(j);
	}
	for (int i = 0; i < k; i++)
	{
		if (new_cluster_ID[i].empty())
		{
			split(new_cluster_ID, i);
		}
	}
}

double kmeans::distance(Centroid cen, int k2)
{
	double dis = 0;
	for (int i = 0; i < dim; i++)
		dis += pow(cen[i] - dataset[k2][i], 2);
	return sqrt(dis);
}

void kmeans::split(vector<set<int>>&clusters, int kk)
{
	int maxsize = 0;
	int th = -1;
	for (int i = 0; i < k; i++)
	{
		if (clusters[i].size() > maxsize)
		{
			maxsize = clusters[i].size();
			th = i;
		}
	}
#define DELTA 1
	vector<double>tpc1, tpc2;
	tpc1.resize(dim);
	tpc2.resize(dim);
	for (int i = 0; i < dim; i++)
	{
		tpc2[i] = center[th][i] - DELTA;
		tpc1[i] = center[th][i] + DELTA;
	}
	for (set<int>::iterator it = clusters[th].begin(); it != clusters[th].end(); it++)
	{
		double d1 = distance(tpc1, *it);
		double d2 = distance(tpc2, *it);
		if (d2 < d1)
		{
			clusters[kk].insert(*it);
		}
	}
	_ASSERTE(!clusters[kk].empty());
	for (set<int>::iterator it = clusters[kk].begin(); it != clusters[kk].end(); it++)
		clusters[th].erase(*it);

}

void kmeans::update_centers()
{
	for (int i = 0; i < k; i++)
	{
		Centroid temp;
		temp.resize(dim);
		for (set<int>::iterator j = new_cluster_ID[i].begin(); j != new_cluster_ID[i].end(); j++)
		{
			for (int m = 0; m < dim; m++)
				temp[m] += dataset[*j][m];
		}
		for (int m = 0; m < dim; m++)
			temp[m] /= new_cluster_ID[i].size();
		new_center[i] = temp;
	}
}

void kmeans::apply()
{
	generate_data();
	init();
	new_center.resize(k);
	new_cluster_ID.resize(k);
	assign();
	update_centers();
	iter = 0;
	while (!isfinish())
	{
		center = new_center;
		cluster_ID = new_cluster_ID;
		new_center.clear();
		new_center.resize(k);
		new_cluster_ID.clear();
		new_cluster_ID.resize(k);
		assign();
		update_centers();
		iter++;
	}
	show_result();
}

void kmeans::show_result()
{
	int num = 0;
	for (int i = 0; i < k; i++)
	{
		char string[100];
		sprintf(string, "第个%d簇:", i);
		cout << string << endl;
		cout << "中心为 (" << center[i][0] << "," << center[i][1] << ")" << endl;
		for (set<int>::iterator it = cluster_ID[i].begin(); it != cluster_ID[i].end(); it++)
		{
			sprintf(string, "编号%d   ", *it);
			cout << string << "(" << dataset[*(it)][0] << "," << dataset[*(it)][1] << ")" << endl;
			num++;
		}

		cout << endl << endl;
	}

	_ASSERTE(num == dataset.size());
}

int _tmain(int argc, _TCHAR* argv[])
{
	
	kmeans km;
	km.apply();

	system("pause");


	return 0;
}


你可能感兴趣的:(kmeans)