最大最小距離算法



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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM