模式识别课程设计一:K-均值法

根据题意,使用上课所教的K——均值法实现对给定数据的聚类分析。

  • 算法介绍

根据题目要求,限定算法使用为K——均值法。

预先的条件及约定:

设待分类的模式特征矢量集为{x1,x2,…,xN};

类的数目K是事先取定的。

算法基本思想:

首先任意选取K个聚类中心,按最小距离原则将各模式分配到K类的某一类;

不断计算聚类中心和调整各模式的类别,最终使各模式到其判属类别中心的距离平方之和最小。

基于使聚类准则函数最小化

准则函数:聚类集中每一样本点到该类中心的距离平方和。

对于第j个聚类集,准则函数定义为

Sj:第j个聚类集(域),聚类中心为Zj ;

Nj:第j个聚类集Sj中所包含的样本个数。

对所有K个模式类有

K-均值算法的聚类准则:聚类中心的选择应使准则函数J极小,即使Jj的值极小。

对于某一个聚类 j,

上式表明,Sj类的聚类中心应选为该类样本的均值。

算法步骤:

(1)任选K个模式特征矢量作为初始聚类中心:z1(1) ,z2(1) ,…zK(1)。括号内的序号表示迭代次数。

(2)将待分类的模式特征矢量集{x}中的模式逐个按最小距离原则分划给K类中的某一类。

如果 Dj(k) =min{||x-zi(k)||},   i=1,2,…,K

则判       x∈Sj(k)

(4)如果zj(k+1)=zj(k)(j=1,2,…K),则结束;否则,k=k+1,转(2)

  • 计算过程

因为题目没有提前预设K值,所以我采用了让用户输入自己所需K值的方式。

为了是归类能够更加完善,所以我每一次的过程所提前选取的Zi都是不一样的,这样的话可以使结果更加完善。

为了便于计算,我设立了一个结构体xly。

具体代码如下:

#include<iostream>

#include<cmath>

using namespace std;

struct xly

{

    double a;

    double b;

    int s;

    double* d;

};

int Ksuanfa(int n, xly x[], xly Z[], xly ZX[], int k)

{

    if (n <= 0)

        return -1;

    int i, j, t;

    int* NN = new int[k + 1];

    for (i = 1; i <= k; i++)

        NN[i] = 0;

    double min[11];

    for (i = 1; i <= 10; i++)

    {

        for (j = 1; j <= k; j++)

        {

             x[i].d[j] = sqrt((x[i].a - Z[j].a) * (x[i].a - Z[j].a) + (x[i].b - Z[j].b) * (x[i].b - Z[j].b));

        }

    }

    for (i = 1; i <= 10; i++)

    {

        min[i] = x[i].d[1];

        x[i].s = 1;

        for (j = 1; j <= k; j++)

        {

             if (min[i] > x[i].d[j])

             {

                 min[j] = x[i].d[j];

                 x[i].s = j;

             }

        }

    }

    for (i = 1; i <= 10; i++)

    {

        NN[x[i].s]++;

    }

    for (i = 1; i <= k; i++)

    {

        ZX[i].a = 0;

        ZX[i].b = 0;

    }

    for (i = 1; i <= 10; i++)

    {

        ZX[x[i].s].a += x[i].a;

        ZX[x[i].s].b += x[i].b;

    }

    for (i = 1; i <= k; i++)

    {

        ZX[i].a = ZX[i].a / NN[i];

        ZX[i].b = ZX[i].b / NN[i];

    }

    for (t = 1; t <= k; t++)

    {

        if (Z[t].a != ZX[t].a)

             break;

        else

        {

             if (Z[t].b != ZX[t].b)

                 break;

        }

    }

    if (t > k)

        return 1;

    else

    {

        for (i = 1; i <= k; i++)

        {

             Z[i].a = ZX[i].a;

             Z[i].b = ZX[i].b;

        }

        return Ksuanfa(n - 1, x, Z, ZX, k);

    }

}

int main()

{

    zcy x[11], Z[11], ZX[11];

    x[1].a = 0;

    x[1].b = 0;

    x[2].a = 3;

    x[2].b = 8;

    x[3].a = 2;

    x[3].b = 2;

    x[4].a = 1;

    x[4].b = 1;

    x[5].a = 5;

    x[5].b = 3;

    x[6].a = 4;

    x[6].b = 8;

    x[7].a = 6;

    x[7].b = 3;

    x[8].a = 5;

    x[8].b = 4;

    x[9].a = 6;

    x[9].b = 4;

    x[10].a = 7;

    x[10].b = 5;

    int k, i, j,m=0;

    char w;

    do {

        cout << "请输入你所预先设置的K值。(注意,K应该是不大于10的正整数)" << endl;

        cin >> k;

        if (k > 10 || k < 1)

             cout << "请正确输入K值。" << endl;

        for (i = 1; i <= 10; i++)

             x[i].d = new double[k + 1];

        int n = 150;

        for (i = 1; i <= k; i++)

        {

             Z[i].a = x[(i+m)%10+1].a;

             Z[i].b = x[(i+m)%10+1].b;

             Z[i].s = i;

        }

        if (Ksuanfa(n, x, Z, ZX, k) > 0)

        {

             cout << "最后的聚类中心分别为:" << endl;

             for (i = 1; i <= k; i++)

                 cout << '(' << Z[i].a << ',' << Z[i].b << ')' << endl;

             cout << "每一个类的点为:" << endl;

             for (i = 1; i <= k; i++)

             {

                 cout << "第" << i << "类:";

                 for (j = 1; j <= 10; j++)

                 {

                     if (x[j].s == i)

                         cout << j << '\t';

                 }

                 cout << endl;

             }

        }

        else

        {

             cout << "该结果不收敛,无法分类!" << endl;

        }

        cout << "如果你不满意分组数或者不满意分组,请您输入y重新开始,如果您满意这一次的分组情况,请您输入n结束程序。";

        cin >> w;

        m++;

    } while (w == 'y');

    return 0;

}

  • 结果分析讨论

首先证明我的结果可以使用于任意K值,以下选取K为2,4,6,8正常值以及19,-1两个异常值的结果:

接着分析在K值为2的时候,预选的Zi值不同,得到的结果也不同:

这也证明了K——均值法的局限性:聚类结果依赖于预选值。

  • 实验感悟

在最开始的时候,因为所给数据的缘由,所以我给自己设立的结构体zcy中的两个坐标设置的类型均为int,但是在这样的情况下,得到的结果十分不精确,如图所示:

仅仅能够大概反映聚类情况,但是有关聚类中心的求解就十分的不精确了,为了更加精确求解,因此将变量类型由int改为了double。

为了更加的方便用户分析我加入了循环,使得用户可以按照自己的意愿决定是否终结程序,实现了交互功能,如图所示:

通过本次课程设计,我对K——均值法有了更深刻的理解,同时也进一步增强了我的C++编程能力,收获满满。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值