KMeans演算法的實現
阿新 • • 發佈:2019-01-07
//咱走的不是流量,走的是心
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<fstream>
#include<cstdio>
using namespace std;
#define Length 5 //資料維數(每個資料集,5維向量)
#define K 5 //類別數(分為5類)
#define Psize 50 //資料集總量
#define T 100 //根據實驗要求選擇迭代次數
#define End 0.00001 //結束條件
typedef struct
{
double p[Length];//儲存資料向量
double distance[K];//儲存距離不同類中心的距離
}Point;
typedef struct
{
Point clu_cent[K];//簇類中心
int cluster[K][Psize];//記錄屬於K簇的樣本編號
int cluster_num[K];//記錄屬於K簇的樣個個數
////data與old_data為了計算聚類之前和聚類之後的變化,如果變化小,則停止聚類,說明比較收斂
double data;
double old_data;
}Cluster_Center;
Point all_data[Psize];//資料大小
Cluster_Center sample;
//自動生成樣本資料
void rand_data()
{
int sum=50;
srand(time(0));
ofstream outfile,fout;
outfile.open("G:\\test.data");
int s[5];
while(sum)
{
for(int i=0;i<5;i++)
s[i]=rand()%100;
outfile<<s[0]<<" "<<s[1]<<" "<<s[2]<<" "<<s[3]<<" "<<s[4]<<endl;//將輸出寫入到檔案中,所有cout的函式都可以用
sum--;
}
outfile.close();
}
//將樣本資料讀入記憶體
void input_data()
{
FILE *infile;
int i,j;
double data;
if((infile=fopen("G:\\test.data","r"))==NULL)
{
cout<<"沒有test.data這個檔案,無法匯入資料"<<endl;
exit(1);
}
for(i=0;i<Psize;i++)//資料集的大小
{
for(j=0;j<Length;j++)//每條資料集中的個數
{
fscanf(infile,"%lf",&data);
all_data[i].p[j]=data; //存入到all_data中去。
// cout<<data<<" ";
}
// cout<<endl;
}
fclose(infile);//關閉檔案描述符
}
//檢查資料是否相等
int Equal(int a[],int n,int b)
{
for(int i=0;i<n;i++)
if(a[i]==b) return 1;
return 0;
}
//隨機初始化聚類質心(要檢測聚類中心不能相同)
void Init_center()
{
int sum=0;
int rand_num;
int center[K];
//隨機產生三個0~Psize的數
while(sum<K)
{
rand_num=rand()%Psize;//設定中心標號
if(!Equal(center,sum,rand_num))
{
center[sum++]=rand_num;
}
}
for(int i=0;i<K;i++)//中心資料放到center中
{
for(int j=0;j<Length;j++)
{
sample.clu_cent[i].p[j]=all_data[center[i]].p[j];//重新賦值
}
}
}
//歐幾里得距離公式
double Euclidean_Distance(int a,int b)//a代表的是樣本資料,b代表的是簇中心資料
{
double square=0;
for(int i=0;i<Length;i++)
{
square+=pow((all_data[a].p[i]-sample.clu_cent[b].p[i]),2);
}
/*
for(int i=0;i<n;i++)//n維向量
{
square+=(a[i]-b[i])*(a[i]-b[i]);
}
*/
return sqrt(square);
}
//計算Psize組資料到K個質心的歐幾里得距離
void claculate_distance()
{
int i,j;
for(i=0;i<Psize;i++)
for(j=0;j<K;j++)
{
all_data[i].distance[j]=Euclidean_Distance(i,j);//傳遞的是向量的標號
}
}
//將資料進行聚類
void cluster()
{
double minV;
for(int i=0;i<K;i++) //初始化每個簇中心中的資料為0
sample.cluster_num[i]=0;
for(int i=0;i<Psize;i++)
{
int index=0;
minV=all_data[i].distance[0];
for(int j=1;j<K;j++)//篩選到簇心歐幾里得最小的
{
if(all_data[i].distance[j]<minV)
{
minV=all_data[i].distance[j];
index=j;
}
}
//劃分簇集:記錄每個簇中樣本的總個數
//sample.cluster_num[index]儲存的是該類總的樣本的當前總數
sample.cluster[index][sample.cluster_num[index]++]=i;
}
double tem=0;
//計算樣本誤差和
for(int i = 0; i < K; i++)
for(int j = 0; j < sample.cluster_num[i]; j++){
tem +=pow(all_data[sample.cluster[i][j]].distance[i],2);
}
sample.old_data = sample.data;
sample.data =tem;
}
//重新計算類中心,按照列進行計算
void new_center()
{
int i, j, n;
double tmp_sum;
for(i = 0; i < K; i++)//簇中心
for(j = 0; j < Length; j++)//向量維數
{
tmp_sum = 0;
for(n = 0; n < sample.cluster_num[i]; n++) //第i個簇的第j維數的所有資料和&&求平均值
{
//sample.cluster[i][n]儲存的是樣本的編號
tmp_sum += all_data[sample.cluster[i][n]].p[j];
}
//取平均數得到新的簇中心
sample.clu_cent[i].p[j] = tmp_sum / sample.cluster_num[i];
}
}
//輸出實驗結果
void output()
{
for(int i=0;i<K;i++)
{
cout<<"第"<<i<<"類為:"<<endl;
for(int k=0;k<sample.cluster_num[i];k++)//sample.cluster_num[i]記錄的是該類中,樣本量的個數
{
for(int j=0;j<Length;j++)//Length每條樣本長度
{
//sample.cluster[i][k]記錄樣本標號
cout<<all_data[sample.cluster[i][k]].p[j]<<" ";
}
cout<<endl;
}
}
}
int main()
{
int iteration;
double differ = 1;
int flag = 0;
rand_data();//生成樣本資料
input_data();//將樣本輸入讀入記憶體
Init_center();//初始化類中心
iteration=0;//迭代標記,T=100,即迭代次數為100,可自己設定
while(1)
{
claculate_distance(); //計算歐幾里德距離
cluster(); //根據距離聚類
differ = sample.old_data - sample.data; /* 判斷條件 */
differ = fabs(differ);
if(differ<=End||iteration>T)
break;
new_center();//重新計算類中心
}
output(); /* 聚類後顯示結果 */
return 0;
}
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<fstream>
#include<cstdio>
using namespace std;
#define Length 5 //資料維數(每個資料集,5維向量)
#define K 5 //類別數(分為5類)
#define Psize 50 //資料集總量
#define T 100 //根據實驗要求選擇迭代次數
#define End 0.00001 //結束條件
typedef struct
{
double p[Length];//儲存資料向量
double distance[K];//儲存距離不同類中心的距離
}Point;
typedef struct
{
Point clu_cent[K];//簇類中心
int cluster[K][Psize];//記錄屬於K簇的樣本編號
int cluster_num[K];//記錄屬於K簇的樣個個數
////data與old_data為了計算聚類之前和聚類之後的變化,如果變化小,則停止聚類,說明比較收斂
double data;
double old_data;
}Cluster_Center;
Point all_data[Psize];//資料大小
Cluster_Center sample;
//自動生成樣本資料
void rand_data()
{
int sum=50;
srand(time(0));
ofstream outfile,fout;
outfile.open("G:\\test.data");
int s[5];
while(sum)
{
for(int i=0;i<5;i++)
s[i]=rand()%100;
outfile<<s[0]<<" "<<s[1]<<" "<<s[2]<<" "<<s[3]<<" "<<s[4]<<endl;//將輸出寫入到檔案中,所有cout的函式都可以用
sum--;
}
outfile.close();
}
//將樣本資料讀入記憶體
void input_data()
{
FILE *infile;
int i,j;
double data;
if((infile=fopen("G:\\test.data","r"))==NULL)
{
cout<<"沒有test.data這個檔案,無法匯入資料"<<endl;
exit(1);
}
for(i=0;i<Psize;i++)//資料集的大小
{
for(j=0;j<Length;j++)//每條資料集中的個數
{
fscanf(infile,"%lf",&data);
all_data[i].p[j]=data; //存入到all_data中去。
// cout<<data<<" ";
}
// cout<<endl;
}
fclose(infile);//關閉檔案描述符
}
//檢查資料是否相等
int Equal(int a[],int n,int b)
{
for(int i=0;i<n;i++)
if(a[i]==b) return 1;
return 0;
}
//隨機初始化聚類質心(要檢測聚類中心不能相同)
void Init_center()
{
int sum=0;
int rand_num;
int center[K];
//隨機產生三個0~Psize的數
while(sum<K)
{
rand_num=rand()%Psize;//設定中心標號
if(!Equal(center,sum,rand_num))
{
center[sum++]=rand_num;
}
}
for(int i=0;i<K;i++)//中心資料放到center中
{
for(int j=0;j<Length;j++)
{
sample.clu_cent[i].p[j]=all_data[center[i]].p[j];//重新賦值
}
}
}
//歐幾里得距離公式
double Euclidean_Distance(int a,int b)//a代表的是樣本資料,b代表的是簇中心資料
{
double square=0;
for(int i=0;i<Length;i++)
{
square+=pow((all_data[a].p[i]-sample.clu_cent[b].p[i]),2);
}
/*
for(int i=0;i<n;i++)//n維向量
{
square+=(a[i]-b[i])*(a[i]-b[i]);
}
*/
return sqrt(square);
}
//計算Psize組資料到K個質心的歐幾里得距離
void claculate_distance()
{
int i,j;
for(i=0;i<Psize;i++)
for(j=0;j<K;j++)
{
all_data[i].distance[j]=Euclidean_Distance(i,j);//傳遞的是向量的標號
}
}
//將資料進行聚類
void cluster()
{
double minV;
for(int i=0;i<K;i++) //初始化每個簇中心中的資料為0
sample.cluster_num[i]=0;
for(int i=0;i<Psize;i++)
{
int index=0;
minV=all_data[i].distance[0];
for(int j=1;j<K;j++)//篩選到簇心歐幾里得最小的
{
if(all_data[i].distance[j]<minV)
{
minV=all_data[i].distance[j];
index=j;
}
}
//劃分簇集:記錄每個簇中樣本的總個數
//sample.cluster_num[index]儲存的是該類總的樣本的當前總數
sample.cluster[index][sample.cluster_num[index]++]=i;
}
double tem=0;
//計算樣本誤差和
for(int i = 0; i < K; i++)
for(int j = 0; j < sample.cluster_num[i]; j++){
tem +=pow(all_data[sample.cluster[i][j]].distance[i],2);
}
sample.old_data = sample.data;
sample.data =tem;
}
//重新計算類中心,按照列進行計算
void new_center()
{
int i, j, n;
double tmp_sum;
for(i = 0; i < K; i++)//簇中心
for(j = 0; j < Length; j++)//向量維數
{
tmp_sum = 0;
for(n = 0; n < sample.cluster_num[i]; n++) //第i個簇的第j維數的所有資料和&&求平均值
{
//sample.cluster[i][n]儲存的是樣本的編號
tmp_sum += all_data[sample.cluster[i][n]].p[j];
}
//取平均數得到新的簇中心
sample.clu_cent[i].p[j] = tmp_sum / sample.cluster_num[i];
}
}
//輸出實驗結果
void output()
{
for(int i=0;i<K;i++)
{
cout<<"第"<<i<<"類為:"<<endl;
for(int k=0;k<sample.cluster_num[i];k++)//sample.cluster_num[i]記錄的是該類中,樣本量的個數
{
for(int j=0;j<Length;j++)//Length每條樣本長度
{
//sample.cluster[i][k]記錄樣本標號
cout<<all_data[sample.cluster[i][k]].p[j]<<" ";
}
cout<<endl;
}
}
}
int main()
{
int iteration;
double differ = 1;
int flag = 0;
rand_data();//生成樣本資料
input_data();//將樣本輸入讀入記憶體
Init_center();//初始化類中心
iteration=0;//迭代標記,T=100,即迭代次數為100,可自己設定
while(1)
{
claculate_distance(); //計算歐幾里德距離
cluster(); //根據距離聚類
differ = sample.old_data - sample.data; /* 判斷條件 */
differ = fabs(differ);
if(differ<=End||iteration>T)
break;
new_center();//重新計算類中心
}
output(); /* 聚類後顯示結果 */
return 0;
}