K-Means C实现(基于iris数据)

本文转载http://blog.csdn.net/qll125596718/article/details/8243404/点击打开链接


将源代码调通后做成一个main.c文件,需要实现的同学可以直接新建win32应用台控制程序,将main.c直接拷贝,并将iris.txt数据集放到项目路径下,即可运行。

http://download.csdn.net/download/u013657981/9241121点击打开链接

该链接可以下载数据,但该数据是将3类150条数据排列好的,你可以将顺序打乱运行程序。

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <math.h>
#include <stdlib.h>
#include<time.h> 
#define k 3//簇的数目
using namespace std;
//存放元组的属性信息
typedef vector<double> Tuple;//存储每条数据记录

int dataNum;//数据集中数据记录数目
int dimNum;//每条记录的维数

//计算两个元组间的欧几里距离
double getDistXY(const Tuple& t1, const Tuple& t2) 
{
	double sum = 0;
	for(int i=1; i<=dimNum; ++i)
	{
		sum += (t1[i]-t2[i]) * (t1[i]-t2[i]);
	}
	return sqrt(sum);
}

//根据质心,决定当前元组属于哪个簇
int clusterOfTuple(Tuple means[],const Tuple& tuple){
	double dist=getDistXY(means[0],tuple);
	double tmp;
	int label=0;//标示属于哪一个簇
	for(int i=1;i<k;i++){
		tmp=getDistXY(means[i],tuple);
		if(tmp<dist) {dist=tmp;label=i;}
	}
	return label;	
}
//获得给定簇集的平方误差
double getVar(vector<Tuple> clusters[],Tuple means[]){
	double var = 0;
	for (int i = 0; i < k; i++)
	{
		vector<Tuple> t = clusters[i];
		for (int j = 0; j< t.size(); j++)
		{
			var += getDistXY(t[j],means[i]);
		}
	}
	//cout<<"sum:"<<sum<<endl;
	return var;

}
//获得当前簇的均值(质心)
Tuple getMeans(const vector<Tuple>& cluster){

	int num = cluster.size();
	Tuple t(dimNum+1, 0);
	for (int i = 0; i < num; i++)
	{
		for(int j=1; j<=dimNum; ++j)
		{
			t[j] += cluster[i][j];
		}
	}
	for(int j=1; j<=dimNum; ++j)
		t[j] /= num;
	return t;
	//cout<<"sum:"<<sum<<endl;
}

void print(const vector<Tuple> clusters[])
{
	for(int lable=0; lable<k; lable++)
	{
		cout<<"第"<<lable+1<<"个簇:"<<endl;
		vector<Tuple> t = clusters[lable];
		for(int i=0; i<t.size(); i++)
		{
			cout<<i+1<<".(";
			for(int j=0; j<=dimNum; ++j)
			{
				cout<<t[i][j]<<", ";
			}
			cout<<")\n";
		}
	}
}

void KMeans(vector<Tuple>& tuples){
	vector<Tuple> clusters[k];//k个簇
	Tuple means[k];//k个中心点
	int i=0;
	//一开始随机选取k条记录的值作为k个簇的质心(均值)
	srand((unsigned int)time(NULL));
	for(i=0;i<k;){
		int iToSelect = rand()%tuples.size();
		if(means[iToSelect].size() == 0)
		{
			for(int j=0; j<=dimNum; ++j)
			{
				means[i].push_back(tuples[iToSelect][j]);
			}
			++i;
		}
	}
	int lable=0;
	//根据默认的质心给簇赋值
	for(i=0;i!=tuples.size();++i){
		lable=clusterOfTuple(means,tuples[i]);
		clusters[lable].push_back(tuples[i]);
	}
	double oldVar=-1;
	double newVar=getVar(clusters,means);
	cout<<"初始的的整体误差平方和为:"<<newVar<<endl; 
	int t = 0;
	while(abs(newVar - oldVar) >= 1) //当新旧函数值相差不到1即准则函数值不发生明显变化时,算法终止
	{
		cout<<"第 "<<++t<<" 次迭代开始:"<<endl;
		for (i = 0; i < k; i++) //更新每个簇的中心点
		{
			means[i] = getMeans(clusters[i]);
		}
		oldVar = newVar;
		newVar = getVar(clusters,means); //计算新的准则函数值
		for (i = 0; i < k; i++) //清空每个簇
		{
			clusters[i].clear();
		}
		//根据新的质心获得新的簇
		for(i=0; i!=tuples.size(); ++i){
			lable=clusterOfTuple(means,tuples[i]);
			clusters[lable].push_back(tuples[i]);
		}
		cout<<"此次迭代之后的整体误差平方和为:"<<newVar<<endl; 
	}

	cout<<"The result is:\n";
	print(clusters);
}
int main(){

	char fname[256];
	cout<<"请输入存放数据的文件名: ";
	cin>>fname;
	cout<<endl<<" 请依次输入: 维数 样本数目"<<endl;
	cout<<endl<<" 维数dimNum: ";
	cin>>dimNum;
	cout<<endl<<" 样本数目dataNum: ";
	cin>>dataNum;
	ifstream infile(fname);
	if(!infile){
		cout<<"不能打开输入的文件"<<fname<<endl;
		return 0;
	}
	vector<Tuple> tuples;
	//从文件流中读入数据
	for(int i=0; i<dataNum && !infile.eof(); ++i)
	{
		string str;
		getline(infile, str);
		istringstream istr(str);
		Tuple tuple(dimNum+1, 0);//第一个位置存放记录编号,第2到dimNum+1个位置存放实际元素
		tuple[0] = i+1;
		for(int j=1; j<=dimNum; ++j)
		{
			istr>>tuple[j];
		}
		tuples.push_back(tuple);
	}

	cout<<endl<<"开始聚类"<<endl;
	KMeans(tuples);
	return 0;
}



由于初始质心是随机选取的,每次运行可能结果不同,后期我想办法不随机选取初始质心,聚类可能更快。

这次的程序运行结果过于理想,有可能出现某一类中有另一类的数据。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值