马尔科夫链算法

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

     言归正传,要通过读取一个文档来生成一个随机的文档,作者使用的方法是根据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 800000
char *word[MAXWORDS];
int nword = 0;
int k = 2;

int next[MAXWORDS];
#define NHASH 499979
int bin[NHASH];
#define MULT 31

int 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;
}
这段程序对输出的文档做出了词数的限制,并且在检查到哨兵就跳出循环,输出结束。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值