Kmeans聚类的C++实现和Matlab实现

Kmeans聚类的C++实现和Matlab实现

  1. 首先介绍Kmeans的工作原理
    Kmeans算法原理比较简单主要步骤为:
    (1).首先确定我们要把这些已知的点分为几个簇,然后随机选取K个中心点,当然这个中心点的选取在后期是会改变的,不过首先就要确定这个K值,是Kmeans聚类算法的一个缺点吧。
    (2).计算空间中所有点距离K个中心点的欧氏距离,数据点距离哪个中心点最近,就将此点划分到该点的簇中。这个算法是以距离作为相似度的评价指标,所以不能发现非凸簇集的,时间复杂度也就比较高了。
    (3).划分完成后,计算每一个类中所有数据点的均值,将该点作为新的中心点。
    (4).重复2和3当中心点位置变化小于预设阈值,聚类结束。

  2. C++代码实现
    代码有参考,点击进入
    在找了许多参考的文章和代码后,这个写的是非常好的,下面是自己理解
    代码第一步将每个坐标的元组准备好:

struct Tuple
{
    float attr1;
    float attr2;
};

计算欧氏距离

float get_XYdis(Tuple t1,Tuple t2)
{
    return sqrt((t1.attr1-t2.attr1)*(t1.attr1-t2.attr1)+(t1.attr2-t2.attr2)*(t1.attr2-t2.attr2));
}

可以根据每个点到各个中心点的距离来确定他是属于哪个簇

int clusteroftulple(Tuple means[],Tuple tupl)
{
    float dis = get_XYdis(means[0],tupl);
    float temp;
    int label=0;
    for(int i=0;i<k;i++)
    {
        temp = get_XYdis(means[i],tupl);
        if(temp < dis)
        {
            dis = temp;
            label = i;
        }
    }
    return label;
}

计算簇集平方误差

float getVar(vector<Tuple>cluster[],Tuple means[])
{
    float var;
    for(int i=0;i<k;i++)
    {
        vector<Tuple> t=cluster[i];
        for(int j=0;j<t.size();j++)
        {
            var += get_XYdis(t[j],means[i]);
        }
    }
    return var;
}

获得当前簇的中心点

Tuple getMeans(vector<Tuple>cluster)
{
    int number = cluster.size();
    double meanX=0,meanY=0;
    Tuple t;
    for(int i=0;i<number;i++)
    {
        meanX += cluster[i].attr1;
        meanY += cluster[i].attr2;
    }
    t.attr1 = meanX/number;
    t.attr2 = meanY/number;
    return t;
}

Kmeans的核心算法

void KMeans(vector<Tuple> tuples)
{
    vector<Tuple>clusters[k];
    Tuple means[k];
    int i=0;
    //默认一开始将前K个元组的值作为K个簇的质心
    for(i=0;i<k;i++)
    {
        means[i].attr1 = tuples[i].attr1;
        means[i].attr2 = tuples[i].attr2;
    }
    int label = 0;
    //根据默认的质心给簇赋值
    for(i=0;i!=tuples.size();++i)
    {
        label = clusteroftulple(means,tuples[i]);
        clusters[label].push_back(tuples[i]);
    }
    //输出刚开始的簇
    for(label=0;label<k;label++)
    {
        cout<< "第" << label+1<<"个簇 : "<<endl;
        vector<Tuple>t =clusters[label];
        for(i=0;i<t.size();i++)
        {
            cout<< "(" << t[i].attr1 << ","<<t[i].attr2<< ")"<< "  ";
        }
        cout<<endl;
    }
    float oldVar =-1;
    float newVar = getVar(clusters,means);
    //当新旧函数值相差不到一时(准则函数值不发生明显变化),算法终止
    while(abs(newVar - oldVar)>=1)
    {
        //更新每个簇中心点
        for(i=0;i<k;i++)
        {
            means[i] = getMeans(clusters[i]);
        }
        oldVar = newVar;
        newVar = getVar(clusters,means);
        //清空每个簇
        for( i=0;i<k;i++)
        {
            clusters[i].clear();
        }
        //根据新的质心获得新的簇
        for(i=0;i!=tuples.size();++i)
        {
            label = clusteroftulple(means,tuples[i]);
            clusters[label].push_back(tuples[i]);
        }
        //输出当前簇
        for(label=0;label<k;label++)
        {
            cout<< "第" << label+1<<"个簇 : "<<endl;
            vector<Tuple>t =clusters[label];
            for(i=0;i<t.size();i++)
            {
                cout<< "(" << t[i].attr1 << ","<<t[i].attr2<< ")"<< "  ";
            }
            cout<<endl;
        }
    }
}

主函数

int main()
{
    char fname[256];
    cout << "请输入文件名 : ";
    cin >> fname;
    cout<<endl;
    ifstream fin;
    fin.open(fname,ios::in);
    if(!fin)
    {
        cout<<"不能打开文件 : "<<fname<<endl;
        return 0;
    }
    int count = 0;
    vector<Tuple>tuples;
    Tuple tuplee;
    //从文件流中读取数据
    while(!fin.eof())
    {
        count++;
        if(count%2==1)
            fin>>tuplee.attr1;
        else
        {
            fin>>tuplee.attr2;
            tuples.push_back(tuplee);
        }
    }
    //输出文件的元组信息
    for(vector<Tuple>::size_type ix=0;ix!=tuples.size();ix++)
    {
        cout<<"("<<tuples[ix].attr1<< ","<<tuples[ix].attr2<< ")"<< "  ";
    }
    cout<<endl;
    KMeans(tuples);
    return 0;
}

通过验证,可以看到程序是哒到了效果的
程序运行结果
3.Matlab实现
首先我们要生成一些需要分类的点,在这里生成三个高斯分布的随机点 data1,data2和data3然后将这些点画出来

clear;
clc;
N = input ('请设置聚类数目 :');
%% data 1
mu1=[0 0];
s1=[0.1 0;0 0.1];
data1=mvnrnd(mu1,s1,100);
%% data 2
mu2=[-1.25 1.25];
s2=[0.1 0;0 0.1];
data2=mvnrnd(mu2,s2,100);
%% data 3
mu3=[1.25 1.25];
s3=[0.1 0;0 0.1];
data3=mvnrnd(mu3,s3,100);
%% display data scatter
plot(data1(:,1),data1(:,2),'b+');
hold on;
plot(data2(:,1),data2(:,2),'b+');
plot(data3(:,1),data3(:,2),'b+');

在然后进行中心点的初始化工作,定义KMeans算法,给所有的点打上标签

data = [data1;data2;data3];
[m,n]=size(data);
center = zeros(N,n);
pattern = data;
%% Kmeans algorithm
for x = 1:N
    center(x,:)=data(randi(300,1),:);
end
while true
distance = zeros(1,N);
num = zeros(1,N);
new_center = zeros(N,n);
%% 将所有的点打上标签
for x=1:m
    for y=1:N
        distance(y)=norm(data(x,:)-center(y,:));
    end
    [~,temp]=min(distance);
    pattern(x,n+1) = temp;
end;
k=0;

将所有在同一类里的点坐标全部相加,计算新的中心坐标

for y = 1 : N
    for x = 1 : m
        if pattern(x,n + 1) == y
           new_center(y,:) = new_center(y,:) + pattern(x,1:n);
           num(y) = num(y) + 1;
        end
    end
    new_center(y,:) = new_center(y,:) / num(y);
    if norm(new_center(y,:) - center(y,:)) < 0.1
        k = k + 1;
    end
end
if k == N
     break;
else
     center = new_center;
end
end
[m, n] = size(pattern);

最后将聚类后的数据用图片显示出来:

figure;
hold on;
for i = 1 : m
    if pattern(i,n) == 1 
         plot(pattern(i,1),pattern(i,2),'r*');
         plot(center(1,1),center(1,2),'ko');
    elseif pattern(i,n) == 2
         plot(pattern(i,1),pattern(i,2),'g*');
         plot(center(2,1),center(2,2),'ko');
    elseif pattern(i,n) == 3
         plot(pattern(i,1),pattern(i,2),'b*');
         plot(center(3,1),center(3,2),'ko');
    elseif pattern(i,n) == 4
         plot(pattern(i,1),pattern(i,2),'y*');
         plot(center(4,1),center(4,2),'ko');
    else
         plot(pattern(i,1),pattern(i,2),'m*');
         plot(center(5,1),center(5,2),'ko');
    end
end

生成的随机数点
分为3个簇时
分为4个簇时
分为四个簇时
感觉对于这些比较简单的算法直接看代码比去看他的原理分析更能让人接受。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值