3.2 数据结构的选择

3.2 数据结构的选择

    对于马尔科夫链的输入,我们就不得不考虑有多少输入需要处理,程序应该运行的多快。假定我们的输入文本有100,000个词,我们还想让代码在几秒之内完成。那么就注定这不会是一个简单的算法。

    以下是书中的摘录:

    马尔可夫算法必须在看到了所有输入之后才能开始产生输出。所以它必须以某种形式存储整个输入。一个可能的方式是读完整个输入,将它存为一个长长的字符串。情况的另一方面也很明显,输入必须被分解成词。如果另用一个指向文本中各词的指针数组,输出的生成将很简单:产生一个词,扫描输入中的词,看看刚输出的前缀有哪些可能的后缀,然后随机选取一个。但是,这个方法意味着生成每个词都需要扫描100,000个输入词。1000个输出就意味着上亿次字符串比较。这样做肯定快不了。

    另一种可能性是存储单个的词,给每个词关联一个链表,指出该词在文本中的位置。这样就可以对词进行快速定位。在这里可以使用第二章提出的散列表。但是,这种方式并没有直接触及到马尔可夫算法的需要。在这里最需要的是能够由前缀出发快速地确定对应的后缀。 例如这样:

    当然,你得做好空间消耗极大的准备,如果你被限定了空间消耗呢?虽然这看起来不可能发生。

const long long HASH = 1000;/*The number of the words in your text.*/

typedef struct Nameval Nameval;
struct Nameval
{
	char *name;
	int position;
	Nameval *next;
};
Nameval *symtab[HASH];

    根据以上的分析,我们需要一种数据结构,它能较好的表示前缀以及与之相关联的后缀。我们的程序将分为两部分,第一部分是输入,它构造表示短语的整个数据结构;第二部分是输出,生成随机的输出。

    这两部分都需要(快速地)查询前缀:输入过程中需要更新与前缀相关的后缀;输出时需要对可能后缀做随机选择。这些分析提醒我们使用一种散列结构,其关键码是前缀,其值是对应于该前缀的所有可能后缀的集合。

    为了描述的方便,我们将假定采用二词前缀,在这种情况下,每个输出词都是根据它前面的一对词得到的。前缀中词的个数对设计本身并没有影响,程序应该能对付任意的前缀长度,但给定一个数能使下面的讨论更具体些。我们把一个前缀和它所有可能后缀的集合放在一起,称其为一个状态,这是马尔可夫算法的标准术语。

    对于一个特定的前缀,我们需要储存所有能跟随它的后缀,以便将来取用。这些后缀都是无序的,由于我们不知道会有多少后缀出现,因此我们需要链表或者动态数组这种可以高效可增长的数据结构。在产生输出的时候,我们要能从关联于特定前缀的后缀集合中随机地选出一个后缀。还有,数据项绝不会被删除。

    现在,我们来考虑一下特殊情况。如果一个短语“might appear twice”出现了两次。我们的解决方法是,在“might appear”这个短语的后缀表中放置两个“twice”,当然,“twice”所对应的后缀位置应该是不相同的。

    总结一下:

    每个状态由一个前缀和一个后缀链表组成。所有这些信息存在一个散列表里,以前缀作为关键码。每个前缀是一个固定大小的词集合。如果一个后缀在给定前缀下的出现多于一次,则每个出现都单独包含在有关链表里。

    接下来的小节将会是代码,我建议代码之前,至少应该了解可增长数组、表、散列表、马尔科夫链算法基本。他们在我之前的笔记中都有呈现,我还是推荐去看一下。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值