Clion 编写 注意问题 :动态内存
C均值聚类算法前提 :有178个数据样本 ,每个样本前面有一个序号,所以每个样本是14维,有13维特征,第一维是序号
C均值聚类算法步骤:
1:首先确定要分的类别数,本例中初始化分3类,自己随机确定初始三类样本中心。
2:计算每个样本与三类中心的欧氏距离,就近归类。
3:计算每类的样本均值,将其确定为新的样本中心。
4:与上次样本中心比较是否一致(收敛),收敛则结束,不收敛返回步骤2。
一:C-MEANS
1.算法程序( c++):
C_means.h文件:
//
#ifndef UNTITLED_C_MEANS_H
#define UNTITLED_C_MEANS_H
#define DATANUM 178 //178个数据样本
#define DATAATTRIBUTE 13 //每个样本里有13个属性
#include <string>
#include <sstream>
#include <vector>
/*****************************************************************
* @ClassName : C_means
* @Function : C均值聚类
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午1:01 2020/4/3*****/
class C_means {
private:
float classData[DATANUM][DATAATTRIBUTE+1]; //文件读取数据 加一是因为第0个数据不是属性
int classNum; //类别数
std::string fileName; //读取的文件名
float **last; //上次类中心
float **now; //本次类中心
std::vector<int> *a; //容器储存每次归属不同类的数据样本下标
int count; //递归次数
public:
C_means( std::string _fileName = "wine.txt",int _classNum = 3);
int getClassNum(){ return classNum;}
void openFile();
void dealData(std::string , float *a,const char flag = ',');
float Dist(float *a, float *b);
bool compare(float **a,float **b);
void test();
float** combine(int * );
~C_means();
};
C_means.cpp文件:
#include <cstring>
#include "C_means.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <math.h>
using namespace std;
/*****************************************************************
* @Fun_Name : C_means::C_means( const std::string _fileName, int _classNum)
* @Function : 构造函数
* @Parameter : 文件名 分类数
* @Return :
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午1:02 2020/4/3*****/
C_means::C_means( const std::string _fileName, int _classNum)
{
classNum = _classNum;
fileName = _fileName;
now = new float* [classNum];
last = new float* [classNum];
for (int k = 0; k < classNum; ++k)
{
last[k] = new float[DATAATTRIBUTE+1];
now[k] = new float[DATAATTRIBUTE+1];
}
for (int i = 0; i < classNum; ++i) {
for (int j = 0; j < DATAATTRIBUTE + 1; ++j) {
last[i][j] = 0;
now[i][j] = 0;
}
}
a = new vector<int>[classNum];
count = 0;
}
/*****************************************************************
* @Fun_Name : C_means::~C_means()
* @Function : 析构函数释放内存
* @Parameter :
* @Return :
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午11:21 2020/4/5*****/
C_means::~C_means()
{
for(int i=0;i<classNum;i++)
{
delete[] now[i];
delete[] last[i];
}
delete []now;
delete []last;
delete []a;
}
/*****************************************************************
* @Fun_Name : void C_means::openFile()
* @Function : 将文件中的数据读取到数组里
* @Parameter :
* @Return :
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午2:00 2020/4/3*****/
void C_means::openFile()
{
ifstream inFile;
inFile.open(fileName);
int count = 0;
string data;
if(inFile.is_open())
{
while(inFile.good())
{
getline(inFile,data);
dealData(data,classData[count]);
count++;
}
inFile.close();
}
else
cout << " open fail "<<endl;
}
/*****************************************************************
* @Fun_Name : void C_means::dealData(const std::string s, float *a, const char flag)
* @Function : 将每一行数据分割开读到数组里
* @Parameter : s字符串 a接收分离后数字数组指针 flag分割标记符
* @Return :
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午4:17 2020/4/3*****/
void C_means::dealData(const string s, float *a, const char flag)
{
istringstream iss(s);
string temp;
int i = 0;
while (getline(iss, temp, flag) && i < DATAATTRIBUTE+1) {
a[i] = stod(temp);
i++;
}
return;
}
/*****************************************************************
* @Fun_Name : float C_means::Dist(float *a, float *b)
* @Function : 计算两个样本之间的欧氏距离
* @Parameter : 两个样本指针
* @Return : 两个样本之间的欧氏距离
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午5:15 2020/4/3*****/
float C_means::Dist(float *a, float *b)
{
float sum = 0;
for (int i = 1; i < DATAATTRIBUTE+1 ; ++i) {
sum += ((a[i]-b[i])*(a[i]-b[i]));
}
return sqrtf(sum);
}
/*****************************************************************
* @Fun_Name : bool C_means::compare(float a[][DATAATTRIBUTE+1],float b[][DATAATTRIBUTE+1])
* @Function : 比较函数 用于比较 两次聚类中心是否一致
* @Parameter : 两次类中心指针
* @Return : 一致返回1,有一个不一样返回0
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午1:30 2020/4/4*****/
bool C_means::compare(float **a,float **b)
{
for (int i = 0; i < classNum; ++i)
{
for (int j = 1; j < DATAATTRIBUTE+1; ++j)
{
if(a[i][j] != b[i][j])
return false;
}
}
return true;
}
/*****************************************************************
* @Fun_Name : float** C_means::combine(int * classnum)
* @Function : 将任意组选好的初始类心归并到一个二维数组
* @Parameter : 一维数组 包含选定初始类心数组下标
* @Return : 初始类心归二维数组指针
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午9:39 2020/4/5*****/
float** C_means::combine(int * classnum)
{
for (int i = 0; i < classNum; ++i)
{
for (int j = 0; j < DATAATTRIBUTE+1; ++j)
{
now[i][j] = classData[classnum[i]][j];
}
// cout <<endl;
}
return now;
}
/*****************************************************************
* @Fun_Name : void C_means::test(float **now)
* @Function : c均值核心算法
* @Parameter : 传入一个初始类心
* @Return : 无
* @Creed : Talk is cheap , show me the code
***********************xieqinyu creates in 下午1:39 2020/4/4*****/
void C_means::test()
{
if(!compare(now,last))
{
/*********************** 计算递归次数*****************************/
count++;
/***********************清空容器为本次计算做准备*******************/
for (int l = 0; l < classNum; ++l)//分的类数
{
a[l].clear();
}
/************************得到此次分类下标结果*********************/
for (int i = 0; i < DATANUM; ++i) //一共有DATANUM样本要划分到classNum个类
{
float min;
int k;
for (int j = 0; j < classNum; ++j) //每个样本分别和三个类心作比较 根据距离分配归类
{
if (j == 0)
{
min = Dist(now[j], classData[i]);
k = j;
}
else
{
if (min > Dist(now[j], classData[i]))
{
min = Dist(now[j], classData[i]);
k = j;
}
}
}
a[k].push_back(i); //将第i组数据的下标 i 藏入第 k 动态容器中
}
/*********************将上次结果给上上次 释放last********************/
//释放last
for(int i=0;i<classNum;i++)
{
delete[] last[i];
}
delete []last;
last = now;
/***************************将下标结果转换为此次类心*****************/
//开辟新内存
float **result1 = new float* [classNum];
for (int k = 0; k < classNum; ++k)
{
result1[k] = new float[DATAATTRIBUTE+1];
}
for (int i = 0; i < classNum; ++i) {
for (int j = 0; j < DATAATTRIBUTE + 1; ++j) {
result1[i][j] = 0;
}
}
//计算本次类心
for (int l = 0; l < classNum; ++l)//分的类数
{
for (int j = 1; j < DATAATTRIBUTE+1; ++j)//数据维数
{
for (int i = 0; i <a[l].size() ; ++i)//容器中的下表数量
{
result1[l][j] +=classData[a[l][i]][j];
}
result1[l][j] /= a[l].size();
}
}
/**************************更新本次结果***************************/
now = result1;
/****************************递归*******************************/
test(); //递归
}
else//如果相等了
{
/****************************输出*******************************/
cout << count <<"次后找到类心"<<endl;
for (int j = 0; j < classNum; ++j)
{
cout << "类心" << j << ": ";
for (int i = 1; i < DATAATTRIBUTE + 1; ++i)
{
cout << now[j][i] << " , ";
}
cout << endl;
}
int sum=0;
for (int j = 0; j < classNum; ++j)
{
cout <<"类心"<<j<<"所属数组下标; ";
for (int i = 0; i < a[j].size(); ++i)
{
cout << a[j][i] << " , ";
for (int k = 1; k < DATAATTRIBUTE+1; ++k) {
sum+=((classData[a[j][i]][k]-now[j][k])*(classData[a[j][i]][k]-now[j][k]));
}
}
cout << endl;
}
cout<<"类内距离平方和:"<<endl;
<<endl;
}
}
main文件:
int main()
{
/*******************************************************
* 模式识别大作业
*******************************************************/
C_means kk("wine.txt", 3);//定义源文件 和 类别数
kk.openFile();
int num = kk.getClassNum();
int classInit[num];
int i = 0;
cout << "请初始化" << num << "组聚类中心(输入0-177任意"
<< num << "个数,不能重复): " << endl;
while (i < num && cin >> classInit[i])
i++;
cout << "成功将第";
for (int j = 0; j < num; ++j) {
cout << classInit[j] << " ";
}
cout << "组设为初始聚类中心" << endl;
kk.combine(classInit);
kk.test();
/*******************************************************
* 模式识别大作业
*******************************************************/
return 0;
}