在《编程珠玑》的第15章有介绍随机生成文本,我上网搜了一下,发现这篇文章,转载一下。
原文: http://blog.csdn.net/z_york/archive/2008/06/14/2546764.aspx
上个星期一个偶然的机会,看到一篇介绍“随机文本生成“的东西(Generating Text ,“随机”二字是我画蛇添足加上去的),挺有趣的,加之这段时间比较有研究算法的兴头,所以就认真地学习了一下,现在把自己的理解和思考 记录下来。
补充一下,Generating Text 是Programming Pearls 一书(相当有名)的其中一节,该书英文全版可以免费在线阅读。
什么是”随机文本“?作者提到的随机文本有两个层次,一个是letter-level,一个是word-level,当然都是针对英语这一自然语言 而言的。所谓letter-level就是”随机“字母拼成的字母序列,word-level就是”随机“英语单词拼成的文本。
可能读者会问,这还不容易?对于letter-level,就是从字母表里面random出一个又一个字母,对于word-level,相同的做 法。 是的,这样干确实够随机够脑残的了,但这样生成的文本很可能没有任何”意义“,请注意,我上面是把”随机“两字特地加了引号,而”意义“两字也特地加了引 号,就是说——这里说的”随机文本“是一种有”意义“的”随机文本“。
太饶舌了,举几个个例子(从上述文章中摘出):
完全没有”意义“的”随机文本“:
uzlpcbizdmddk njsdzyyvfgxbgjjgbtsak rqvpgnsbyputvqqdtmgltz ynqotqigexjumqphujcfwn ll jiexpyqzgsdllgcoluphl sefsrvqqytjakmav bfusvirsjl wprwqt
稍稍有点”意义“的”随机文本“:
saade ve mw hc n entt da k eethetocusosselalwo gx fgrsnoh,tvettaf aetnlbilo fc lhd okleutsndyeoshtbogo eet ib nheaoopefni ngent
saade ve mw hc n entt da k eethetocusosselalwo gx fgrsnoh,tvettaf aetnlbilo fc lhd okleutsndyeoshtbogo eet ib nheaoopefni ngent
比较有”意义“的”随机文本“:
Ther I the heingoind of-pleat, blur it dwere wing waske hat trooss. Yout lar on wassing, an sit. " " Yould, " " I that vide was nots ther
Ther I the heingoind of-pleat, blur it dwere wing waske hat trooss. Yout lar on wassing, an sit." "Yould," "I that vide was nots ther
容易看得出来,作者所认为的有”意义“,就是所生成的文本比较接近自然语言(这里是指英语):对letter-level而言,就是生成的字母序列 比较接近自然语言的规则,就英语来说,就是辅音字母和元音字母的某种微妙的交替出现(如:wh、th、qu、ey、al等等);对word-level来 说,就是单词在文本中出现的顺序符合或部分符合自然语言的文法,读者能读到似是而非的意思(如:i 后面经常出现的是 am,go 后面经常出现的是 to 等等诸如此类的规则)。
好了,简言之,这里讨论的”随机文本生成“,就是指生成那些部分符合自然语言规则,带有似是而非意思的文本(这是本人的体会,非文章本身的定 义)。
为什么要”随机生成文本“?作者并没有着墨太多,举例的几个软件也没有用过,所以就不说了,倒是我碰到过一个这样的应用实例,在本文末尾再提。
如何”随机文本生成“?由前文可知,简单的random脑残大法是没办法达到目标的,那种方法最倾向于产生第一种毫无“意义”的文本。作者给出的算 法是基于“马尔可夫链”和“香农理论”,事实上,本人限于数学水平有限,并不能弄明白这些理论(香农那个倒还学过,属于通信原理里面的内容,马尔可夫就完 全是空白),所以不在这里浪费时间了。倒是关于马尔可夫链,我还看过另一个有趣的帖子:用马尔可夫链和蒙特卡罗模 拟求解天赋"燃烧"和"疾驰热量"的量化价值 ,推荐玩过魔兽世界的同好们观摩……)。
言归正传,虽然理论看似艰深,但作者描述起来倒很简单很形象很好理解,给出的代码也很短小精干。先描述算法。
为了达到生成“有意义”的文本的目的,作者认为算法必须基于一个“输入文本”(这也是马尔可夫链理论吧……),而不是凭空地从字母表/单词表中生造 文本,用形象的语言描述该过程(就letter-level来说),就是:设“输入文本”是一本书(厚的比较好),随机翻一页,随机挑一个字母,输出,并 记之为“当前字母”,翻到另一页(非下一页,而是随机的另一页),寻找“当前字母”出现的位置,如果“当前字母”出现多次,则随机选一个,然后向后看k个 字母,把那第k个字母输出,并把新的字母记为“当前字母”,然后继续翻另一页,直到书翻完了或输出的字母总数达到某预设值,程序结束。
看起来很简单的算法,即使对于某些特殊情况没有特别说明:比如当前字母在下一页没有出现怎么办,或者向后看k个字母失败——已经超出该页了,等等, 仍然可以从作者给出的例子代码中找到完整的算法描述:
补充几点,上述程序都是用C语言写的,作者写得很紧凑。对于第一个程序,用的是简单的扫描法去找“当前字母”在“下一页”的新位置,每次都扫描一次 “输入文本”全文,所以效率其低,仅仅作为一个算法演示而已;第二个程序用到了散列表,效率不错,我认为有相当的实用价值,另外作者实现简易散列表的方法 非常娴熟非常牛逼,实乃我等低端职业程序员居家旅行必备之参考。
我照着上述两个程序依样画葫芦地用python重新实现了一遍(主要是闲得蛋疼拿这个练习一下python),有兴趣的可以点这里 下载,在WinXP+python3.0下 编写和测试,使用方法见文件的“__main__”段,很简单的程序。
回到“随机文本生成到底拿来干什么”这个现实问题,我前段时间恰好碰到一个实例,有一个叫“Fate”(中文译名“黑暗诗史”,在迅雷一搜就有)的 仿暗黑2ARPG游戏,在创建角色的时候,除了可以自己输入角色名外,还可以让它代你生成一个随机的名字,我试了一下,效果不错,能随机生成一些非常见的 英文名字,而且好像还是随机的,当时就觉得挺过瘾的,后来看到了Generating Text一文,才想起它可能用到的算法。
我是这样想的,如果用脑残算法,从一个现成英文名字大全表中random一个出来,很难满足我等暗黑玩家的暗黑心理——头上顶着诸如Adam、 Bob、Carl、Dave这类初中英语课本里面的名字,哪有勇气杀入巴尔王座啊?Fate的程序员当然也知道,所以他随机出来的名字都是有别于常见名字 的,而拼法上又基本符合一般英语规则,比如Zerandra、qwarkty、hypheni这类比较装逼的名字。参考一下上面提到的理论和算法,我想了 下面一种可能的算法(没有动手去实现过测试过,纯属YY):
1、当然需要一份“输入文本”,这里把一份“男女英文名字大全(如果有的话)”作为输入文本
Aariz
Aaron
Abayomi
Abderrahim
Abdiel
Abdul
Abdullah
Abe
...
Babbette
Babette
Babs
Babsi
Baby
Babydoll
Bahar
Bailee
Bailey
Balbina
...
...
...
Zac
Zach
Zachariah
Zacharias
Zachary
Zachery
Zack
Zackary
Zackery
Zade
Zae
...
2、对“输入文本”进行一趟扫描生成一散列表,散列表的key是字母(A-Z),value是该字母的坐标(所在名字下标+所在位置下标)的数组, 比如对于字母'A'这个key,其对应的value是:
[ (0, 0), (0, 1), (1, 0), (1, 1), ...] —— 表示字母'A'出现的位置是第0个单词第0个字母、第0个单词第1个字母、第1个单词第0个字母等等,如此类推。
3、开始生成名字: