机器学习--C++实现 K-Means聚类demo

本文档展示了使用C++实现K-Means聚类算法的过程,包括选择初始化聚类中心、计算距离、更新簇中心等步骤。代码实现中,算法仅支持三维数据,采用欧氏距离作为距离度量标准。通过不断迭代,直至簇中心不再变化,得到最终的聚类结果。
摘要由CSDN通过智能技术生成

大一时了解到K-Means算法,刚好在学习C++,想着动手写一个这算法,以达到更好地学习效果,因为只支持三维数据,因此仅供学习。

算法流程参考下面文章:

(152条消息) K-means算法的基本原理_纯粹.的博客-CSDN博客_k-means聚类算法的原理

  1) K-means算法首先需要选择K个初始化聚类中心
(2) 计算每个数据对象到K个初始化聚类中心的距离(该距离可以为多种度量方式,如曼哈顿距离、欧氏距离、马氏距离,甚至你自定义的距离度量),将数据对象分到距离聚类中心最近的那个数据集中,当所有数据对象都划分以后,就形成了K个数据集(即K个簇)
(3)接下来重新计算每个簇的数据对象的均值,将均值作为新的聚类中心
(4)最后计算每个数据对象到新的K个初始化聚类中心的距离,重新划分
(5)每次划分以后,都需要重新计算初始化聚类中心,一直重复这个过程,直到所有的数据对象


看个动态图(该图来自Matplotlib输出动画实现K-means聚类过程可视化 - 知乎 (zhihu.com)):

首先看头文件

#pragma once

#ifndef K_MEANS_H
#define K_MEANS_H
#include<vector>
using namespace std;


struct Tuple
{
	float x;
	float y;
	float z;

};//这里只将聚类对象的纬度定义为三维,事实上可以为任意大于零的纬度,可以用动态数组来表示

class K_means
{
private:
	vector<Tuple> basic_data;//初始数据
	int mean_number;//k的值
	vector<vector<Tuple>> result_data;//{ mean_number };//结果数据,也用来储存中间聚类数据
	vector<Tuple> means;//中间计算储存的聚类中心
	vector<float> mea_square_error;//每个簇的均方差


public:
	K_means(vector<Tuple> basic_data, int mean_number) : result_data(mean_number), mea_square_error(mean_number)
	{
		this->basic_data = basic_data;
		this->mean_number = mean_number;
	}
    //计算最后的聚类结果
	void get_fina_result_data();

private:
	//计算两个点间的距离
	float get_distxyz(Tuple tuple1, Tuple tuple2);
	//初始化k个聚类中心
	void prime_k_meansbase();
	//初始化得到k个簇
	void get_k_means();
	//得到当前每个簇类中心
	void get_basemean();
	//得到新的簇
	void get_new_k_means();
	//得到每一个簇的方差
	void get_k_mean_erro();
	


};

实现代码如下:

#include<iostream>
#include<vector>
#include<cmath>
#include"k_means.h"
using namespace std;



float K_means::get_distxyz(Tuple tuple1, Tuple tuple2)//这里采用欧氏距离度量
{
	int distance = pow(pow((tuple1.x - tuple2.x), 2) + pow((tuple1.y -
		tuple2.y), 2) + pow((tuple1.z - tuple2.z), 2), 0.5);
	return distance;
}
//初始化k个聚类中心
void K_means::prime_k_meansbase()
{
	for (int i = 0;i != mean_number; i++)
	{
		means.push_back(basic_data[i]);
	}
}

//初始化得到k个簇
void K_means::get_k_means()
{

		
	for (int i = 0;i != basic_data.size();++i)
	{
		float min_distance = 0x3f3f3f3f;
		int static temp;
		for (int j = 0;j != mean_number;++j)
		{
			if (min_distance > get_distxyz(basic_data[i], means[j]))
			{
				min_distance = get_distxyz(basic_data[i], means[j]);
				temp = j;
				cout << temp << endl;;
			}
		}
		result_data[temp].push_back(basic_data[i]);
	}
}

//得到当前每个簇类中心
void K_means::get_basemean()
{
	vector<Tuple> A_mean;
	for (int j = 0;j < mean_number;j++)
	{
		float sumx = 0;
		float sumy = 0;
		float sumz = 0;
		int num = result_data[j].size();
		A_mean = result_data[j];
		cout << num << endl;
		for (int i = 0;i < num;i++)
		{
			sumx += A_mean[i].x;
			sumy += A_mean[i].y;
			sumz += A_mean[i].z;
		}
		Tuple basemean = { sumx / num,sumy / num,sumz / num };
		means[j] = basemean;
	}
}

//得到新的簇
void K_means::get_new_k_means()
{
	result_data.clear();//清除迁移过程结果
	result_data = vector<vector<Tuple>>(mean_number);//分配结果储存容器
	for (int i = 0;i != basic_data.size();++i)//每个点与每个簇类中心的距离进行比较
	{
		float min_distance = 0x3f3f3f3f;//表示无穷大
		int static temp2;
		for (int j = 0;j != mean_number;++j)
		{
			if (min_distance > get_distxyz(basic_data[i], means[j]))
			{
				//选距离最近的簇中心作为中心
				min_distance = get_distxyz(basic_data[i], means[j]);
				temp2 = j;
			}
		}
		result_data[temp2].push_back(basic_data[i]);
	}
}

//得到每一个簇的方差
void K_means::get_k_mean_erro()
{
	vector<float> square_error;
	for (int j = 0;j < mean_number;j++)
	{
		float sum = 0;//方差
		int num = result_data[j].size();//方差的被除数,得到均方差

		vector<Tuple> A_mean = result_data[j];
		Tuple basemean = means[j];

		for (int i = 0;i < num;i++)
		{
			sum += pow(get_distxyz(A_mean[i], basemean), 2);
		}
		float result = sum / num;
		mea_square_error[j] = result;
	}
}



//计算最后的聚类结果

void K_means::get_fina_result_data()
{
	//初始化
	prime_k_meansbase();
	get_k_means();
	get_k_mean_erro();//得到每一个簇的方差

	int i = 1;
	while (i)
	{
		i = 0;
		vector<float> last_k_mean_erro = mea_square_error;
		get_basemean();//得到当前每个簇的中心
		get_new_k_means();//得到新的聚类
		get_k_mean_erro();//得到每一个簇的方差
			
		for (int j = 0;j != mean_number;++j)
		{
			cout << mea_square_error[j] << '\t' << last_k_mean_erro[j] << endl;
			if (mea_square_error[j]!=last_k_mean_erro[j])
			{
				i = 1;
				
			}
		}
	}

	//打印分类结果
	for (int j = 0;j != mean_number;++j)
	{
		cout << "****************************************************************" << endl;
		for (vector<Tuple>::iterator m = result_data[j].begin();m != result_data[j].end();++m)
		{
			cout << (*m).x << '\t' << (*m).y << '\t' << (*m).z << endl;
		}
			
	}


}

测试代码

#include<iostream>
#include<vector>
#include"k_means.h"
using namespace std;


void test()
{
	vector<Tuple> basic_data_test;
	Tuple temp_data;
	int x = 0, y = 0, z = 0;
	int k;//分类数量
	int n;//样本数量
	cout << "请输入类别数量:" << endl;
	cin >> k;
	cout << "请输入数据量" << endl;
	cin >> n;
	for (int i = 0;i != n;++i)
	{
		cout << "请输入x,y,z:" << endl;
		cin >> x >> y >> z;
		temp_data.x = x;
		temp_data.y = y;
		temp_data.z = z;
		basic_data_test.push_back(temp_data);
	}
	K_means a(basic_data_test, k);
	a.get_fina_result_data();
}


int main()
{
	test();
}

测试:输入15组点坐标,分为三类

结果图:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值