DM&ML_note.6-K-中心点聚类算法

来源:互联网 发布:linux rpm文件 编辑:程序博客网 时间:2024/06/05 02:24

这个学期要学DM&ML,用的是《数据挖掘算法原理与实现》王振武 本着造福同学的思想,开一个DM&ML的笔记系列,打算给书上的源代码添加一点注释,方便阅读和理解


    • 前置知识要求
    • 具体实现
    • 感想

前置知识要求

C++,队列

具体实现

#include "iostream"#include "time.h"using namespace std;struct mem             //成员结构体包含符号和一个表示是否是中心点的属性{    bool isMedoid;    char symbol;};struct Node;           //队列节点typedef struct Node * PNode;    //队列节点指针struct Node           //队列节点结构体{    mem info;    PNode link;};struct LinkQueue{   //队列数据结构     PNode f;//hiro:队列头    PNode r;//hiro:队列尾};typedef struct LinkQueue * PLinkQueue;         //队列指针PLinkQueue createEmptyQueue_link()     //创建空队列函数{    PLinkQueue plqu;    plqu=(PLinkQueue)malloc(sizeof(struct LinkQueue));    if(plqu!=NULL)    {        plqu->f=NULL;        plqu->r=NULL;    }    else        cout<<"Out of space!"<<endl;    return plqu;}int isEmptyQueue_link(PLinkQueue plqu)    //判断队列是否为空函数{    return (plqu->f==NULL);}void enQueue_Link(PLinkQueue plqu, mem x)       //元素入队函数{    PNode p;    p=(PNode)malloc(sizeof(struct Node));    if(p==NULL)cout<<"Out of space!"<<endl;    else    {        p->info=x;        p->link=NULL;        if(plqu->f==NULL) plqu->f=p;        else plqu->r->link=p;        plqu->r=p;    }}void deQueue_link(PLinkQueue plqu)          //队列元素出队并打印函数{    PNode p;    if(plqu->f==NULL)cout<<"Empty Queue"<<endl;    else    {        p=plqu->f;        cout<<p->info.symbol;        plqu->f=p->link;        free(p);    }}void showQueue(PLinkQueue pq)       //打印出队列并释放队列占用内存空间{    cout << "{";    while (!isEmptyQueue_link(pq))    {        deQueue_link(pq);        cout << ",";    }    cout << '\b';    cout << "}" << endl;}/*hiro:以上所有函数,结构体为队列数据结构及相关操作的实现,可以跳过阅读。完全可以用stl代替,不过也写得没什么问题*//*hiro:真的就只是打印,可跳过*/void showCase(double linjiebiao[5][5])       //打印邻接矩阵函数{    int i,j;    char tmp;    cout<<"目前的邻接矩阵形式如下:"<<endl;    cout<<"=========================================="<<endl;    cout<<"       A      B      C      D      E     "<<endl;    for(i=0;i<5;i++)    {        switch (i)        {        case 0: tmp='A';break;        case 1: tmp='B';break;        case 2: tmp='C';break;        case 3: tmp='D';break;        case 4: tmp='E';break;        }        cout<<tmp;        for(j=0;j<5;j++)            cout<<"      "<<linjiebiao[i][j];        cout<<endl;    }    cout<<"=========================================="<<endl;}/*hiro:真的就只是生成了两个不想等的随机数然后赋值。。。*/void arbStart(struct mem memlist[5])    //起初时随机确定两个为中心点{    int i,j;    for(i=0;i<5;i++)        if(memlist[i].isMedoid!=false)            memlist[i].isMedoid=false;    srand((unsigned)time(NULL));    i = rand()%5;    memlist[i].isMedoid=true;    j= rand()%5;    while(j==i)    {        j= rand()%5;    }    memlist[j].isMedoid=true;}double distance(int j, int i, int k, double linjiebiao[5][5])  //求解点ji、k两个中心点中较近的那个点的距离,参考邻接矩阵linjiebiao {    if(linjiebiao[j][i]<linjiebiao[j][k])    return linjiebiao[j][i];    else    return linjiebiao[j][k];}//求中心点i和h交换后 距离代价TC  的变化值double TC(int index[2],int i,int h, double linjiebiao[5][5])   {    int j;    double sum=0;    int tmp;/*hiro:tmp==另一个中心点对应的下标*/    if(i==index[0])        tmp=index[1];    else if(i==index[1])        tmp=index[0];    for(j=0;j<5;j++)    {        /*hiro:        distance(j,h,tmp,linjiebiao)替换后的距离        distance(j, index[0], index[1],linjiebiao)原本的距离        这一条公式直接囊括了四种情况,值得研究研究的。        */        sum+=distance(j,h,tmp,linjiebiao)-distance(j, index[0], index[1],linjiebiao);    }    return sum;}int smallest_distance_index(int index[2],int h,double linjiebiao[5][5])  //判断点h属于那个中心点以便形成簇{    int i,result=index[0];    for(i=0;i<2;i++)        if(linjiebiao[index[i]][h]<linjiebiao[index[0]][h])            result=index[i];        return result;}void reposition(mem memlist[5], double linjiebiao[5][5])        //PAM算法关键函数,是该算法的核心体现{    int count,count1,h,i,k,holdi,holdh,index[2];    double tempdif;/*hiro:记录最小总代价*/    bool tmp;    do    {//------------------------每次训话计算出更新后的两个中心点的序号        count1=0;        for(k=0;k<5;k++)        {            if(memlist[k].isMedoid==true)            {                index[count1]=k;                count1++;            }        }//-------------------------        count=0;        for(h=0;h<5;h++)        {            for(i=0;i<5;i++)            {/*hiro:遍历所有Oh非代表&&Oi为代表的组合*/                if(memlist[h].isMedoid==false&&memlist[i].isMedoid==true)                {                    if(count==0)                    {                        tempdif=TC(index,i,h,linjiebiao);                        holdi=i;                        holdh=h;                        count++;                    }                    else if (TC(index,i,h,linjiebiao)<tempdif)                    {/*不断比较取记录最小的TC相应的值和下标*/                        tempdif=TC(index,i,h,linjiebiao);                        holdi=i;                        holdh=h;                        count++;                    }                }            }        }        if (tempdif<0)/*hiro:P173:当最小总代价为正,可以认为认为算法结束*/        {            /*hiro:交换原中心点和新中心点的信息*/            tmp=memlist[holdi].isMedoid;            memlist[holdi].isMedoid=memlist[holdh].isMedoid;            memlist[holdh].isMedoid=tmp;        }        else if(tempdif>=0)            break;        //-----------------------test--------//      if(test==1)//          cout<<"Yes"<<endl;        //-------------------------test----------    }    while(1);}void main()               //主函数,提供邻接矩阵,出示成员集合等PAM算法需要的输入项{    int i,h,count;    int index[2];                  //用来存储为中心点的两个点的索引    PLinkQueue pq[2];             //预备两个队列用以存储表示两个簇    pq[0]=createEmptyQueue_link();      //队列0的创建    pq[1]=createEmptyQueue_link();          //队列1的创建    double linjiebiao[5][5]={{0,1,2,2,3},{1,0,2,4,3},{2,2,0,1,5},{2,4,1,0,3},{3,3,5,3,0}};  //初始化邻接矩阵    struct mem memlist[5]={{false,'A'},{false,'B'},{false,'C'},{false,'D'},{false,'E'}};   //初始化成员集合    showCase(linjiebiao);/*hiro:可注释*/    cout<<"期望得到的簇的数目暂定为 2 为例。"<<endl;    cout<<endl<<endl<<endl;    arbStart(memlist);            //随意确定两个点作为中心点    cout<<"初始化后的中心点分布情况:"<<endl;    int k;    for(k=0;k<5;k++)        cout<<memlist[k].symbol<<"       "<<memlist[k].isMedoid<<endl;    /*hiro:数据初始化完毕,开跑*/    reposition(memlist,linjiebiao);           //PAM算法处理    cout<<endl<<endl<<endl;    cout<<"经过PAM算法处理后的中心点分布情况:"<<endl;    for(k=0;k<5;k++)        cout<<memlist[k].symbol<<"       "<<memlist[k].isMedoid<<endl;    cout<<endl<<endl<<endl;    /*hiro:得到两个中心点以后开始处理两个簇*/    count=0;    for(i=0;i<5;i++)    {        if(memlist[i].isMedoid==true)        {//          cout<<memlist[i].symbol<<"是最终得到的中心点"<<endl;            enQueue_Link(pq[count], memlist[i]);            index[count]=i;            count++;        }    }    for(h=0;h<5;h++)    {        if(memlist[h].isMedoid==false)        {            if(smallest_distance_index(index,h,linjiebiao)==index[0])                enQueue_Link(pq[0], memlist[h]);            else if(smallest_distance_index(index,h,linjiebiao)==index[1])                enQueue_Link(pq[1], memlist[h]);        }    }    cout<<"以以上两个中心点为中心的两个簇为:"<<endl;    showQueue(pq[0]);    showQueue(pq[1]);}

感想

额虽然应该可能大概没有在等更的同学,但是最近的确精力放在了其他地方,课程也差不多跟上了我之前的进度了,于是回来把剩下的两个算法给写了。。。嗯,我才不是懒更,只是在鼓捣别的【逃】
这个算法虽然简单,但是时间复杂度好高啊。。。。。
先不说一开始就要准备N^2(无论空间上还是时间上)的距离数据,在一次迭代当中,跑的好像还是N^2啊…,不过还好可以并发解决性能问题。
这份源码怎么说呢。。。其实用STL替代自己实现的队列可以少一堆代码,,然后还有各种各样的小地方看起来都很冗余,可以再精简很多的。。
回到算法本身来讲本质上和之前的K-means思路是很像的,只是一些距离衡量和中心点的选取上决策不一样了。

0 0
原创粉丝点击