以下是头文件:
#ifndef WSNHSCDA_H
#define WSNHSCDA_H
#include "common.h"
//凡需要计算两个向量欧氏距离的类,都包含此类
class eucideanDistanceClass
{
public:
static double eucideanDistance( const vector& f1, const vector& f2 ); //计算向量之间的欧氏距离
};
// 测量得到的数据向量,标示一个测量值。
// 每隔一段时间,传感节点进行测量,一个测量值有很多特征值。
// 此类保存一次取样得到的数据。
class dataSample{
public:
dataSample():type(0)
{
};
void operator()( dataSample & a)
{
cout<<"type = "<type = other.type;
this->features = other.features;
return *this;
}
unsigned int size()
{
return features.size();
};
double push_back( double val )
{
features.push_back(val);
};
double & operator[](unsigned int i)
{
return features[i];
};
public:
unsigned int type;
vector features;
};
// 簇的总结:簇中所有向量的线性和,数据向量的个数,簇ID。
// 由于簇要进行融合和传递,融合后还是一个簇总结,所以还需要保存节点ID和融合之前的簇的ID。
// 此类方便于簇总结传递的管理。
class clusterSample
{
public:
clusterSample():featuresNum(0),clusterID(0),sensorID(0),isMerged(0)
{
return;
};
void featuresUpdate(const clusterSample &cS ,unsigned int num = 1 );//用于融合
void featuresUpdate(const dataSample &dS ,unsigned int num = 1 );//用于聚类
void operator()(clusterSample & cS)
{
cout<<"ID: "<(cS.featuresAvg);
};
public:
bool isMerged;
vector featuresAvg;
vector featuresSum;
unsigned int featuresNum;
unsigned int clusterID;//用于标示新的簇,簇ID
unsigned int sensorID;//用于标示新的簇,产生这个簇的节点ID
vector mergeClusters;//仅仅用于簇的融合。保存融合之前簇的信息。
private:
void featuresAvgUpdate(unsigned int size);
private:
class eucideanDist :public eucideanDistanceClass
{
};
};
// 每个时间窗口的得到n个测量值。
// 在每一个节点上需要对测量值进行固定宽度聚类。
// 操作一个节点上的簇集合。包含原始的数据和簇信息。
// 此类方便节点自己管理自己的簇集合和子节点发送来的簇总结。
// 封装了关于节点对簇操作的所有逻辑,即是以节点为单位的。
class dataCluster
{
public:
dataCluster(vector & a, unsigned int sensorID = 0):dataSet(a),nodeID(sensorID)
{
return;
};
void fixedWidthCluster(double width);//对自身数据进行聚类
unsigned int size()
{
return dataSet.size();
};
public:
static unsigned int clusterCount; //记录簇的个数
vector dataSet;//节点自身的数据
vector clusters;//节点根据自身的数据,构造出的簇集合
private:
void normalize();
class eucideanDist :public eucideanDistanceClass
{
};
unsigned int nodeID;
protected:
dataCluster()
{
};//为sensorNode单独构造的缺省构造函数
friend class sensorNode;
};
//由于KNN算法结构比较复杂
//需要在这里对KNN算法内容进行封装
//仅仅需要在这个类里面封装的是KNN算法的逻辑部分
//KNN算法步奏描绘如下:
//1、每一个clusterSample包含了一个特征值的向量。
//2、对于簇集合中的每一个簇,计算其他所有簇和这个簇的距离。
//3、根据前K个距离,计算平均簇间距离。
//4、并返回由前K个值算出的均值组成的向量。
class KNNClass
{
public:
KNNClass(vector clusters,unsigned int k):clusters(clusters),k(k),AVGICD(0.0),SDICD(0.0)
{
dstMtrx.reserve(clusters.size());
};
double getAVGICD( )
{
return AVGICD;
};
double getSDICD()
{
return SDICD;
};
void creatDistanceMatrix( );
vector getICD()
{
return ICD;
};
private:
void cnstrctICD();
void calAVGICD();
void calSDICD();
private:
class eucideanDist:public eucideanDistanceClass
{
};
const unsigned int k;
vector clusters;
vector > dstMtrx;
vector ICD;
double AVGICD;
double SDICD;
};
// 在基站上进行KNN异常检测。
// 基站自己并不搜集数据,所以将dataCluster dC一项作为。
class BSNode
{
public:
BSNode(unsigned int nodeID = 0):nodeID(nodeID)
{
};
public:
/*保存来自各个子节点的簇集的集合。以便于操作来自多个节点的簇集合。此时二维的。
*第一维是各个节点。第二维是簇集合。
*/
vector clustersSet;
vector dataSet;//将二维的来自不同节点的数据融合变为为一维的节点自身的数据
const unsigned int nodeID;
private:
void anormalyDetectKNN(double anorm,unsigned int k);
private:
class eucideanDist:public eucideanDistanceClass
{
};
//保存被检测出异常的簇
vector clustersAnormal;
};
// 基站和节点是is a的关系。
// 每一个节点都是一个微型的基站。
// 1、搜集数据
// 2、对数据进行聚类
// 3、将聚类结果送到一跳父亲节点
// 4、需要对来自子节点的簇进行融合
// 5、需要进行异常检测
class sensorNode :private BSNode
{
public:
sensorNode(dataCluster &a,unsigned int nodeID):BSNode(nodeID)
{
dC = a;
};
void clusterMerge(double);
void anormalyDetect(double anorm);
private:
//继承来自于基站类的数据成员
//保存来自各个子节点的簇集的集合。以便于操作来自多个节点的簇集合。
//vector clustersSet;
//自己节点上的数据
dataCluster dC;
class eucideanDist:public eucideanDistanceClass
{
};
};
void WSNHSCDATESTCPP();
#endif
#include "wsnHsCDA.h"
unsigned int dataCluster::clusterCount = 0;
//计算特征量之间的欧氏距离
double eucideanDistanceClass::eucideanDistance( const vector& f1, const vector& f2 )
{
vectorPrint(f1);
vectorPrint(f2);
assert( f1.size() == f2.size() );
double dist = 0.0;
for( vector::size_type i = 0; i < f1.size(); i++ )
{
dist += pow(f1[i]-f2[i],2);
}
return sqrt(dist);
}
//固定宽度聚类,发生在每一个节点上,对本地数据聚类。
//固定宽度聚类创建一个固定半径(宽度)的超球面簇集合。
//首先,随机选择任何数据点作为第一个以w为半径的簇的中心。
//通常,以欧式距离计算当前这个簇的中心和下一个待聚类数据向量的距离。
//如果这个距离小于W,这个数据向量就属于这个簇,然后更新中心点。
//如果距离大于w,就以这个数据向量为新的中心,构造一个新的簇。
void dataCluster::fixedWidthCluster(double width)
{
bool found = false;
clusterSample cs;
cs.featuresUpdate(dataSet[0]);
cs.clusterID = clusterCount;//对于新建立的簇要分配其簇ID
cs.sensorID = nodeID;//对于新建立的簇要分配其节点ID
dataSet[0].type = cs.clusterID;
clusters.push_back(cs);
for( vector::size_type countD = 1; countD < dataSet.size(); countD++ ) // 遍历数据集
{
found = false;
for(vector::size_type countC = 0; countC < clusters.size(); countC++ )//遍历簇集
{
if( eucideanDist::eucideanDistance( dataSet[countD].features,clusters[countC].featuresAvg ) < width )//加入类别
{
cout<<"*****dis is: "<::size_type countCSet1 = 0; countCSet1 < clustersSet.size(); countCSet1++ )//从第一个节点开始上的簇集开始
{
dataCluster dC1 = clustersSet[countCSet1];//取出第一个节点上的簇集
for( vector::size_type countCSm1 = 0; countCSm1 < dC1.clusters.size(); countCSm1++ )//从第一个簇开始
{
clusterSample cS1 = dC1.clusters[countCSm1];//取出第一个簇集上的簇
if( true == cS1.isMerged )//此簇已经融合,进入到下一个簇
{
continue;
}
for( vector::size_type countCSet2 = countCSet1 +1; countCSet2 < clustersSet.size(); countCSet2++)//从下一个节点开始
{
dataCluster dC2 = clustersSet[countCSet2];//取出簇集合
for(vector::size_type countCSm2 = 0; countCSm2 < dC1.clusters.size(); countCSm2++)
{
clusterSample cS2 = dC1.clusters[countCSm2]; //取出簇
if( true == cS2.isMerged )
{
continue;
}
if( eucideanDist::eucideanDistance(cS1.featuresAvg,cS2.featuresAvg) < w )
{
clusterSample cs;
cs.featuresUpdate(cS1,cS1.featuresNum);//将两个簇sample合并为一个簇
cs.featuresUpdate(cS2,cS2.featuresNum);
dC.clusters.push_back(cs);//并将此簇加入到当前节点的簇集合中
cS1.isMerged = true;
cS2.isMerged = true;
}
}
}
}
}
//然后,融合到自己的簇集合
//最后在遍历子节点发来的簇集的过程中,
//对没有融合的所有簇,都添加进节点自身的簇集合dC
for( vector::size_type countCS3 = 0; countCS3 < dC.clusters.size(); countCS3++ )//从第一个簇开始(dataCluster dC)
{
if( true == dC.clusters[countCS3].isMerged )
{
continue;
}
for( vector::size_type countCSet3 = 0; countCSet3 < clustersSet.size(); countCSet3++)//从第一个节点开始
{
dataCluster dC3 = clustersSet[countCSet3];//取出簇集合
for(vector::size_type countCSm3 = 0; countCSm3 < dC3.clusters.size(); countCSm3++)
{
if( true == dC3.clusters[countCSm3].isMerged )//已经融合过的不再融合
{
continue;
}
if( eucideanDist::eucideanDistance(dC.clusters[countCS3].featuresAvg,dC3.clusters[countCSm3].featuresAvg) < w )
{
clusterSample cs;
cs.featuresUpdate(dC.clusters[countCS3],dC.clusters[countCS3].featuresNum);//将两个簇sample合并为一个簇
cs.featuresUpdate(dC3.clusters[countCSm3],dC3.clusters[countCSm3].featuresNum);
dC.clusters.push_back(cs);//并将此簇加入到当前节点的簇集合中
dC.clusters[countCS3].isMerged = true;
dC3.clusters[countCSm3].isMerged = true;
}
else
{
dC.clusters.push_back(dC3.clusters[countCSm3]);//并将此簇加入到当前节点的簇集合中,并不在融合
//那么融合后,此节点的簇集合就是dC中没有被标记isMerged的所有簇
}
}
}
}
return;
}
//对节点自身的数据进行归一化处理
void dataCluster::normalize()
{
assert( dataSet.size() ); //确保数据集不为空
vector dataSet;
vector avg;
avg.reserve(dataSet[0].size());
vector stdDiv;
stdDiv.reserve(dataSet[0].size());
for( unsigned int count1 = 0; count1 < dataSet.size(); count1++ )//求出各个特征值在各个特征向量的和。
{
for( unsigned int count2 = 0; count2 < dataSet[count1].size(); count2++ )
{
avg[count2] += dataSet[count1][count2];
}
}
for( unsigned int count3 = 0; count3 < avg.size(); count3++ )//求出均值。
{
avg[count3] /= dataSet.size();
}
for( unsigned int count4 = 0; count4 < dataSet.size(); count4++ )
{
for( unsigned int count5 = 0; count5 < dataSet[count4].size(); count5++ )
{
stdDiv[count5] += pow(dataSet[count4][count5]-avg[count5],2);
}
}//求出标准方差。
for( unsigned int count6 = 0; count6 < stdDiv.size(); count6++ )//求出均值。
{
stdDiv[count6] = sqrt(stdDiv[count6]);
}
//归一化。
for( unsigned int count1 = 0; count1 < dataSet.size(); count1++ )//求出各个特征值在各个特征向量的和。
{
for( unsigned int count2 = 0; count2 < dataSet[count1].size(); count2++ )
{
dataSet[count1][count2] = (dataSet[count1][count2] - avg[count2])/stdDiv[count2];
}
}
}
void clusterSample::featuresAvgUpdate(unsigned int size )
{
if( featuresAvg.size() != 0 )
{
for( vector::size_type i = 0; i < featuresSum.size();i++ )
featuresAvg[i] = featuresSum[i]/featuresNum;
}
else{
for( vector::size_type i = 0; i < size; i++ )
featuresAvg.push_back(featuresSum[i]/featuresNum);
}
return;
}
void clusterSample::featuresUpdate(const clusterSample &cS,unsigned int num )
{
featuresNum += num;
if( featuresSum.size() != 0 )
{
for( vector::size_type i = 0 ; i < featuresSum.size(); i++ )
featuresSum[i] = featuresSum[i]+cS.featuresAvg[i];
}
else{
for( vector::size_type i = 0; i < cS.featuresAvg.size(); i++ )
featuresSum.push_back(cS.featuresAvg[i]);
}
mergeClusters.push_back(cS);
featuresAvgUpdate(cS.featuresAvg.size());
return;
}
void clusterSample::featuresUpdate(const dataSample &dS ,unsigned int num )
{
featuresNum += num;
if( featuresSum.size() != 0 )
{
for( vector::size_type i = 0 ; i < featuresSum.size(); i++ )
featuresSum[i] = featuresSum[i]+dS.features[i];
}
else{
for( vector::size_type i = 0; i < dS.features.size(); i++ )
featuresSum.push_back(dS.features[i]);
}
featuresAvgUpdate(dS.features.size());
return;
}
void KNNClass::calAVGICD()
{
unsigned int count = 0;
for( vector::size_type countdM1 = 0; countdM1 < dstMtrx.size(); countdM1++ )
{
for( vector::size_type countdM2 = 0; countdM2 <= dstMtrx[countdM1].size(); countdM2++ )
{
AVGICD += dstMtrx[countdM1][countdM2];
count++;
}
}
AVGICD /= (2*count);
}
void KNNClass::calSDICD()
{
unsigned int count = 0;
for( vector::size_type countdM1 = 0; countdM1 < dstMtrx.size(); countdM1++ )
{
for( vector::size_type countdM2 = 0; countdM2 <= dstMtrx[countdM1].size(); countdM2++ )
{
SDICD += pow(dstMtrx[countdM1][countdM2] - AVGICD,2);
count++;
}
}
SDICD = sqrt( SDICD/(count-1) );
}
void KNNClass::cnstrctICD( )
{
unsigned temp = ( k > dstMtrx.size() ) ? dstMtrx.size()-1 : k ;
for( vector::size_type countdM1 = 0; countdM1 < dstMtrx.size(); countdM1++ )
{
for( vector::size_type countdM2 = 0; countdM2 <= temp; countdM2++ )
{
ICD[countdM1] += dstMtrx[countdM1][countdM2];
}
ICD[countdM1] /= k;
}
}
void KNNClass::creatDistanceMatrix( )
{
for( vector::size_type countcS1 = 0; countcS1 < clusters.size(); countcS1++ )
{
for( vector::size_type countcS2 = countcS1 + 1; countcS2 < clusters.size(); countcS2++ )
{
dstMtrx[countcS1][countcS2] = dstMtrx[countcS2][countcS1] =
eucideanDist::eucideanDistance(clusters[countcS1].featuresAvg,clusters[countcS2].featuresAvg);
}
dstMtrx[countcS1][countcS1] = 0.0;
sort(dstMtrx[countcS1].begin(),dstMtrx[countcS1].end());
}
cnstrctICD( );
calAVGICD();
calSDICD();
}
//在节点上执行,此处实现在节点上运行的,
void sensorNode::anormalyDetect(double anorm)
{
return;
}
//在基站上执行。
//基站上汇聚有全网络搜集的数据
//以KNN为基础,实现全局异常簇的识别。
//并以簇为检测单位的一次检测算法。
//论文中并没有讲明白这个平均簇间距离如何求。
//也没有说清楚标准方差如何求。
void BSNode::anormalyDetectKNN(double anorm,unsigned int k)
{
KNNClass knnobject(dataSet,k);
knnobject.creatDistanceMatrix( );
//求出所有簇间距离的均值。
//求出所有簇间距离的方差。
//筛选出异常簇。
for( vector::size_type countdM1 = 0; countdM1 < knnobject.getICD().size(); countdM1++ )
{
if( knnobject.getICD()[countdM1] > knnobject.getAVGICD() + anorm*knnobject.getSDICD() )
clustersAnormal.push_back(dataSet[countdM1]);
}
return;
}
// 以下实验数据,共计三个簇。
// 分别为:1,2,3,4; 5,6,7,8; 9,10,11,12.
// 当width取值为40-100之间时,可以分为三个簇。
void WSNHSCDATESTCPP()
{
vector dataSet;
dataSample d1;
d1.features.push_back(1.0); //x
d1.features.push_back(2.0); //y
dataSample d2;
d2.features.push_back(1.1); //x
d2.features.push_back(2.1); //y
dataSample d3;
d3.features.push_back(0.5); //x
d3.features.push_back(2.0); //y
dataSample d4;
d4.features.push_back(1.5); //x
d4.features.push_back(2.9); //y
dataSet.push_back(d1);
dataSet.push_back(d2);
dataSet.push_back(d3);
dataSet.push_back(d4);
dataSample d5;
d5.features.push_back(1.0); //x
d5.features.push_back(112.0); //y
dataSample d6;
d6.features.push_back(10.1); //x
d6.features.push_back(102.1); //y
dataSample d7;
d7.features.push_back(0.5); //x
d7.features.push_back(132.0); //y
dataSample d8;
d8.features.push_back(5.5); //x
d8.features.push_back(120.9); //y
dataSet.push_back(d5);
dataSet.push_back(d6);
dataSet.push_back(d7);
dataSet.push_back(d8);
dataSample d9;
d9.features.push_back(111.0); //x
d9.features.push_back(2.0); //y
dataSample d10;
d10.features.push_back(121.1); //x
d10.features.push_back(32.1); //y
dataSample d11;
d11.features.push_back(100.5); //x
d11.features.push_back(2.0); //y
dataSample d12;
d12.features.push_back(111.5); //x
d12.features.push_back(2.9); //y
dataSet.push_back(d9);
dataSet.push_back(d10);
dataSet.push_back(d11);
dataSet.push_back(d12);
cout<<"数据集大小为: "<