k近邻算法c++实现

k近邻算法的c++实现,我的github地址:https://github.com/halooooJeffrey/machine-learning/blob/master/kNN/kNN.h


#ifndef _KNN_H_
#define _KNN_H_
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
#include <iterator>
#include <iostream>
using namespace std;

/************************************************************************/
/*   定义一个结构体,里面是存储的是一个vector里面元素的值和它对应所在的位置,将会在
/*   sortDistancesIndices函数中使用
/************************************************************************/
struct valueAndIndices{
	double value;
	int index;
};

/************************************************************************/
/* 是一个比较函数,比较valueAndIndices中的元素哪个最小,用在sort函数的第三个参数
/************************************************************************/
int cmp(const valueAndIndices& a, const valueAndIndices& b){
	if (a.value < b.value)
		return 1;
	else
		return 0;
}

/************************************************************************/
/* 是一份数据的类,这个类里面包含有数据和所属的分类,因为维度不确定使用vector来储存                                    
/************************************************************************/
class data{
public:
	vector<double> _data;
	char _label;
	data();
	data(const data&);
};

/************************************************************************/
/* data的默认构造函数,是取得一个样本,这里写的是手工输入,维度可以较小,在实际情况中
/* 读入数据方式会不同,这里仅作用作验证kNN算法只用,
/************************************************************************/
data::data(){
	//copy(istream_iterator<double>(cin), istream_iterator<double>(), back_inserter(_data));
	double tmp;
	cin >> _label;
	cout << "有多少维数据: ";
	int num;
	cin >> num;
	while (num--){
		cin >> tmp;
		_data.push_back(tmp);
	}
}

/************************************************************************/
/* data的拷贝构造函数                                                       
/************************************************************************/
data::data(const data& d){
	_label = d._label;
	_data = d._data;
}

/************************************************************************/
/* 定义一个训练数据集,里面有数据的集合和一个标签的集合,一份data对应一份label,在vector
/* 中的位置也是对应的。
/************************************************************************/
class trainingDataSet{
public:
	vector<data> _dataSet;
	vector<char> _labelSet;
	trainingDataSet();
};

/************************************************************************/
/* 同样这里只是一个简单的获取一个样本数据集合的方法                                                                    
/************************************************************************/
trainingDataSet::trainingDataSet(){
	cout << "有多少组数据: ";
	int num;
	cin >> num;
	while (num--){
		data tempData;
		_dataSet.push_back(tempData);
		_labelSet.push_back(tempData._label);
	}
}

/************************************************************************/
/* 定义一个kNN类,数据成员有一个测试样本,一个样本数据集合
/* 成员函数有计算距离的函数,将距离排序返回下标的函数,计算各标签的个数的函数以及最终分类函数
/* 这里没有写测试函数用来检测样本数据集中的错误率,我们当做是可以满足条件的
/************************************************************************/
class kNN{
	data _testData;
	trainingDataSet _trainingDataSet;	//为什么无法访问_trainingDataSet里的_dataSet(为私有成员)
public:
	kNN();
	kNN(data aTestData, trainingDataSet aTrainingDataSet) : _testData(aTestData), _trainingDataSet(aTrainingDataSet){}
	vector<double> distances();
	vector<int> sortDistancesIndices(vector<double>);
	map<char, int> classCount(vector<int>, int);
	char classify(const map<char, int>&);
};

/************************************************************************/
/* 计算测试样本与样本数据集合中各样本点的距离,外层的for循环是指向样本数据集合中的单个
/* 样本,即data类型,内存for循环是将测试样本和数据集中的样本对应位上求平方和(这里计算距离
/* 公式是简单的欧氏距离),开根号求得距离后压入vector中,vector中的距离元素所在位置是与
/* 数据集合中标签vector中标签的位置一一对应,这会用在sortDistancesIndices函数中
/************************************************************************/
vector<double> kNN::distances(){
	vector<double> retVec;
	double sum = 0, aDistance;
	for (vector<data>::const_iterator it1 = _trainingDataSet._dataSet.begin(); it1 != _trainingDataSet._dataSet.end(); ++it1){
		for (vector<double>::const_iterator it2 = _testData._data.begin(), it3 = it1->_data.begin(); it2 != _testData._data.end(); ++it2){
			sum += (*it2 - *it3) * (*it2 - *it3);
			++it3;
		}
		aDistance = sqrt(sum);
		retVec.push_back(aDistance);
		sum = 0;
	}
	return retVec;
}

/************************************************************************/
/* 将距离vector中的元素从小到大排序,返回的vector是排序后的元素在原矩阵中的位置。
/* 例如[3,5,2,9]运用这个函数后返回[2,0,1,3]
/************************************************************************/
vector<int> kNN::sortDistancesIndices(vector<double> vec){
	vector<int> retVec;
	vector<valueAndIndices> vecTemp;
	for (vector<double>::size_type st = 0; st != vec.size(); ++st){
		valueAndIndices v;
		v.value = vec[st];
		v.index = st;
		vecTemp.push_back(v);
	}
	sort(vecTemp.begin(), vecTemp.end(), cmp);
	for (vector<valueAndIndices>::const_iterator it = vecTemp.begin(); it != vecTemp.end(); ++it)
		retVec.push_back(it->index);
	return retVec;
}

/************************************************************************/
/* 计算前k个最近距离做属于标签的统计                                                                     */
/************************************************************************/
map<char, int> kNN::classCount(vector<int> vec, int k){
	map<char, int> retMap;
	for (vector<int>::size_type st = 0; st != k; ++st)
		++retMap[_trainingDataSet._labelSet[vec[st]]];
	return retMap;
}

/************************************************************************/
/* 分类函数返回标个数统计中有最大个数的标签,即为测试样本的标签
/************************************************************************/
char kNN::classify(const map<char, int>& m){
	char labelResult;
	int num;
	map<char, int>::const_iterator baseIt = m.begin();
	num = baseIt->second;
	labelResult = baseIt->first;
	for (map<char, int>::const_iterator it = m.begin(); it != m.end(); ++it){
		if (it->second > num){
			num = it->second;
			labelResult = it->first;
		}
	}
	return labelResult;
}

#endif // !_KNN_H_


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
K近邻算法是一种基于实例的机器学习算法,它的主要思想是通过计算样本之间的距离来确定新样本的类别。下面是一个简单的C++实现: ```c++ #include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; // 定义一个结构体表示样本 struct Sample { vector<double> features; // 样本的特征向量 int label; // 样本的类别 }; // 计算两个样本之间的欧几里得距离 double distance(const Sample& s1, const Sample& s2) { double sum = 0.0; for (int i = 0; i < s1.features.size(); i++) { sum += pow(s1.features[i] - s2.features[i], 2); } return sqrt(sum); } // K近邻算法 int knn(const vector<Sample>& samples, const Sample& query, int k) { // 计算所有样本与查询样本之间的距离 vector<pair<double, int>> distances; // 存储距离和样本下标的pair for (int i = 0; i < samples.size(); i++) { double d = distance(samples[i], query); distances.push_back(make_pair(d, i)); } // 按距离从小到大排序 sort(distances.begin(), distances.end()); // 统计前k个样本中出现最多的类别 vector<int> counts(k, 0); // 存储每个类别出现的次数 for (int i = 0; i < k; i++) { int label = samples[distances[i].second].label; counts[label]++; } // 返回出现次数最多的类别 int max_count = 0; int max_label = -1; for (int i = 0; i < counts.size(); i++) { if (counts[i] > max_count) { max_count = counts[i]; max_label = i; } } return max_label; } int main() { // 构造样本数据 vector<Sample> samples = { {{1.0, 2.0}, 0}, {{2.0, 1.0}, 0}, {{4.0, 5.0}, 1}, {{5.0, 4.0}, 1} }; // 构造查询样本 Sample query = {{3.0, 3.0}, -1}; // 进行K近邻分类 int k = 3; int label = knn(samples, query, k); // 输出结果 cout << "query sample belongs to class " << label << endl; return 0; } ``` 这个实现中,我们首先定义了一个`Sample`结构体表示样本,其中包含了样本的特征向量和类别。然后,我们定义了一个`distance`函数来计算两个样本之间的欧几里得距离。接着,我们实现了一个`knn`函数来进行K近邻分类,其中,我们首先计算所有样本与查询样本之间的距离,并按距离从小到大排序。然后,我们统计前k个样本中出现最多的类别,并返回出现次数最多的类别。最后,我们在`main`函数中构造了样本数据和查询样本,并调用`knn`函数进行分类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值