最大最小距离算法

Posted lepeCoder

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大最小距离算法相关的知识,希望对你有一定的参考价值。


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

以上是关于最大最小距离算法的主要内容,如果未能解决你的问题,请参考以下文章

算法:点与点之间欧式距离最小

算法将点放入具有最大最小距离的正方形

比给定值最小的最大元素的 STL 算法

网络流的最大流和最小流是啥算法

基于最大最小距离的分类数目上限K确定的聚类方法

最小生成树应用