马尔科夫链随机文本生成器

说明:

有一种基于马尔可夫链算法的随机文本生成方法,它利用任何一个现有的某种语言的文本(如一本英文小说),可以构造出由这个文本中的语言使用情况而形成的统计模型,并通过该模型生成的随机文本将具有与原文本类似的统计性质(即具有类似写作风格)。

该算法的基本原理是将输入看成是由一些互相重叠的短语构成的序列,其将每个短语分割为两个部分:一部分是由多个词构成的前缀,另一部分是只包含一个词的后缀。在生成文本时依据原文本的统计性质(即前缀确定的情况下,得到所有可能的后缀),随机地选择某前缀后面的特定后缀。在此,假设前缀长度为两个单词,则马尔可夫链(Markov Chain)随机文本生成算法如下:

设w1和w2为文本的前两个词

输出w1和w2

循环:

随机地选出w3,它是原文本中w1w2为前缀的后缀中的一个

输出w3

w1 = w2

w2 = w3

重复循环

下面将通过一个例子来说明该算法原理,假设有一个原文如下:

Show your flowcharts and conceal your tables and I will be mystified. Show your tables and your flowcharts will be obvious.

下面是上述原文的一些前缀和其后缀(注意只是部分)的统计:


Prefix


Suffix


Show your


flowcharts  tables


your flowcharts


and  will


flowcharts and


conceal


flowcharts willl


be


your tables


and  and


will be


mystified.  obvious.


be mystified.


Show


be obvious.


(end)

基于上述文本,按照马尔可夫链(Markov Chain)算法随机文本生成文本时,首先输出的是Show your,然后随机取出flowcharts或tables。如果为前者,则接下来的前缀就变成your flowcharts,而下一个后缀应该是and或will;如果为tables,则接下来的前缀就变成your tables,而下一个词就应该是and。这样继续下去,直到产生出足够多的输出,或在查找后缀时遇到了结束标志。

编写一个程序从文件中读入一个英文文本,利用马尔可夫链(Markov Chain)算法,基于文本中固定长度的短语的出现频率,生成一个最大单词数目不超过N的新文本到给定文件中。程序要求前缀词的个数为2,最大单词数目N由标准输入获得。

说明:

  1. 为了得到更好的统计特性,在此标点符号等非字母字符(如’ “ . , ? – ()等)也被看成单词的一部分,即“words”和“words.”是不同的单词。因此,在此将“词”定义为由“空白界定的字符串”;
  2. 对于同一个前缀的后缀按出现顺序排放(不管该后缀是否已存在);
  3. 在处理文本时,文件结束标志也将作为某一前缀的一个后缀,如上面示例(说明:在为文件最后两个前缀单词“be obvious.”读取后缀时,遇到文件结束,即其没有相应后缀,此时可用一个特殊标记来表示其后缀,如,可存储一个自定义的特殊串(如“(end)”)作为其后缀来表示当前状态,即文件结束);
  4. 对于某一前缀,按如下方式来随机选择其后缀(如果某一前缀只有一个后缀,将直接选择该后缀):

n = (int)(rrand() * N);

在此N为某一前缀的所有后缀的总数,n为所确定的后缀在该前缀的后缀序列中的序号(从0开始计数,即n为0时选取第一个后缀,为1时选取第二个后缀,以此类推)。在此,随机数生成函数rrand()的定义如下:

double seed = 997;

double rrand()
{
    double lambda = 3125.0;
    double m = 34359738337.0;
    double r;
    seed = fmod(lambda*seed, m); //要包含头文件#include <math.h>
    r = seed/ m;
    return r;
}

注意:为了保证运行结果的确定性,请务必使用本文提供的随机数生成函数。

在下面条件满足时文本生成结束:1)遇到后缀为文件结束标志;或2)生成文本的单词数达到所设定的最大单词数。在程序实现时,当读到文件(结束)尾时,可将一个特殊标志赋给后缀串suffix变量。

【输入形式】

创建英文文本文件“article.txt”进行统计分析,并从标准输入中读入一个正整数作为生成文本时的最大单词数。

【输出形式】

将生成文本输出到当前目录下文件“markov.txt”中。单词间以一个空格分隔,最后一个单词后空格可有可无。

【样例输入】

若当前目录下文件article.txt中内容如下:

I will give you some advice about life.

Eat more roughage;

Do more than others expect you to do and do it pains;

Remember what life tells you;

do not take to heart every thing you hear.

do not spend all that you have.

do not sleep as long as you want;

Whenever you say "I love you", please say it honestly;

Whevever you say "I am sorry", please look into the other person‘s eyes;

Whenever you find your wrongdoing, be quick with reparation!

Whenever you make a phone call smil when you pick up the phone, because someone feel it!

Understand rules completely and change them reasonably;

Remember, the best love is to love others unconditionally rather than make demands on them;

Comment on the success you have attained by looking in the past at the target you wanted to achieve most;

In love and cooking, you must give 100% effort - but expect little appreciation.

从标准输入中输入的单词个数为:

1000

【样例输出】

当前目录下所生成的文件markov.txt中内容如下:

I will give you some advice about life. Eat more roughage; Do more than others expect you to do and do it pains; Remember what life tells you; do not take to heart every thing you hear. do not take to heart every thing you hear. do not spend all that you have. do not sleep as long as you want; Whenever you find your wrongdoing, be quick with reparation! Whenever you find your wrongdoing, be quick with reparation! Whenever you find your wrongdoing, be quick with reparation! Whenever you find your wrongdoing, be quick with reparation! Whenever you say "I am sorry", please look into the other person‘s eyes; Whenever you say "I am sorry", please look into the other person‘s eyes; Whenever you make a phone call smil when you pick up the phone, because someone feel it! Understand rules completely and change them reasonably; Remember, the best love is to love others unconditionally rather than make demands on them; Comment on the success you have attained by looking in the past at the target you wanted to achieve most; In love and cooking, you must give 100% effort - but expect little appreciation.

【样例说明】

按照本文介绍的马尔可夫链(Markov Chain)算法将生成相关输出文件。

【使用什么数据结构?】

使用如上图所示的数据结构,建立一个数据结构State存储状态和一个后缀链表Suffix,这样建立数据结构的原因是每通过hash找到一个前缀pref的State,就可以通过这个这个State的suf链表寻找随机生成的后缀,另外为了加快速度也可以用二叉搜索树代替这个这个suf后缀链表。这里为了加快速度用了一些技巧,就是在State加了一个变量SufNum,这个变量的目的就是使插入的时候不用按照传统链表插入到末尾这种方法,而是直接在头部插入,读取的时候通过(SufNum-the_index_you_want)次next操作就可以找到所需要的后缀Suf了。

说起来很简单,但实现起来还是十分的困难,作者在代码里加了两个C语言常用的函数memcpy和strdup

以下是 memcpy() 函数的声明。

void *memcpy(void *str1, const void *str2, size_t n)
参数
str1 -- 这是指针数组,其中的内容将被复制到目标,类型强制转换为void*类型的指针。

str2 -- 这是要复制的数据源的指针,void*类型的指针型铸造。

n -- 这是要被复制的字节数。

返回值
这个函数返回一个指针到目的地,str1。

可参考网址 http://www.cplusplus.com/reference/cstring/memcpy/

Example Code:

#include <stdio.h>
#include <string.h>

int main ()
{
   const char src[50] = "test";
   char dest[50];
   printf("Before:%s\n", dest);
   memcpy(dest, src, strlen(src)+1);
   printf("After: %s\n", dest);
   return(0);
}

结果:

Before:

After: test

strdup是个字符串的复制,这个函数会单独alloc一块新的记忆体,不像strcpy函数一样需要自己准备两个记忆体。

调用之后需要用free()函数释放掉。

#include <string.h>
#include <assert.h>
#include <stdlib.h>

int main(void)
{
    const char *s1 = "String";
    char *s2 = strdup(s1);
    assert(strcmp(s1, s2) == 0);
    free(s2);
}

部分函数还使用了inline内联函数

inline函数优点:
    传统程序的函数调用需要不停的调用栈,当有函数需要频繁调用的时候,那就会导致栈溢出或者效率不高等其他问题,用inline函数相当于把函数源代码直接“嵌入”到函数调用点
inline函数缺点:
       如果调用inline函数的地方过多,也可能造成代码膨胀。

有了如上基础之后我们可以从建立State的hash表,然后编写增加后缀函数和查找函数,就可以实现马尔可夫链随机文本的生成了。

这里的hash方法使用的是NHASH为5000011的BKDR算法,写有很多效率更高的方法,可以上网去寻找替代。

博主的代码:

  1 #include <math.h>
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5 const int NHASH = 5000011;
  6 const int PREFIX_NUM = 2;
  7 typedef struct State State;
  8 typedef struct Suffix Suffix;
  9 struct State {
 10   char *pref[PREFIX_NUM];
 11   Suffix *suf;
 12   State *next;
 13   unsigned int sufNum;
 14 };
 15 struct Suffix {
 16   char *word;
 17   Suffix *next;
 18 };
 19 State *statetab[NHASH];
 20
 21 /*利用了BKDR HASH方法,这里还可以使用别的HASH方法减少冲突*/
 22 unsigned int hash(char *s[PREFIX_NUM]) {
 23   unsigned int seed = 131;
 24   unsigned int hash = 0;
 25   unsigned int i;
 26   for (i = 0; i < PREFIX_NUM; i++) {
 27     char *str = s[i];
 28     while (*str)
 29       hash = hash * seed + (*str++);
 30   }
 31   return hash % NHASH;
 32 }
 33
 34 /*查找前缀数组prefix[PREFIX_NUM]是否在哈希表中出现*/
 35 State *lookup(char *prefix[PREFIX_NUM], int isBuild) {
 36   /*If isBuild is true,it will be a new node*/
 37   int i, h;
 38   h = hash(prefix);
 39   State *sp = statetab[h];
 40   while (sp != NULL) {
 41     for (i = 0; i < PREFIX_NUM; ++i) {
 42       if (strcmp(prefix[i], sp->pref[i]))
 43         break;
 44     }
 45     if (i == PREFIX_NUM) //找到了就返回
 46       return sp;
 47     sp = sp->next;
 48   }
 49   if (isBuild) {
 50     sp = malloc(sizeof(State));
 51     for (i = 0; i < PREFIX_NUM; ++i) {
 52       sp->pref[i] = prefix[i];
 53     }
 54     sp->suf = NULL;
 55     sp->sufNum = 0;
 56     sp->next = statetab[h]; //头插法
 57     statetab[h] = sp;
 58   }
 59   return sp;
 60 }
 61
 62 /*直接在头结点插入后缀,减少插入时间*/
 63 State *addsuffix(State *sp, char *suffix);
 64 inline State *addsuffix(State *sp, char *suffix) {
 65   Suffix *suf = malloc(sizeof(Suffix));
 66   suf->word = suffix;
 67   suf->next = sp->suf;
 68   sp->sufNum++;
 69   sp->suf = suf;
 70   return sp;
 71 }
 72
 73 /*往数据结构中插入一个新的项,使用inline内联函数加快速度*/
 74 void add(char *prefix[PREFIX_NUM], char *suffix);
 75 inline void add(char *prefix[PREFIX_NUM], char *suffix)
 76 {
 77   State *sp = NULL;
 78   sp = lookup(prefix, 1);
 79   sp = addsuffix(sp, suffix);
 80   // memmove(prefix, prefix + 1, sizeof(prefix[0]));
 81   memcpy(prefix, prefix + 1, sizeof(prefix[0]));
 82   prefix[1] = suffix;
 83 }
 84
 85 double seed = 997;
 86
 87 /*如上面所要求的随机生成器*/
 88 double rrand() {
 89   double lambda = 3125.0;
 90   double m = 34359738337.0;
 91   double r;
 92   seed = fmod(lambda * seed, m); //要包含头文件#include <math.h>
 93   r = seed / m;
 94   return r;
 95 }
 96
 97 void build(char *prefix[PREFIX_NUM], FILE *f);
 98 inline void build(char *prefix[PREFIX_NUM], FILE *f) {
 99   char buf[40];
100   while (fscanf(f, "%39s", buf) != EOF) {
101     add(prefix, strdup(buf));
102   }
103 }
104
105 /*从这里生成markov链*/
106 void generate(int nwords, FILE *OUT) {
107   State *sp;
108   char *prefix[PREFIX_NUM], *w;
109   unsigned int i;
110   for (i = 0; i < PREFIX_NUM; ++i)
111     prefix[i] = "\0";
112
113   for (i = 0; i < nwords; ++i) {
114     sp = lookup(prefix, 0);
115     Suffix *suf = sp->suf;
116     if (sp->sufNum == 1) {
117       w = suf->word;
118     } else {
119       int n = sp->sufNum - (int)(rrand() * sp->sufNum) - 1;
120       while (suf != NULL) {
121         if (n == 0) {
122           w = suf->word;
123           break;
124         }
125         suf = suf->next;
126         n--;
127       }
128     }
129     if (strcmp(w, "(end)") == 0)
130       break;
131     fprintf(OUT, "%s ", w);
132     memcpy(prefix, prefix + 1, (PREFIX_NUM - 1) * sizeof(Suffix));
133     // strcpy(prefix[0], prefix[1]);
134     prefix[1] = w;
135   }
136 }
137
138 int main() {
139   unsigned long int nwords;
140   unsigned int i;
141   scanf("%lu", &nwords);
142   char *prefix[PREFIX_NUM];
143   for (i = 0; i < PREFIX_NUM; ++i)
144     prefix[i] = "\0";
145   FILE *in = fopen("article.txt", "r");
146   build(prefix, in);
147   fclose(in);
148   /*末尾处加(end)*/
149   add(prefix, "(end)");
150   FILE *out = fopen("markov.txt", "w");
151   generate(nwords, out);
152   fclose(out);
153   return 0;
154 }

原文地址:https://www.cnblogs.com/adamwong/p/10199942.html

时间: 2024-10-14 01:17:11

马尔科夫链随机文本生成器的相关文章

使用马尔科夫链生成文本

马尔科夫链是一种随机模型,能根据先前的事件单独预测一个时间. 用马尔科夫链生成文本也是采用了相同的理念,努力找到一个词出现在另一个词后面的概率. 代码包含两部分,一个用来训练,一个用来生成 #!/usr/bin/env python # coding:utf8 # author:Z time:2018/10/27 import random#训练模型 model={'START': ['i', 'you'], 'i': ['like'], 'like': ['to'], 'to': ['eat'

马尔科夫链和隐马尔可夫模型(转载)

马尔可夫模型是由Andrei A. Markov于1913年提出的 ?? 设 SS是一个由有限个状态组成的集合 S={1,2,3,-,n?1,n}S={1,2,3,-,n?1,n} 随机序列 XX 在 tt时刻所处的状态为 qtqt,其中 qt∈Sqt∈S,若有: P(qt=j|qt?1=i,qt?2=k,?)=P(qt=j|qt?1=i)P(qt=j|qt?1=i,qt?2=k,?)=P(qt=j|qt?1=i) aij≥0∑jnaij=1aij≥0∑jnaij=1 则随机序列 XX构成一个一

MCMC(二)马尔科夫链

MCMC(一)蒙特卡罗方法 MCMC(二)马尔科夫链 MCMC(三)M-H采样和Gibbs采样(待填坑) 在MCMC(一)蒙特卡罗方法中,我们讲到了如何用蒙特卡罗方法来随机模拟求解一些复杂的连续积分或者离散求和的方法,但是这个方法需要得到对应的概率分布的样本集,而想得到这样的样本集很困难.因此我们需要本篇讲到的马尔科夫链来帮忙. 1. 马尔科夫链概述 马尔科夫链定义本身比较简单,它假设某一时刻状态转移的概率只依赖于它的前一个状态.举个形象的比喻,假如每天的天气是一个状态的话,那个今天是不是晴天只

【HDOJ6229】Wandering Robots(马尔科夫链,set)

题意:给定一个n*n的地图,上面有k个障碍点不能走,有一个机器人从(0,0)出发,每次等概率的不动或者往上下左右没有障碍的地方走动,问走无限步后停在图的右下部的概率 n<=1e4,k<=1e3 思路:据说是找规律     From https://blog.csdn.net/anna__1997/article/details/78494788 牛逼的证明 马尔科夫链的随机游走模型 可建立状态转移矩阵,对n * n 的图中n * n 个点编号为0 ~[ (n - 1) * n + n – 1]

PyMC:马尔科夫链蒙特卡洛采样工具

PyMC是一个实现贝叶斯统计模型和马尔科夫链蒙塔卡洛采样工具拟合算法的Python库.PyMC的灵活性及可扩展性使得它能够适用于解决各种问题.除了包含核心采样功能,PyMC还包含了统计输出.绘图.拟合优度检验和收敛性诊断等方法. 加qq群813622576或vx;tanzhouyiwean免费领取Python学习资料 特性 PyMC使得贝叶斯分析尽可能更加容易.以下是一些PyMC库的特性: 用马尔科夫链蒙特卡洛算法和其他算法来拟合贝叶斯统计分析模型. 包含了大范围的常用统计分布. 尽可能地使用了

马尔科夫链蒙特卡洛采样(MCMC)入门

1.从随机变量分布中采样 研究人员提出的概率模型对于分析方法来说往往过于复杂.越来越多的研究人员依赖数学计算的方法处理复杂的概率模型,研究者通过使用计算的方法,摆脱一些分析技术所需要的不切实际的假设.(如,正态和独立) 大多数近似方法的关键是在于从分布中采样的能力,我们需要通过采样来预测特定的模型在某些情况下的行为,并为潜在的变量(参数)找到合适的值以及将模型应用到实验数据中,大多数采样方法都是将复杂的分布中抽样的问题转化到简单子问题的采样分布中. 本章,我们解释两种采样方法:逆变换方法(the

《概率统计》基于马尔科夫链的近似采样

楔子 从这一篇开始,我们主要来介绍基于马尔科夫链的近似采样过程.具体如何采样,以及整个采样过程中的思维过程,我们随着这篇的内容讲解而逐步展开 马尔科夫链的稳态与采样的关系 马尔科夫链的平稳分布是一个意义非凡的重要特性,我们换个角度说明一下大家就能明白它的重要意义:也就是说无论我们的起始状态是位于状态 1.状态 2 还是状态 3,在状态转移矩阵 P 的作用下,经过足够大的 n 步转移之后,它处于三种状态的概率都是固定的 利用下面这幅图,我们可以把这个状态转移和到达稳态的过程表达得更加直白一些: 好

Chapter 4 马尔科夫链

4.1 引言 现在要研究的是这样一种过程: 表示在时刻的值(或者状态),想对一串连续时刻的值,比如:,, ... 建立一个概率模型. 最简单的模型就是:假设都是独立的随机变量,但是通常这种假设都是没什么根据的,也缺乏研究的意义. 举例来说的话,如果用来代替某个公司,比如Google,在个交易日之后的股票价格. 那么说第天的股票价格和之前第天,第天,第乃至第天的股票价格一点关系都没有,这样是说不过去的. 但是说第天股票的收盘价格依赖于第天的收盘价格还是有点道理的. 同样还可以做出这样的合理假设:在

马尔科夫链与蒙特卡洛方法采样算法

马尔科夫链 http://wenku.baidu.com/link?url=26MSlOhtBMPQJz3ta2p3bM6IMdLsvvHQ2mzw8AI2GcSdZAI7Ukdf1rl4KR6VUojnuutXwU5EqHNv-V0acHQn1PlkoyYT0j7DrVRWskg_Kr7&pn=50 蒙特卡洛采样算法 http://cos.name/2013/01/lda-math-mcmc-and-gibbs-sampling/ http://imbinwang.github.io/blog