马尔科夫链算法

来源:互联网 发布:php解密工具 编辑:程序博客网 时间:2024/05/16 14:27

       重新把《编程珠玑》读了一遍,以前并没有仔细研究最后一章的生成随机文本,昨天仔细读了一下,感悟颇深,想记录一下自己的感悟,顺便理清一下思路。

     言归正传,要通过读取一个文档来生成一个随机的文档,作者使用的方法是根据k连单词的后一个单词的出现概率来选取下一个单词。作者在书中用的方法是读取之后,对数组进行排序,那么前k个单词相同的子串一定是相邻的,然后通过二分查找,找到第一个子串,通过随机选取的方法选一个子串输出。但是在习题中,作者给出了更快的方法,用哈希表来代替排序和查找的过程。之所以被称作“马尔科夫链算法”是因为每个状态表示一个k连字母,并且从一个状态到另一个状态的概率是不变的。

     读取完文本之后,将读取的数据放入到一个哈希表中,值得注意的是,作者给出了常见的哈希冲突的解决方法:通过维护一个大数组来解决冲突,而不是一般数据结构书中介绍的使用链表。代码如下:

for(i=0;i<=nword-k;i++){j=hash(word[i]);next[i]=bin[j];bin[j]=i;}

查找hash表时,bin[k]=i1,next[i1]=i2,next[i2]=i3······一直下去就可以找出哈希值同是k的所有项。

       在输出时,通过查找这个hash表,并通过概率的判断来决定下一个单词的输出,如果同一个单词多次出现,那么会通过多次的判断,来增加被选中的概率。通过判断“哨兵”来判断是否已经结束,“哨兵”是由k个0构成,放在输入文本的最后。

#include <stdio.h>#include<stdlib.h>#include "string.h"char inputchars[4300000];#define MAXWORDS 800000char *word[MAXWORDS];int nword = 0;int k = 2;int next[MAXWORDS];#define NHASH 499979int bin[NHASH];#define MULT 31int hash(char * w){unsigned int h=0;int n;for(n=k;n>0;w++){h=h*MULT+*w;if(*w==0)n--;}return h%NHASH;}int wordncmp(char *p,char *q) //判断前K个单词是否相同{int n=k;for(;*p==*q;p++,q++)if(*p==0 && --n==0)return 0;return *p-*q;}char *skip(char *p, int n) //跳过n个单词{for ( ; n > 0; p++)      if (*p == 0)      n--;    return p;}int main(){int i,j,wordleft;char *phrase,*p;word[0]=inputchars;while(scanf("%s",word[nword])!=EOF){word[nword+1]=word[nword]+strlen(word[nword])+1;nword++;}for(i=0;i<k;i++)word[nword][i]=0;memset(bin,-1,NHASH);for(i=0;i<=nword-k;i++){j=hash(word[i]);next[i]=bin[j];bin[j]=i;}for(i=0;i<k;i++)printf("%s ",word[i]);phrase=inputchars;for(wordleft=1000;wordleft>0;wordleft--){i=0;for(j=bin[hash(phrase)];j>0;j=next[j]){if((wordncmp(phrase,word[j])==0) && (rand()%(++i)==0))p=word[j];}if(strlen(skip(p,k))==0)break;printf("%s ",skip(p,k));phrase=skip(p,1);}return 0;}
这段程序对输出的文档做出了词数的限制,并且在检查到哨兵就跳出循环,输出结束。


原创粉丝点击