马尔科夫链算法

     这里介绍的马尔科夫链算法实现的功能是:读入一段英文文本,构造出由这个文本中语言使用情况而形成的统计模型,然后根据统计模型随机输出另一段文本。

     马尔科夫链算法的基本思想是:将输入想象成一些相互重叠的短语构成的序列,把每个短语分割为两个部分:一部分是由多个词构成的前缀,另一部分是只包含一个词的后缀。马尔科夫链算法能够生成输出短语的序列,其方法是依据原文本的统计性质,随机地选择跟在前缀后面的特定后缀。采用三个词的短语就能够很好工作,这三个词中前两个词构成前缀来选择作为后缀的一个词设置:
w1和w2为文本的前两个词

输出w1和w2
循环:
  随机地选出w3,它是文本中w1w2的后缀中的一个
  打印w3
  把w1和w2分别换成w2和w3
  重复循环

 

选择二词前缀,则每个输出词w3都是根据它前面的一对词(w1,w2)得到的。前缀中词的个数对设计本身并没有影响,程序应该能对付任意的前缀长度。我们把一个前缀和它所有可能后缀的集合放在一起,称其为一个状态。
为了说明问题,假设我们要基于本章开头的引语里的几个句子生成一些随机文本。这里采用
的是两词前缀:
Show your flowchars and conceal your tables and I will be mystified. Show your tables and your flowcharts will be obvious. (end)
下面是一些输入的词对和跟随它们之后的词:
输入前缀                           跟随的后缀
Show your                         flowcharts tables
your flowcharts                  and will
flowcharts and                   conceal
flowcharts will                    be
your tables                         and and
will be                               mystified. obvious.
be mystified                       Show
be obvious                         (end)
处理这个文本的马尔可夫算法将首先打印出 Show   your,然后随机取出flowcharts或table。如果选中了前者,那么现在前缀就变成 your  flowcharts,而下一个词应该是and或will。如果它选取 tables,下一个词就应该是 and。这样继续下去,直到产生出足够多的输出,或者在找后缀时遇到了结束标志。

C++实现:
#include <iostream>
#include <map>
#include <deque>
#include <vector>
#include <string>
#include <cstdio>
#include <sstream>
#include <ctime>
using namespace std;

enum {
     NPREF  =  2 ,           //前缀的个数
     MAXGEN  =  10000  //最多有那么多个词
};  

typedef deque<string> Prefix; 
map<Prefix,vector<string> > statetab; // 使用duque 作为第一项方便前缀词数扩展和改变,这里statetab将前缀和后缀关联起来

char  NONWORD[] = "\n"; //伪前缀,将真正的输入作为它的后缀

void add(Prefix &prefix, const string &s)
{
     if(prefix.size() == NPREF) //当前缀数目达到指定词数之后,改变前缀,也就是w1 w2 变成 w2 w3
     {
          statetab[prefix].push_back(s);
          prefix.pop_front();
     }
     prefix.push_back(s);
}

void build(Prefix &prefix, istream &in)//构造统计表
{
     string buf;
     while(in >> buf)
          add(prefix,buf);
}

void generate(int nwords)
{
     Prefix prefix;
     int i;
     srand(time(NULL));
     for(i = 0; i < NPREF; i++)
          add(prefix,NONWORD);//生成伪前缀
     //cout<<"NOW prefix: "<<prefix[0]<<" "<<prefix[1]<<endl;
     for(i = 0; i < nwords; i++)
     {
          vector<string> & suf = statetab[prefix];
          //cout<<"prefix:"<<prefix[0]<<" suf "<<suf[0]<<endl;
          const string &w = suf[rand() % suf.size()];//在所有可能的后缀中随机的选择一个词
          if(w == NONWORD)
               break;
          cout << w <<" ";
          prefix.pop_front();//改变前缀
          prefix.push_back(w);
     }
     cout<<endl;
}

int main()
{
     int nwords = MAXGEN;
     Prefix prefix;
     string sentence = "Show your flowchars and conceal your tables and I will be mystified. Show your tables and your flowcharts will be obvious. (end)";
     istringstream in(sentence);
     //freopen("mkov.txt","r",stdin);
     for(int i = 0; i < NPREF; i++)
          add(prefix,NONWORD);
     build(prefix,in);
     add(prefix,NONWORD);
     generate(nwords);
     return 0;
}

 

运行结果1:

运行结果2:

posted @ 2012-06-18 22:27  KingsLanding  阅读(10510)  评论(0编辑  收藏  举报