/*
K-中心点算法思想: 将n个样本分成k个聚类,每个聚类里的样本关联性(相似性)较强。
假如我现在有5个样本,分别是A,B,C,D,E,他们是二维向量。第一步是随机选择其中两个样本,就选A和B好了,并称他们为中心点,其他的称为非中心点,即C,D,E为非中心点。
第二步,将非中心点归类到距离它最近的那个中心点的聚类。比如现在C归到A的聚类中,D归到B的聚类中,E归到A的聚类中,此时A,C,E是一个聚类,B,D是一个聚类。并计算总的代价(以距离来算)。第三步,用非中心点替换中心点,比如用C替换A,C变成了中心点,A变成了非中心点,然后再计算与之前的代价之差,如果小于0则可替换,否则不可替换。一直迭代下去,直到
所有的中心点都没有发生替换。
*/
#include<cstdio>
#include<cstdlib>
#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;
PNode r;
};
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 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++){
tmp='A'+i;
cout<<tmp;
for(j=0;j<5;j++) cout<<" "<<linjiebiao[i][j];
cout<<endl;
}
cout<<"========================================"<<endl;
}
void arbStart(struct mem memlist[5]){ //随机选2个点作为中心点
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]){
//求解j,i,k中心店中较近的那个点的距离
if(linjiebiao[j][i]<linjiebiao[j][k]) return linjiebiao[j][i];
else return linjiebiao[j][k];
}
double TC(int index[2],int i,int h,double linjiebiao[5][5]){
int j;
double sum=0;
int tmp;
if(i==index[0]) tmp=index[1];
else if(i==index[1]) tmp=index[0];
for(j=0;j<5;j++){
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 showQueue(PLinkQueue pq){
cout<<"{";
while(!isEmptyQueue_link(pq)){
deQueue_link(pq);
cout<<",";
}
cout<<'';
cout<<"}"<<endl;
}
void reposition(mem memlist[5],double linjiebiao[5][5]){
//重新调整位置
int count,count1,h,i,k,holdi,holdh,index[2];
double tempdif;
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++){
if(memlist[h].isMedoid==false&&memlist[i].isMedoid==true){
double dif=TC(index,i,h,linjiebiao);
if(count==0){
tempdif=dif;
holdi=i;
holdh=h;
count++;
}
else if(dif<tempdif){
tempdif=dif;
holdi=i;
holdh=h;
count++;
}
}
}
}
if(tempdif<0){ //小于0可替换
tmp=memlist[holdi].isMedoid;
memlist[holdi].isMedoid=memlist[holdh].isMedoid;
memlist[holdh].isMedoid=tmp;
}
else if(tempdif>=0) break;
}while(1);
}
int main(){
int i,h,count; //i指代当前中心点,h指代可替换非中心点
int index[2]; //保存簇中心点
PLinkQueue pq[2];
pq[0]=createEmptyQueue_link(); //两个队列
pq[1]=createEmptyQueue_link();
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);
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;
reposition(memlist,linjiebiao);
cout<<endl<<endl<<endl;
cout<<"经过K-中心点算法处理后的中心点分布情况:"<<endl;
for(k=0;k<5;k++) cout<<memlist[k].symbol<<" "<<memlist[k].isMedoid<<endl;
cout<<endl<<endl<<endl;
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]);
return 0;
}