信息检索——k-means算法

信息检索之扁平聚类的k-means聚类算法的c++实现

  • k-means算法:
  • 1.选取任意的初始二维点为初始的质心,数量为簇数。
  • 2.计算所有的二维点到质心的欧几里得距离,将距离短的二维点归为一类,形成初始簇类。
  • 3.迭代:1.更新质心;2.清空簇类容器;3.计算所有二维点到新质心的欧氏距离,选择距离进的加入相应的簇类容器;4.通过计算该簇类的平方误差判断是否继续迭代。
  • 实现的功能:随机产生50组二维随机点进行聚类。
  • 代码如下
#include<iostream>
#include<vector>
#include<math.h>
#define k 3  //聚类数,可在kmeans算法处作参数
#define random(x) rand()%(x)//产生随机数
#include <ctime>
int const N = 50;
using namespace std;
struct Tuple  //数据结构
{
	float attr1;//可看作x
	float attr2;//可看作y
};

float Dist(Tuple t1,Tuple t2)//欧式距离
{
	return sqrt((t1.attr1 - t2.attr1)*(t1.attr1 - t2.attr1) + (t1.attr2 - t2.attr2)*(t1.attr2 - t2.attr2));
}

//决定该样本属于哪一个聚类,传入的是聚类的质心(也是一个组,看作x,y)和一个样本,返回的是label;
int clusterofTuple(Tuple means[],Tuple tuple)
{
	float distance = Dist(means[0],tuple);
	int label = 0;
	for (int i = 0; i < k; i++)
	{
		if (Dist(means[i],tuple)<distance)
		{
			label = i;
		}
	}
	return label; //找最近质心

}

//获得蔟集的平方误差,用来判断是否还需要继续迭代,传入的是蔟集的质心,以及所有归类好的样本,装着每个蔟集的容器数组,计算该聚类到自己质心的距离,所有距离的加和,返回所有的平方误差
float getVar(Tuple means[],vector<Tuple> cluster[])
{
	float var = 0;
	for (int i = 0; i < k; i++)
	{
		vector<Tuple> t = cluster[i];
		for (int j = 0; j < t.size(); j++)
		{
			var += Dist(means[i], t[j]);
		}
	}
	return var;
}

float ads(float oldvar,float newvar){  //计算平方差变化
	float p;
	p=newvar-oldvar;
	return p;

}
//计算当前蔟集的质心,输入的是一个蔟集的容器,质心的计算就是对于两个属性累加后除以个数求平均,然后返回质心,所以也要初始化一个质心Tuple t
Tuple getMeans(vector<Tuple> cluster)
{
	Tuple t;
	int num = cluster.size();
	float meanX = 0, meanY = 0;
	for (int i = 0; i < num; i++)
	{
		meanX += cluster[i].attr1;
		meanY += cluster[i].attr2;
	}
	t.attr1 = meanX / num;
	t.attr2 = meanY / num;
	return t;

}

void Kmeans(vector<Tuple> tuples)  //kmeans算法
{    //定义与初始化
    //首先是要定义一个放置分好的蔟,那就是容器组咯,一个容器放一个蔟
    //然后还要有放k个质心的数组
	vector<Tuple> cluster[k];//容器组
	Tuple means[k];//放k个质心的数组
	//首先设置默认的质心,就是每个组分别是所有tuples里面最前面三个;
	for(int i = 0; i < k; i++)
	{
		means[i].attr1 = tuples[i].attr1;
		means[i].attr2 = tuples[i].attr2;
	}

	//第一次计算距离,进行分类,得到第一次的类标,容器的话是直接用push_back放置进去
	int label = 0;
	for (int i = 0; i < tuples.size(); i++)
	{
		label = clusterofTuple(means, tuples[i]);
		cluster[label].push_back(tuples[i]);

	}

    //输出刚开始的蔟
    cout<<"默认以数据集前三个坐标作为初始质心,初始的簇为:"<<endl;
	for (int i = 0; i < k; i++)
	{
        cout<<endl;
		cout << "第" << i+1 <<"个簇:"<< endl;
		vector<Tuple> t = cluster[i];
		for (int j = 0; j<t.size(); j++)
		{
			cout <<"("<< t[j].attr1 << "," << t[j].attr2 << ")" << endl;
		}
	}

	float oldvar = 0;//上一轮平方差
    float newvar = getVar(means,cluster);
	//循环迭代
		while (ads(oldvar,newvar)>5)  //结束条件,可修改
		{
			//1先计算新的k个质心
			for (int i = 0; i < k; i++)
			{
				means[i] = getMeans(cluster[i]);
			}
			//2清空分号蔟的容器,待会才可以根据新的质心重新分配
			for (int i = 0; i < k; i++)
			{
				cluster[i].clear();
			}
			//3根据新的质心,对于原来传入的所有数据重新分配
			for (int i = 0; i < tuples.size(); i++)
			{
				label = clusterofTuple(means, tuples[i]);
				cluster[label].push_back(tuples[i]);
			}
			//4最后输出
			    cout<<endl<<"----------------------------------------------"<<endl;
			    cout<<"迭代后:"<<endl;
			for (int i = 0; i < k; i++)
			{
                cout<<endl;
			    cout<<"第"<<i+1<<"个簇:"<<endl;
				vector<Tuple> t = cluster[i];
				for (int j = 0; j < t.size(); j++)
				{
					cout <<"("<< t[j].attr1 <<","<< t[j].attr2<<")" << endl;
				}
			}
				exit(1);
		}

}

vector<Tuple> input(vector<Tuple> &tuples){  //数据输入,可改读入
	srand((int)time(0));
	Tuple tuple;
	for (int i=0;i<N;i++){
        tuple.attr1=random(100);
        tuple.attr2=random(100);
        tuples.push_back(tuple);
	}

}

int main()
{
    vector<Tuple> tuples;
	input(tuples);
	Kmeans(tuples);
	return 0;
}


运行截图
在这里插入图片描述
在这里插入图片描述

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值