1. 程式人生 > >模糊C均值聚類(FCM)演算法

模糊C均值聚類(FCM)演算法

本程式碼演算法用例為鳶尾花資料集合;

IOSDATA演算法實現步驟,在很多資料和論壇中都有詳細的介紹,這裡就不對演算法步驟進行陳述了。

就我程式碼中,我對下面幾個控制引數的理解:

初始聚類數:初始類聚中心,跟聚類聚中心劃分簇。

期望得到的聚類數:這個數並不是最終得到的類聚數目,可以理解為我們人為的預估可能得到的類聚數,最後的結果不一定與這個數相等。

最大迭代次數:控制程式的迭代次數,根據樣本數量大小設定,本程式碼中的最大迭代次數設定為10,但迭代到4步左右就已經類聚完成了。

其他的一些控制引數看註解或一些資料便知道其作用,這裡就不做多餘介紹。

建議先將K-MEAN聚類演算法看懂,再看IOSDATA聚類演算法,程式理解就容易多。K-MEAN演算法的程式碼和實現步驟在我之前的部落格中也有。

c語言程式碼如下:

(程式碼並不是很完美,當類聚得到的數目過少(本例為<3時)時會出現錯誤,本程式碼僅供學習參考)

#include "stdio.h"
#include "stdlib.h"
#include "time.h"
#include "math.h"
#include "vector"
using namespace std;


#define alpha 0.5   //分裂比
#define dimNum 3   //維數為3
#define cNum 8  //初始類數
#define expectNum 4  //預期成類數
#define minNum 3     //小於此數則不能成類
#define splitVar 1.2  //大於此數分裂
#define connectVar 0.3  //小於此數合併
#define connectNum 2   //每次迭代允許合併的最大數
#define iterNum 10  //迭代次數為10




typedef vector<double> doubleVector;
typedef vector<doubleVector> clusterVector;


void ISODATA(vector<doubleVector> srcInf);  //開始引擎
vector<doubleVector> getInf();  //獲取檔案資料
int getClusterNum(vector<doubleVector> mCenter, doubleVector srcInf); //歸類
double getEUCdis(doubleVector t1, doubleVector t2);  //計算歐式距離
doubleVector get_wMean(clusterVector cluster);  //計算均值向量
doubleVector averageDist(doubleVector mCenter, clusterVector cluster); //計算Wi中所有樣本距其相應聚類中心的平均距離
doubleVector allAvgDist(vector<doubleVector> avgDis); //計算所以樣本距其相應的聚類中心的平均值
doubleVector getDeviation(doubleVector mCenter, clusterVector cluster); //計算沒一聚類的標準偏差
double getMaxDev(doubleVector Dev);  //獲取最大標準偏差
doubleVector clusterSplit(doubleVector wMean, double sigma);  //進行分裂
doubleVector eachCenterDist(doubleVector t1, doubleVector t2, int i, int j);  //計算聚類中心兩兩間的距離
vector<doubleVector> distSort(vector<doubleVector> eachDist);   //按距離進行排序
vector<doubleVector> Union(vector<doubleVector> mCenter, vector<doubleVector> sortDist); //進行合併
void Output(vector<clusterVector> cluster);  //結果輸出


//主函式
void main()
{
vector<doubleVector> srcInf;
srcInf = getInf();
ISODATA(srcInf);
}




//迭代開始引擎
void ISODATA(vector<doubleVector> srcInf)
{
int c;
int i, j;
int iter=1;
int lable;
vector<doubleVector> mCenter;
vector<clusterVector> cluster;
vector<doubleVector> wMean;   //均值向量
vector<doubleVector> avgDist;
doubleVector all_AvgDis;
vector<doubleVector> devVal;  //標準偏差值
vector<doubleVector> eachDist;  //兩兩聚類中心距離
vector<doubleVector> eachDistSort;  //兩兩聚類中心距離
doubleVector devMax;   //最大偏差值
double sigma;


start:
printf("聚類開始:\n");




//初始化類聚中心
step1:   
srand(time(NULL));
int chooseNum;
if(mCenter.empty()==NULL)
mCenter.clear();


mCenter.resize(cNum);
for(i=0; i<cNum; i++)
{
chooseNum = rand()%(int)(srcInf.size()/cNum)+i*(srcInf.size()/cNum);
for(j=0; j<dimNum; j++)
mCenter[i].push_back(srcInf[chooseNum][j]);
}




//根據距離歸類
step2:  
if(cluster.empty()==NULL)
cluster.clear();


cluster.resize(2*cNum);
for(i=0; i<srcInf.size(); i++)
{
lable = getClusterNum(mCenter, srcInf[i]);
cluster[lable].push_back(srcInf[i]);
}




//捨棄過小的類
step3:
for(i=0; i<cluster.size(); )
{
if(cluster[i].size()<minNum)
{
cluster.erase(cluster.begin()+i);
if(i<mCenter.size())
mCenter.erase(mCenter.begin()+i);
}

else
i++;
}
c = mCenter.size();




//更新均值向量
step4:  
if(wMean.empty()==NULL)
wMean.clear();


for(i=0; i<cluster.size(); i++)
wMean.push_back(get_wMean(cluster[i]));



//獲取各平均距離
step5:
if(avgDist.empty()==NULL)
avgDist.clear();


for(i=0; i<cluster.size(); i++)   //計算Wi中所有樣本距其相應聚類中心的平均距離
avgDist.push_back(averageDist(mCenter[i], cluster[i]));  


all_AvgDis = allAvgDist(avgDist);  //計算所以樣本距其相應的聚類中心的平均值




//判斷分裂、合併及迭代的運算步驟
step7:
if(iter==iterNum)   //步驟(1)
{

//connectNum = 0;
goto step11;
}


if(c<=expectNum/2)//步驟(2)
goto step8;


if(iter%2==0 || c>=2*expectNum)//步驟(3)
goto step11;




//計算標準偏差
step8:
if(devVal.empty()==NULL)
devVal.clear();


for(i=0; i<mCenter.size(); i++)   //計算標準偏差
devVal.push_back(getDeviation(mCenter[i], cluster[i]));






//計算標準偏差中偏差最大值
step9:
if(devMax.empty()==NULL)
devMax.clear();


for(i=0; i<devVal.size(); i++)    //計算標準偏差中偏差最大值
devMax.push_back(getMaxDev(devVal[i]));






//進行分裂
step10:
for(i=0; i<devMax.size(); i++)
{
if((devMax[i]>splitVar && cluster[i].size()>2*minNum) || c<=expectNum/2)
{
sigma = alpha*devMax[i];
mCenter.push_back(clusterSplit(wMean[i], sigma));
mCenter.push_back(clusterSplit(wMean[i], -sigma));
mCenter.erase(mCenter.begin()+i);
}
}
c = mCenter.size();




//計算兩兩聚類間的距離
step11:
if(eachDist.empty()==NULL)
eachDist.clear();


for(i=0; i<mCenter.size()-1; i++)
for(j=i+1; j<mCenter.size(); j++)
{
eachDist.push_back(eachCenterDist(mCenter[i], mCenter[j], i, j));

}






//對距離進行排序
step12:
eachDistSort = distSort(eachDist);




//進行合併
step13:
mCenter = Union(mCenter, eachDistSort);
c = mCenter.size();




step14:
if(iter==iterNum)
{
printf("類聚結果為:\n");
Output(cluster);
system("pause");
return;
}


else
{
iter++;
goto step2;
}


}






//獲取檔案資料
vector<doubleVector> getInf()
{
int i=1;
vector<doubleVector> dst;
doubleVector temp;
double num;

FILE *fp;

fp = fopen("Inf.txt", "r");

if(fp == NULL)
printf("Open file error!\n");

//讀取檔案的資料
while(fscanf(fp, "%lf", &num)!=EOF)
{
temp.push_back(num);
if(i%3==0)
{
dst.push_back(temp);
temp.clear();
}
i++;
}

fclose(fp);

return dst;
}




//計算歐式距離
double getEUCdis(doubleVector t1, doubleVector t2)
{
double distance=0;
int i;


for(i=0; i<dimNum; i++)
distance += (t1[i]-t2[i])*(t1[i]-t2[i]);


return sqrtf(distance);
}


//根據初始中心進行歸類
int getClusterNum(vector<doubleVector> mCenter, doubleVector srcInf)
{
double Dis;
double minDis = getEUCdis(mCenter[0], srcInf);
int i, lable=0;


for(i=1; i<mCenter.size(); i++)
{
Dis = getEUCdis(mCenter[i], srcInf);
if(Dis<minDis)
{
minDis = Dis;
lable = i;
}
}


return lable;
}




//計算均值向量
doubleVector get_wMean(clusterVector cluster)
{
doubleVector wMean;
double temp[dimNum];
int i, j;


for(i=0; i<dimNum; i++)
temp[i] = 0;


for(i=0; i<cluster.size(); i++)
for(j=0; j<dimNum; j++)
temp[j] += cluster[i][j];


for(i=0; i<dimNum; i++)
temp[i] = temp[i]/cluster.size();


wMean.clear();
for(i=0; i<dimNum; i++)
wMean.push_back(temp[i]);


return wMean;
}




//計算類聚中心m[i]的平均距離
doubleVector averageDist(doubleVector mCenter, clusterVector cluster)
{
 int i, j;
 doubleVector avgDist;
double temp[dimNum]={0};


for(i=0; i<cluster.size(); i++)
for(j=0; j<dimNum; j++)
temp[j] += cluster[i][j];


for(i=0; i<dimNum; i++)
temp[i] /= cluster.size();


avgDist.clear();
for(i=0; i<dimNum; i++)
avgDist.push_back(temp[i]);


return avgDist;


}




//計算所以樣本距其相應的聚類中心的平均值
doubleVector allAvgDist(vector<doubleVector> avgDis)
{
int i, j;
doubleVector allAvg;
double temp[dimNum]={0};


for(i=0; i<avgDis.size(); i++)
for(j=0; j<dimNum; j++)
temp[j] += (i+1)*avgDis[i][j];


for(i=0; i<dimNum; i++)
temp[i] /= avgDis.size();


allAvg.clear();
for(i=0; i<dimNum; i++)
allAvg.push_back(temp[i]);


return allAvg;
}




//計算每一個聚類的標準偏差
doubleVector getDeviation(doubleVector mCenter, clusterVector cluster)
{
doubleVector dst;
int i, j;
double temp[dimNum]={0};


for(i=0; i<cluster.size(); i++)
for(j=0; j<dimNum; j++)
temp[j] += (cluster[i][j]-mCenter[j])*(cluster[i][j]-mCenter[j]);


dst.clear();
for(i=0; i<dimNum; i++)
{
temp[i] /= cluster.size();
temp[i] = sqrtf(temp[i]);
dst.push_back(temp[i]);
}


return dst;


}




//獲取最大標準偏差
double getMaxDev(doubleVector Dev)
{
int i;
double max = Dev[0];


for(i=1; i<dimNum; i++)
if(max<Dev[i])
max = Dev[i];


return max;
}




//進行分裂
doubleVector clusterSplit(doubleVector wMean, double sigma)
{
int i;
doubleVector newMean;
double temp;


newMean.clear();
for(i=0; i<dimNum; i++)
{
temp = wMean[i]+sigma;
newMean.push_back(temp);
}


return newMean;
}




//計算聚類中心兩兩間的距離
doubleVector eachCenterDist(doubleVector t1, doubleVector t2, int x, int y)
{
int i;
doubleVector dist;


dist.clear();
for(i=0; i<dimNum; i++)
dist.push_back(fabs(t1[i]-t2[i]));


dist.push_back(x);
dist.push_back(y);


return dist;
}




//按距離進行排序
vector<doubleVector> distSort(vector<doubleVector> eachDist)
{
int i, j, l;
vector<doubleVector> dst;
doubleVector maxEachDist;
double temp;
doubleVector tempVet;
double min;


dst = eachDist;
//尋找每組中最大的距離
maxEachDist.clear();
for(i=0; i<eachDist.size(); i++)
maxEachDist.push_back(getMaxDev(eachDist[i]));


//排序
for(i=0; i<maxEachDist.size(); i++)
{
l=i;
min = maxEachDist[i]; 
for(j=i+1; j<maxEachDist.size(); j++)
if(min>maxEachDist[j])
{
min = maxEachDist[j];
l = j;
}


temp = maxEachDist[i];
maxEachDist[i] = maxEachDist[l];
maxEachDist[l] = temp;


tempVet = dst[i];
dst[i] = dst[l];
dst[l] = tempVet;
}


return dst;
}




//進行合併
vector<doubleVector> Union(vector<doubleVector> mCenter, vector<doubleVector> sortDist)
{
int i[connectNum], j[connectNum];
int m, n, delNum=0;
vector<doubleVector> newCenter;
vector<int> del;
doubleVector temp;


newCenter = mCenter;


//可聯合數量
for(m=0; m<connectNum; m++)
for(n=0; n<dimNum; n++)
if(sortDist[m][n]<connectVar)
{
delNum++;
break;
}


//進行聯合
for(m=0; m<delNum; m++)
{
i[m] = sortDist[m][dimNum];
j[m] = sortDist[m][dimNum+1];


for(n=0; n<dimNum; n++)
temp.push_back((i[m]*mCenter[i[m]][n]+j[m]*mCenter[j[m]][n])/(i[m]+j[m]));


newCenter.push_back(temp);
}


//避免重複刪除
for(m=0; m<delNum; m++)
del.push_back(i[m]);

for(n=0; n<delNum; n++)
{
if(del[n]==j[n])
continue;
else
del.push_back(j[n]);
}



 for(m=0; m<del.size(); m++)
 newCenter[del[m]].clear();


for(m=0; m<newCenter.size(); )
{
if(newCenter[m].empty())
newCenter.erase(newCenter.begin()+m);
else
m++;
}

return newCenter;
}




//結果輸出
void Output(vector<clusterVector> cluster)
{
int i, j, k;


for(i=0; i<cluster.size(); i++)
{
printf("\n類聚%d :\n", i+1);
for(j=0; j<cluster[i].size(); j++)
printf("%lf  %lf  %lf\n", cluster[i][j][0], cluster[i][j][1], cluster[i][j][2]);
}
}