KMeans算法的实现

//咱走的不是流量,走的是心


#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值