C++基于特征向量的KNN分类算法
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
#include "stdafx.h" #include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> #include <set> #include <map> #include <cmath> using namespace std; //样本特征结构体 struct sample { string type; vector<double> features; }; //读取训练样本train.txt,训练样本格式:类型名+特征向量 void readTrain(vector<sample>& train, const string& file) { ifstream fin(file.c_str()); //file是存储希望读写的文件名的string对象,fin是读的流 if(!fin) { cerr<<"Unable to open the input file: "<<file<<endl; exit(1); } string line; double d=0.0; while(getline(fin,line)) //fin是读入流,getline从输入流fin读入一行到line { istringstream stream(line); //bind to stream to the line we read sample ts; stream>>ts.type; while(stream>>d) //read a word from line { ts.features.push_back(d); //在trains.features的末尾添加一个值为d的元素 } train.push_back(ts); //在train的末尾添加一个值为ts的元素 } fin.close(); } //读取测试样本test.txt,每行都是一个特征向量 void readTest(vector<sample>& test, const string& file) { ifstream fin(file.c_str()); if(!fin) { cerr<<"Unable to open the input file: "<<file<<endl; exit(1); } string line; double d=0.0; while(getline(fin,line)) { istringstream stream(line); //bind to stream to the line we read sample ts; while(stream>>d) { ts.features.push_back(d); } test.push_back(ts); } fin.close(); } //输出结果,为每一个向量赋予一个类型,写入result.txt中 void writeResult(const vector<sample>& test, const string& file) { ofstream fout(file.c_str()); if(!fout) { cerr<<"Unable to write the input file: "<<endl; exit(1); } for(vector<sample>::size_type i=0;i!=test.size();++i) { fout << test[i].type << '\t'; for(vector<double>::size_type j=0;j!=test[j].features.size();++j) { fout<<test[i].features[j]<<' '; } fout<<endl; } } //KNN算法的实现 void knnProcess(vector<sample>& test, const vector<sample>& train, const vector<vector<double> >& dm, unsigned int k) { for (vector<sample>::size_type i = 0; i != test.size(); ++i) { multimap<double, string> dts; //保存与测试样本i距离最近的k个点 for (vector<double>::size_type j = 0; j != dm[i].size(); ++j) { if (dts.size() < k) //把前面k个插入dts中 { dts.insert(make_pair(dm[i][j], train[j].type)); //插入时会自动排序,按dts中的double排序,最小的排在最后 } else { multimap<double, string>::iterator it = dts.end(); --it; if (dm[i][j] < it->first) //把当前测试样本i到当前训练样本之间的欧氏距离与dts中最小距离比较,若更小就更新dts { dts.erase(it); dts.insert(make_pair(dm[i][j], train[j].type)); } } } map<string, double> tds; string type = ""; double weight = 0.0; //下面for循环主要是求出与测试样本i最邻近的k个样本点中大多数属于的类别,即将其作为测试样本点i的类别 for (multimap<double, string>::const_iterator cit = dts.begin(); cit != dts.end(); ++cit) { // 不考虑权重的情况,在 k 个样例中只要出现就加 1 // ++tds[cit->second]; // 这里是考虑距离与权重的关系,距离越大权重越小 tds[cit->second] += 1.0 / cit->first; if (tds[cit->second] > weight) { weight = tds[cit->second]; type = cit->second; //保存一下类别 } } test[i].type = type; } } // 计算欧氏距离 double euclideanDistance(const vector<double>& v1, const vector<double>& v2) { if(v1.size() != v2.size()) { cerr<<"Unable to get a distance! "<<endl; } else { double distance = 0.0; for (vector<double>::size_type i = 0; i != v1.size(); ++i) { distance += (v1[i] - v2[i]) * (v1[i] - v2[i]); } return sqrt(distance); } } /*初始化距离矩阵,该矩阵是根据训练样本和测试样本而得, 矩阵的行数为测试样本的数目,列数为训练样本的数目, 每一行为一个测试样本到各个训练样本之间的欧式距离组成的数组*/ void initDistanceMatrix(vector<vector<double> >& dm, const vector<sample>& train, const vector<sample>& test) { for (vector<sample>::size_type i = 0; i != test.size(); ++i) { vector<double> vd; for (vector<sample>::size_type j = 0; j != train.size(); ++j) { vd.push_back(euclideanDistance(test[i].features, train[j].features)); } dm.push_back(vd); } } //封装 void xfxKnn(const string& file1, const string& file2, const string& file3, int k) { vector<sample> train,test; readTrain(train, file1.c_str()); readTest(test, file2.c_str()); vector< vector<double> > dm; initDistanceMatrix(dm, train, test); knnProcess(test, train, dm, k); writeResult(test, file3.c_str()); } // 测试 int main() { xfxKnn("train.txt", "test.txt", "result.txt", 5); return 0; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
上一篇:C++实现连连看游戏核心代码
栏 目:C语言
下一篇:MFC实现连连看游戏之消子算法
本文标题:C++基于特征向量的KNN分类算法
本文地址:https://www.xiuzhanwang.com/a1/Cyuyan/546.html
您可能感兴趣的文章
- 04-02c语言没有round函数 round c语言
- 01-10深入理解C++中常见的关键字含义
- 01-10使用C++实现全排列算法的方法详解
- 01-10c++中inline的用法分析
- 01-10用C++实现DBSCAN聚类算法
- 01-10全排列算法的非递归实现与递归实现的方法(C++)
- 01-10C++大数模板(推荐)
- 01-10浅谈C/C++中的static与extern关键字的使用详解
- 01-10深入C/C++浮点数在内存中的存储方式详解
- 01-10基于atoi()与itoa()函数的内部实现方法详解
阅读排行
本栏相关
- 04-02c语言函数调用后清空内存 c语言调用
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言的正则匹配函数 c语言正则表达
- 04-02c语言用函数写分段 用c语言表示分段
- 04-02c语言中对数函数的表达式 c语言中对
- 04-02c语言编写函数冒泡排序 c语言冒泡排
- 04-02c语言没有round函数 round c语言
- 04-02c语言分段函数怎么求 用c语言求分段
- 04-02C语言中怎么打出三角函数 c语言中怎
- 04-02c语言调用函数求fibo C语言调用函数求
随机阅读
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-10delphi制作wav文件的方法
- 01-11ajax实现页面的局部加载
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-10C#中split用法实例总结
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-10SublimeText编译C开发环境设置
- 04-02jquery与jsp,用jquery