统计

计算模拟历史

以前闲着无聊的时候曾经做过一个《资治通鉴》的字频统计,单以频率计,中国历史不过是“王”与“人”,“义”与“忠”,“将军”与“刺史”,“长安”与“洛阳”。

既然有了频率,自然也就有了概率和条件概率。根据条件概率,当给出一个序列的字词后,预测下一个字词是什么,就变成了一个简单的最大似然估计问题。如果觉得这个序列太长,计算起来太麻烦,可以假设简化的马尔科夫结构,譬如假设下一个词的概率取决与之前的n个词而不是整个序列,这基本上就是计算语言学里的 n-gram 算法了。

所以我们可以用《资治通鉴》作为语料得出经验条件概率,然后来随机模拟出历史文本,产生原汁原味(至少是统计意义上的)史书 (技术细节见附录)。 虽然这只是文字游戏,但是仍然能从概率上看出《资治通鉴》记述的历史中,最容易重现怎样的事件。

譬如下面这则 (random seed = 2000):

撰 刘 崇 俊 以 惟 岳 又 从 入 关 , 宣 等 从 太 子 也 , 惧 履 危 亡 之 事 , 发 步 骑 二 十 骑 自 北 至 北 寺 狱 , 竟 不 使 宗 庙 社 稷 。 宗 元 为 柳 州 司 马 。 坚 大 怒 , 士 气 彫 沮 , 无 事 , 更 为 后 拒 , 倍 急 于 亡 命 聚 众 二 万 会 麻 秋 、 姚 、 宋 赤 眉 将 逢 安 为 新 都 , 剽 掠 。

我们可以这样翻译:

刘崇俊因为惟岳(人名,可能是李惟岳。刘崇俊是五代人,李惟岳是晚唐人,相差不算太远)一起入关(姑且认为是潼关,但是李惟岳在和河北,真实历史是不会入关的),而宣(人名)等人却追随太子而去,害怕这是危亡社稷的事情,于是发步骑二十骑(区区二十骑!估计是武林高手),从北面到北寺狱(这是东汉时候黄门署属下的监狱,鞫禁将相大臣的,好吧,晚唐也有宦官之祸,这里东汉的宦官乱入了,不过二十骑到北寺狱,难道是要劫狱?),最终也没有拜谒宗庙(不把皇帝放在眼中啊)。

柳宗元被任命为柳州司马(二王八司马嘛,承接上文,还是和阉祸有关)。

(某)坚大怒(不知道是孙坚?苻坚?杨坚?),士气不振,好在也没有什么大事,继续抗拒(王师?)。 因急于亡命,聚众两万人与麻秋汇合(麻秋登场,那么坚应该是苻坚了,但是麻秋很早就被苻坚的伯伯苻健杀死了)。

(与此同时)姚、宋(姚崇,宋璟?)率领赤眉军把逢安(这个地名是自动产生的,历史上似乎没有,权且当作是四川 蓬安 吧)作为新的都城,四处剽掠。

我们来梳理一下这段模拟历史的脉络:

这大概是一个王朝末年的乱象。 地方农民起义(赤眉),建立政权(所以有新都),负责讨伐的将领反而形成军阀割据,一些军阀随权臣(刘崇俊)入京,干预朝政(惟岳),一些军阀在地方反叛(坚),勾结外敌(麻秋是羯族人)。这一切的原因可能是因为朝廷宦官弄权已久(北寺狱),忠良被贬(柳宗元)。 军阀入京大概是打着清除宦官的名义(所以要发兵北寺狱),但是同时他们也不把社稷放在眼里。 京城在军阀到来前似乎已经被反贼攻克,所以皇帝和太子分道逃亡。如今皇帝似乎已经回到京城,而太子却还在外面招兵买马(宣等人追随),似乎有不臣之心。

简而言之: 中央朝政腐败,宦官专政,两宫不和。地方盗贼风起,军阀割据,外患不断。

难道这就是随机生成的中国历史最典型的场景? :-)

附录

文中使用的通鉴文本是从维普网上下载的,做了一些简单的清理,上传到了百度云(链接)。 为了避免古文分词的麻烦,在作 n-gram 的时候以字为单位,所以用 gsub 在每个字的后面加入空格。 n-gram 选择 n=2.

 library(ngram)        
 file<-"C:/Users/Zeng/Downloads/zztj.txt"     
 str=scan(file,"character",encoding="GB2312")      
 s = concat(str)      
 s = gsub("(.)", "\\1 ", s)         
 ng = ngram(s, 2)  
 o = babble(ng, 100,seed=2000)  
 Encoding(o)<-"UTF-8"  
 o  
Blog分类: 

做一个简单的计数区块

昨天有人问的,研究了一下,因为对Drupal本身的函数结构不是很熟悉,所以到Drupal函数手册那里去钻研了一下,可惜里面的东西太复杂,所以就用了一种比较土的方式解决了,如下。呵呵,同时还想试一下新安装的编码过滤器,因为昨天发现没有这个过滤器的时候,所有的php代码的前提示符都会被滤掉。

$n_of_comments= db_result(db_query('SELECT count(*) FROM comments'));
$n_of_posts=db_result(db_query('SELECT count(*) FROM node'));
$n_of_blogs=db_result(db_query("select count(*) from node where type='blog'"));
print "本站共有blog $n_of_blogs 篇
";
print "本站共有文章 $n_of_posts 篇
";
print "本站共有评论 $n_of_comments 篇"; ?>

当然,如果你嫌这个显示出来的效果比较土,还可以加style,譬如输出时

print "本站共有blog $n_of_blogs 篇
";

我的站点的右下角有一个没有加修饰的例子。

其他的数据可以列出,譬如一共有多少论坛话题,一共有多少tag,一共有多少用户,只需要稍微修改一下上面这段代码就可以了:)


update:做了一个小小的更改,max(nid)不等于你所有的node的总数,而是等于曾经有过的node的最大值,因为你即便是删除了一个node,这个id已经被它占有,后边的node id不会再发生变化,所以应该使用 count(*),呵呵,刚才秀豆了。

Blog分类: