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;
}