随机计算TFIDF作为权重,然后利用余弦距离进行聚类,用的是简单k-means算法。

#include <iostream>

#include <string>
#include <vector>
#include <map>
#include <utility>
#include <sstream>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

struct Kmeans
{
    vector< pair<string,map<string,int>> > asAllLines;    //存储所有文本行以及每一行各个单词出现次数,用于计算TF
    vector< vector<double> > aaLinesTf;    //所有文本行对应所有单词的TF*IDF
    map<string,int> mKeyAprLines;    //每个单词在多少行里出现过,用于计算IDF
    vector< vector<double> > aCluCenter;    //聚类中心 
    vector<int> aiLinesInCenter;    //每个中心聚集了多少文本行
    vector<int> aLinesCenter;    //每个文本行属于哪个聚类中心

    int iKeyNum;    //单词种类
    int iLineNum;    //总文本行数
    int iCenterNum;    //中心个数
};

#define MAX_DISTANCE 1e6

void stringToLower(string &key)
{
    for(int i=0;i<(int)key.size();)
    {
        if(key[i]<0)
        {
            i+=2;
        }
        else 
        {
            if('A'<=key[i] && key[i]<='Z')
            {
                key[i]=key[i]-'A'+'a';
            }

            i++;
        }
    }
}

void kMeansCentersRandomInit(Kmeans *pKmeans)
{
    srand((unsigned int)time(NULL));

    for(int i=0;i<pKmeans->iCenterNum;++i)
    {
        bool bContinue=true;

        do
        {
            int iToSelect=rand()%pKmeans->iLineNum;

            if(pKmeans->aLinesCenter[iToSelect]==-1)
            {
                for(int j=0;j<pKmeans->iKeyNum;++j)
                {
                    pKmeans->aCluCenter[i][j]=pKmeans->aaLinesTf[iToSelect][j];
                }

                pKmeans->aLinesCenter[iToSelect]=i;
                cout<<"初始化中心"<<i<<":"<<pKmeans->asAllLines[iToSelect].first<<endl;
                bContinue=false;
            }
        }while(bContinue);
    }
}

int kMeansInit(Kmeans *pKmeans,const vector<string> &asAllLines,int iLineNum,int iCenterNum)
{
    if(pKmeans==NULL)
    {
        return -1;
    }

    pKmeans->asAllLines.assign(iLineNum,pair< string,map<string,int> >());
    pKmeans->aaLinesTf.assign(iLineNum,vector<double>());
    pKmeans->aCluCenter.assign(iCenterNum,vector<double>());
    pKmeans->aiLinesInCenter.assign(iCenterNum,0);
    pKmeans->aLinesCenter.assign(iLineNum,0);

    pKmeans->iLineNum=iLineNum;
    pKmeans->iCenterNum=iCenterNum;

    //拆分单词,统计数据
    for(int i=0;i<(int)asAllLines.size();++i)
    {
        pKmeans->asAllLines[i].first=asAllLines[i];

        istringstream istrm(asAllLines[i]);
        string key;

        while(istrm>>key)
        {
            stringToLower(key);

            if(pKmeans->asAllLines[i].second.find(key)==pKmeans->asAllLines[i].second.end())
            {
                pKmeans->asAllLines[i].second[key]=1;
            }
            else
            {
                pKmeans->asAllLines[i].second[key]++;
            }

            if(pKmeans->asAllLines[i].second[key]==1)
            {
                pKmeans->mKeyAprLines[key]++;
            }
        }
    }

    pKmeans->iKeyNum=pKmeans->mKeyAprLines.size();
    
    for(int i=0;i<pKmeans->iLineNum;++i)
    {
        pKmeans->aaLinesTf[i].assign(pKmeans->iKeyNum,0);
        pKmeans->aLinesCenter[i]=-1;
    }

    for(int i=0;i<pKmeans->iCenterNum;++i)
    {
        pKmeans->aCluCenter[i].assign(pKmeans->iKeyNum,0);
    }
    
    //计算TF-IDF
    int iKeyIndex=0;
    for(map<string,int>::iterator iter=pKmeans->mKeyAprLines.begin();iter!=pKmeans->mKeyAprLines.end();++iter,++iKeyIndex)
    {
        for(int line=0;line<iLineNum;++line)
        {
            if(pKmeans->asAllLines[line].second.find(iter->first)==pKmeans->asAllLines[line].second.end())
            {
                pKmeans->aaLinesTf[line][iKeyIndex]=0;    
            }
            else
            {
                double TF=1.0*pKmeans->asAllLines[line].second[iter->first]/pKmeans->asAllLines[line].second.size();
                double DF=log(1.0*pKmeans->iLineNum/iter->second);
                pKmeans->aaLinesTf[line][iKeyIndex]=TF*DF;
            }
        }
    }

    return 0;
}

void kMeansMainLoop(Kmeans *pKmeans,int iTimes,double iThreshold)
{
    /*
    if(iTimes==0 && iThreshold==0)
    {
        return;    //没有终止条件
    }
    */

    int iLeftTimes=iTimes;
    double lastJ=-1;
    vector<int> aPreLinesCenter;    //之前的聚类

    kMeansCentersRandomInit(pKmeans);

    while(true)
    {
        double J=0;
        pKmeans->aiLinesInCenter.assign(pKmeans->iCenterNum,0);
        aPreLinesCenter=pKmeans->aLinesCenter;    //保存之前的聚类

        //聚类
        for(int i=0;i<pKmeans->iLineNum;++i)
        {
            int iCtrIndex=-1;
            double minDist=MAX_DISTANCE;
            
            for(int j=0;j<pKmeans->iCenterNum;++j)
            {
                double dist,centerSum=0,lineSum=0,crossSum=0;

                for(int k=0;k<pKmeans->iKeyNum;++k)
                {
                    crossSum+=pKmeans->aaLinesTf[i][k]*pKmeans->aCluCenter[j][k];
                    centerSum+=pKmeans->aCluCenter[j][k]*pKmeans->aCluCenter[j][k];
                    lineSum+=pKmeans->aaLinesTf[i][k]*pKmeans->aaLinesTf[i][k];
                }

                dist=crossSum/(sqrt(centerSum)*sqrt(lineSum));
                dist=1-dist;
                
                if(dist<minDist)
                {
                    iCtrIndex=j;
                    minDist=dist;
                }
            }

            pKmeans->aLinesCenter[i]=iCtrIndex;
            pKmeans->aiLinesInCenter[iCtrIndex]++;
            J+=minDist*minDist;
        }

        int iCnt=0;

        for(int i=0;i<pKmeans->iLineNum;++i)
        {
            if(pKmeans->aLinesCenter[i]==aPreLinesCenter[i])
            {
                iCnt++;
            }
        }

        if(iCnt==pKmeans->iLineNum)
        {
            break;
        }

        //重新计算各聚类中心点
        for(int i=0;i<pKmeans->iCenterNum;++i)
        {
            for(int j=0;j<pKmeans->iKeyNum;++j)
            {
                pKmeans->aCluCenter[i][j]=0;
            }
        }

        for(int i=0;i<pKmeans->iLineNum;++i)
        {
            for(int j=0;j<pKmeans->iKeyNum;++j)
            {
                pKmeans->aCluCenter[pKmeans->aLinesCenter[i]][j]+=pKmeans->aaLinesTf[i][j];
            }
        }

        //求均值,更新聚类中心
        for(int i=0;i<pKmeans->iCenterNum;++i)
        {
            if(pKmeans->aLinesCenter[i]!=0)
            {
                for(int j=0;j<pKmeans->iKeyNum;++j)
                {    
                    pKmeans->aCluCenter[i][j]/=pKmeans->aiLinesInCenter[i];
                }
            }
        }

        if(iTimes!=0 && --iLeftTimes==0)
        {
            break;
        }

        if(iThreshold!=0 && lastJ!=-1 && fabs(lastJ-J)<iThreshold)
        {
            break;
        }

        lastJ=J;
    }
}

int main()
{
    Kmeans test;

    string input[]=
    {
        "奥运 拳击 入场券 基本 分罄 邹市明 夺冠 对手 浮出 水面",
        "股民 要 清楚 自己 的 目的",
        "印花税 之 股民 四季",
        "杭州 股民 放 鞭炮 庆祝 印花税 下调",
        "残疾 女 青年 入围 奥运 游泳 比赛 创 奥运 历史 两 项 第一",
        "介绍 一 个 ASP.net MVC 系列 教程",
        "在 asp.net 中 实现 观察者 模式 ,或 有 更 好 的 方法 (续)",
        "输 大钱 的 股民 给 我们 启迪",
        "Asp.Net 页面 执行 流程 分析",
        "运动员 行李 将 “后 上 先 下” 奥运 相关 人员 行李 实名制",
        "asp.net 控件 开发 显示 控件 内容",
        "奥运 票务 网上 成功 订票 后 应 及时 到 银行 代售 网点 付款",
        "某 心理 健康 站 开张 后 首 个 咨询 者 是 位 新 股民",
        "ASP.NET 自定义 控件 复杂 属性 声明 持久性 浅析"
    };
/*
    for(int i=0;i<14;++i)
    {
        stringToLower(input[i]);
        cout<<input[i]<<endl;
    }
*/

    vector<string> allLines(input,input+sizeof(input)/sizeof(string));

    kMeansInit(&test,allLines,sizeof(input)/sizeof(string),3);
    kMeansMainLoop(&test,0,0);

    for(int i=0;i<test.iCenterNum;++i)
    {
        cout<<"Center "<<i<<" : "<<endl;

        for(int j=0;j<test.iLineNum;++j)
        {
            if(test.aLinesCenter[j]==i)
            {
                cout<<test.asAllLines[j].first<<endl;
            }
        }

        cout<<endl;
    }

    return 0;
}

http://topic.csdn.net/u/20110906/11/89e01a81-923d-4cfb-b0ed-3dd295c1fd98.html

初始化

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值