title: 最大最小距離算法
date: 2017-12-16 17:36:54
tags: 聚類算法
categories: Algorithms
課程設計
使用最大最小距離算法做聚類分析
/*
使用最大最小距離法做聚類分析
1. 任選一個樣本作為聚類中心z1
2. 選擇離z1距離最大的樣本作為第二個聚類中心z2
3. 計算其余樣本與{z1,z2}之間的距離,Di1=|Xi-z1|, Di2=|Xi-z2|,Di=min(Di1,Di2)
4. 若max(Di,{i=1..2..}) >
θ|z1-z2|,即樣本離兩個中心太遠,則取max(Di)中的Xi為新的聚類中心z3,
若沒有新的聚類中心,則找聚類中心過程結束,直接將樣本分到最近的中心。
5. 重新回到第3步計算最小距離,
*/
/*
距離測度:
歐式距離 絕對值距離 切氏距離 閔氏距離
*/
/*
類內距離
*/
/*
二值特征還有匹配測度
*/
/*
1. 輸入比例系數
2. 輸入樣本數和樣本維度
3. 輸入樣本
4. 確定距離測度
5. 第一個樣本作為第一個聚類中心
6. 找到離第一個樣本最遠的樣本作為第二個聚類中心
7. 找到離這兩個聚類中心都很遠的樣本作為其他可能的中心
8. 將所有樣本分到最近的聚類中心
9. 計算類間距離
*/
#include "algorithm"
#include "cmath"
#include "cstring"
#include "iostream"
#include "vector"
using namespace std;
int z[30]; //聚類中心
int zn; //聚類中心的個數
double a[30][6]; //樣本,最多30個樣本,每個樣本最多6個維度
double d[33]; //樣本到z1的距離
double mind[33]; //樣本到最近聚類中心的距離
int n; //樣本數量
int D; //樣本維數
//初始化分類向量,最多30個類
vector<vector<int>> v(30);
int DistanceMeasure; //距離測度
//計算兩點間歐式距離
double Euclidean(double x[], double y[]) {
int sum = 0;
for (int i = 1; i <= D; i++)
sum = sum + (x[i] - y[i]) * (x[i] - y[i]);
return sqrt(sum);
}
//絕對值距離
double Manhattan(double x[], double y[]) {
int sum = 0;
for (int i = 1; i <= D; i++)
sum += abs(x[i] - y[i]);
return sum;
}
//切氏距離
double Chebyahev(double x[], double y[]) {
double ans = 0;
for (int i = 1; i <= D; i++) {
ans = max(ans, abs(x[i] - y[i]));
}
return ans;
}
//閔氏距離
double Minkowski(double x[], double y[], int m) {
int sum = 0;
for (int i = 1; i <= D; i++) {
sum += pow(abs(x[i] - y[i]), m);
}
return pow(sum, 1.0 / m);
}
//用DistanceMeasure選擇不同的距離測度
double Distan(double x[], double y[]) {
if (DistanceMeasure == 1) //歐式距離
return Euclidean(x, y);
else if (DistanceMeasure == 2) //絕對值距離
return Manhattan(x, y);
else if (DistanceMeasure == 3) //切氏距離
return Chebyahev(x, y);
else if (DistanceMeasure == 4) { //閔氏距離
static int m;
if (!m) {
cout << "請輸入閔氏距離的參數m: ";
cin >> m;
}
return Minkowski(x, y, m);
} else {
cout << "參數錯誤!!\n";
return -1;
}
}
/*
類間距離:
最近距離法 最遠距離法 中間距離法 重心距離法 平均距離法
*/
//最近距離
//計算第x個類別和第y個類別的元素間的最小距離
double ClusterMin(int x, int y) {
double MinDistan = 1 << 30;
for (auto i = v[x].begin(); i != v[x].end(); i++) {
for (auto j = v[y].begin(); j != v[y].end(); j++) {
double tempd = Distan(a[*i], a[*j]);
MinDistan = min(MinDistan, tempd);
}
}
return MinDistan;
}
//最遠距離
double ClusterMax(int x, int y) {
double MaxDistab = 0;
for (auto i = v[x].begin(); i != v[x].end(); i++)
for (auto j = v[y].begin(); j != v[y].end(); j++) {
double tempd = Distan(a[*i], a[*j]);
MaxDistab = max(MaxDistab, tempd);
}
return MaxDistab;
}
//重心距離
//以樣本均值作為類的重心
double ClusterCentroid(int x, int y) {
double CenterX[33], CenterY[33];
memset(CenterX, 0, sizeof(CenterX));
memset(CenterY, 0, sizeof(CenterY));
for (auto i = v[x].begin(); i != v[x].end(); i++) {
for (int j = 1; j <= D; j++) {
CenterX[j] += a[*i][j];
}
}
for (auto i = v[y].begin(); i != v[y].end(); i++) {
for (int j = 1; j <= D; j++) {
CenterY[j] += a[*i][j];
}
}
//求得中心位置
for (int i = 1; i <= D; i++) {
CenterX[i] /= v[x].size();
CenterY[i] /= v[y].size();
}
//計算中間距離
double ans = Distan(CenterX, CenterY);
return ans;
}
//中心距離
// double ClusterMedian(int x, int y) { return 0; }
//平均距離
double ClusterAverage(int x, int y) {
double sum = 0, tempd;
for (auto i = v[x].begin(); i != v[x].end(); i++) {
for (auto j = v[y].begin(); j != v[y].end(); j++) {
tempd = Distan(a[*i], a[*j]);
sum += tempd;
}
}
sum /= (v[x].size() * v[y].size());
return sqrt(sum);
}
//類內距離
//輸入x為類別,z[x]為x的類心,v[x]為類內樣本
double ClusterInDistance(int x) {
//定義為類內樣本到類中心的距離和
double sum = 0;
int t = z[x];
double d;
for (auto i = v[x].begin(); i != v[x].end(); i++) {
d = Distan(a[t], a[*i]);
sum += d;
}
return sum;
}
int main() {
freopen("in.txt", "r", stdin);
double k; //閾值
cout << "請輸入閾值:\n";
cin >> k;
cout << "請輸入樣本數量和樣本維數:\n";
cin >> n >> D;
cout << "輸入樣本:\n";
int i, j;
//錄入樣本
for (i = 1; i <= n; i++)
for (j = 1; j <= D; j++)
cin >> a[i][j];
// 選擇距離測度
cout << "選擇距離測度: \n";
cout << "1. 歐氏距離\n";
cout << "2. 絕對值距離\n";
cout << "3. 切氏距離\n";
cout << "4. 閔氏距離\n";
cin >> DistanceMeasure;
//選擇第一個樣本點為初始聚類中心
z[1] = 1;
//選擇距離z1最遠的樣本作為第2個聚類中心
double t = 0; //最大距離
for (i = 2; i <= n; i++) {
d[i] = Distan(a[1], a[i]); //樣本i距z1的距離
// cout << i << "到x1距離: " << d[i] << endl;
if (d[i] > t) {
t = d[i];
z[2] = i;
}
}
zn = 2;
//查找聚類中心
while (1) {
//計算其余樣本到最近聚類中心的距離
//聚類中心數量>=2
for (i = 1; i <= n; i++) {
double t = 1 << 30; //臨時作為最小距離
for (j = 1; j <= zn; j++) {
t = min(t, Distan(a[z[j]], a[i]));
}
mind[i] = t;
}
//選出最小距離中的最大距離
int tempZ; //可能的新聚類中心
double tempD = 0; //最小距離中的最大距離
for (int i = 1; i <= n; i++) {
if (mind[i] > tempD) {
tempD = mind[i];
tempZ = i;
}
}
if (tempD > k * Distan(a[1], a[z[2]])) { //增加新聚類中心
z[++zn] = tempZ;
} else {
//查找聚類中心結束
break;
}
}
//輸出聚類中心
/* cout << "聚類中心: \n";
for (i = 1; i <= zn; i++)
cout << z[i] << ' ';
cout << endl;*/
//將樣本分配到最近的聚類中心
for (i = 1; i <= n; i++) { //遍歷所有的樣本
vector<int> v1;
double d; //距離
t = 1 << 30; //最小距離
int tempNo; //臨時分組
for (j = 1; j <= zn; j++) { //遍歷所有的中心
d = Distan(a[z[j]], a[i]);
if (d < t) { //距離更近,更新分組
t = d;
tempNo = j;
}
}
// cout << "push" << tempNo << endl;
v[tempNo].push_back(i); //將樣本i加到第tempNo個分組中
}
//for (auto x = v[3].begin(); x != v[3].end(); x++)
//cout << *x;
//cout << endl;
//輸出分組信息
for (i = 1; i <= zn; i++) {
cout << "分組中心: " << z[i] << endl;
cout << "分組成員: ";
for (auto x = v[i].begin(); x != v[i].end(); x++)
cout << *x << ' ';
cout << endl;
}
//計算類間距離
cout << "類間距離: \n";
for (i = 1; i <= zn; i++) {
for (j = i + 1; j <= zn; j++) {
cout << " " << i << " --- " << j << endl;
cout << "最近距離: " << ClusterMin(i, j) << endl;
cout << "最遠距離: " << ClusterMax(i, j) << endl;
// cout << "中間距離: " << ClusterMedian(i, j) << endl;
cout << "重心距離: " << ClusterCentroid(i, j) << endl;
cout << "平均距離: " << ClusterAverage(i, j) << endl;
}
}
//類內距離
//定義為類內樣本到類心的距離和
cout << "\n類內距離: \n";
for (i = 1; i <= zn; i++) {
cout << "第" << i << "類: ";
cout << ClusterInDistance(i) << endl;
}
return 0;
}
測試輸入文件
in.txt
0.5
10 2
0 0
3 8
2 2
1 1
5 3
4 8
6 3
5 4
6 4
7 5
1
