kobe's profileKobeSoftPhotosBlogLists Tools Help

Blog


    September 15

    大学本科相识10年大聚会 [090912,090913]

    DSC_0266

    DSC_0257

    百年修得同船渡
    千载结缘同窗读
    三十沧桑染鬓白
    少时冰心在玉壶

    December 19

    自己当不了英雄,但请别侮辱英雄

    前几天偶尔看了篇奇文“闾丘露薇:扔向布什的鞋子和记者伦理学”,文中最后的话很有意思:“不过一个真正的记者,应该是明白,工作就是做一个记者应该做的事情。而社会,应该是鼓励记者做自己应该做的事情,所以,扔鞋子的记者,在我眼中,绝对不是一个英雄,喊口号的记者,也不配记者这个称号。”

    看了这篇文章,我不禁想到百年前的谭嗣同,他的角色最多不过是参政者(破格提拔官至四品),职责只是帮上司出谋划策,按理说实在犯不上把自己的小命拼上(何况上司已经倒了),但他在关键时刻没有选择“出国旅游”,而是选择了“一定要流血警醒世人”。当然,我们不能要求每个人都这么选择,包括我认为自己都不会做出同样的选择,但没有人能,也没有人配判定他死的值不值得,因为你的任何判定其实都是在判定“如果我是谭嗣同,选择了死,值不值得”,而死对于谭嗣同,只有一个答案:“值得”,因为他这么选择了!

    世界本没有真相,我无意讨论伊拉克是被美国“占领”了,还是被“扶助”,但在这样一个颠沛流离的国度里,很明显两个鞋子发出的声音要比一个称职的记者发出的声音要震撼得多!因为两只鞋子让世界人敬佩这位记者,敬畏伊拉克人民,而这是一个仅仅称职的记者奋斗一辈子都难以做到的。

    不是人人能都当出头鸟,我相信闾丘露薇和我一样都不具备这样的勇气和胆量,每人都有每人的选择,但当别人选择了我们不敢选择的东西,请别侮辱他!

    November 24

    TAOCP

    当人遇到困惑的时候,读一读TAOCP,会有境界的提升

    September 26

    奇虎部署2000台服务器组建云计算中心

    继趋势、瑞星等公司后,奇虎公司昨日首次披露了“云计算”战略,该公司已经部署了超过2000台服务器的国内最大的云计算中心,借此正式杀入“云计算”战局。

      奇虎董事长周鸿祎昨日给部署“云计算”的公司设置了一个门槛,称“必须有1000台以上的服务器”才能保证实现“云计算”战略。奇虎方面表示,“云计算”的实现要求安全厂商必须拥有海量用户,足够的服务器群以及海量数据处理能力,三者缺一不可。而庞大的数据中心就要求至少拥有1000台以上的服务器。

      奇虎内部人士透露,奇虎已经在全国20多个城市的机房中购置了超过2000台服务器,带宽达到50G,借此组建自己的“云计算中心”。

     

    结论:记者SB or 奇虎SB or BOTH

    September 02

    范围数据原地排序,最坏复杂度2N

    int SelfSort(vector<int>& vec)
    {
    int loop_number=0;
    for(int i=vec.size()-1;i>=1;)
        {
         loop_number++;
         if(vec[i]<i)
            swap(vec[i],vec[vec[i]]);
         else
            i--;
        }
    return loop_number;
    }

    int main(int argc,char* agrv[])
    {
    srand(time(NULL));
    vector<int> vec;
    for(int i=0;i<1000;i++)
         vec.push_back(i);
    random_shuffle(vec.begin(),vec.end(),p_myrandom);
    cout<<SelfSort(vec)<<endl;
    return 0;
    }

    July 08

    Javascript开发也可以使用"ant"了

    what is Javascript Development Environment

    JavascriptDevelopmentEnvironment,简称JSDE,是一套面向特殊需求的JS开发部署环境,对于JS的开发人员,它支持扩展JS语法、分析JS工程、分析CSS工程、整合JS代码、分析JS工程、除错等功能,是一套基于linux平台的,面向RIA开发人员的开发环境。


    需求来源

    JSDE的需求最早来源自RIA组的JS开发实际环境,JS开发需要两套系统:开发模式和线上模式。开发模式为人为编写的代码,线上模式为经过除注释、整合、混淆后的代码。可以近似的理解为开发模式对应源代码,而线上模式对应包含、link后的目标代码。
    以linux下GCC编译环境为例,需要makefile的依赖分析, gcc的预编译、编译、链接等环境,最终形成从源代码.c文件到线上部署的可执行程序的转变。不像成熟的编译语言,JS语言没有统一、商业化的线上部署的环境支持,正基于此,为了提高整个RIA的开发效率、线上部署效率,一套灵活、具体的可靠的JSDE就显得尤为重要。

     

    功能

    JSDE具有以下12点功能:
    1,致力于为RIA打造基于linux平台的JS开发部署环境:
    基于通用框架,面向特定需求
    2,具有支持类如自定义函数等扩展JS语法功能:
    开发人员可以自形扩展javascript的语法,以达到方便工程的目的,扩展的语法由JSDE负责进行解释和转义
    3,具有检查JS语法错误功能
    可以检查代码的语法正确性
    4,具有去除注释功能
    可以去除代码的各种注释说明,以达到代码瘦身的目的
    5,具有去除冗余代码功能
    可以去除代码中的冗余部分,比如多余的分号、空格等,以达到代码瘦身的目的
    6,具有整合代码功能
    可以通过预编译,实现解释语言的include
    7,具有依赖关系分析功能
    类似Makefile,分析工程中的文件、函数等依赖关系,给出结构图,方便调试
    8,具有编码检查功能
    可以针对实际线上环境,检查工程文件编码格式,并提出警报
    9,具有include once和检查功能
    实现#pragam once等宏定义,保证依赖文件关系保持拓扑性(无环)
    10,具有整合CSS功能
    根据CSS语法和依赖关系,整合CSS代码
    11,具有发布工程的功能
    按工程配置,发布工程,完成开发模式到线上模式的转换
    12,具有代码混淆功能
    混淆代码,实现霍夫曼编码,达到代码瘦身的目的

    June 12

    最近比赛真多

    最近的竞技体育真多,PGL、NBA,Euro2008,白天黑夜连滚播出。竞技体育的一大特点就是只重结果,结局只有赢或者输,强调享受过程只能是loser的言论。在工作中,也要具备这样的精神,期待胜利时刻的那一声怒吼。

    April 30

    SINA BLOG 5.0,let's start from here

     
    January 28

    娱乐精神

        话说部门第N腐败归来后,已近午夜,登录进服务器,半天在VIM敲不进一个字符,脑子里想起白天看的一个小灶消息,“林丹为什么拿球拍怒砸韩国羽毛球队教练李矛?”:
        话说,林丹走过韩国队教练席的时候,李矛悄悄的在林丹耳边问:“听说你买中石油了?”,超级丹遂大怒,挥拍砸向......
       
        PS:推荐一下偶像LX的博客,下次他来游戏频道做客,我一定一定要和他合影!!!
        http://blog.sina.com.cn/luoxian1984

    December 30

    绝望女主播

    这几步,走的很艰辛
    那几句,说的挺悲伤
    怎料未施幕前胭粉
    隐现幕后岁月沧桑
    辗转星空长夜
    女人决绝泪光
    昔高大身影
    今意外的镇定慌张

    此一幕,看的好热闹
    头一条,炒的蛮开心
    果然张张天上君子
    又食人间烟火气息
    大国气象价值
    半生奋斗泡汤
    你悲欢离合
    我年终的八卦乐章

    November 25

    寻找最快的优先队列

        前段时间给N年前的一些代码code review,其中涉及到了一个人机博弈的程序。人机博弈中有一个很重要的结构,就是优先队列,其作用的是能够随机的insert一些数据,并保证随时能获得最小(大)的点,其实该结构在大部分的图算法中(包括寻路问题)也很常用。我原来的代码使用heap实现的,不过最近看了一篇paper,是描述《帝国时代》游戏中寻路算法的数据结构的,很是新颖,其用线性表+缓存的结构就可以达到相当快的速度,于是想拿它来试试,看看它和堆哪个快。结果很失望,还是堆快,而且快的不是一星半点,当然也许在特定领域会有不同的表现。
        为了完善该paper的思想,我决定对这个结构进行改进,看看能不能接近或者超过堆。先看看堆的优先队列是如何实现的,我这里使用STL里面的结构:
    class PriorityQueueHeap
    {
    private:
      vector<int> vec;
    public:
      int ExtractMin();
      void Insert(const int value);
      void Display(){}
      bool IsEmpty(){return vec.empty();}
    };
    void PriorityQueueHeap::Insert(const int value)
    {
    vec.push_back(value);
    push_heap(vec.begin(),vec.end(),greater<int>());
    }
    int PriorityQueueHeap::ExtractMin()
    {
    pop_heap(vec.begin(),vec.end(),greater<int>());
    int min=vec.back();
    vec.pop_back();
    return min;
    }
        接下来,我开始对线性表的结构进行改进,首先将固定缓存进行扩展,变成了动态缓存,在做了这个改进后,发现性能有显著提高,大约相当于STL Heap版的60%,这当然是不令人满意的;而后,利用桶排序的思想分割线性表并结合动态缓存修改算法,经过一系列精心的优化(比如减少函数调用次数,利用内联等等)后,惊人的效果出现了,测试表示改进后的时间比STL要快了将近30%,测试程序如下:
    srand((unsigned)time(NULL));
    for(int k=0;k<1;k++){
     KobePriorityQueue pq1; (PriorityQueueHeap pq1;)
         for(int i=0;i<=100000000;i++){
         int value=rand()%16777216;
         pq1.Insert(value);
         if(!pq1.IsEmpty()&&rand()%8==1)
            pq1.ExtractMin();
         }//end for i
    }//end for k
        KobePriorityQueue是经过我改进后的结构,PriorityQueueHeap是STL最小堆实现,测试程序会不停的插入节点,并随机取最小点,随机数1/8的来源是因为大多数寻路问题(棋盘问题)新增节点和删除节点的比是8。测试结果如下(linux 4核,4G内存):

    Scale Loop STL HeapPQ(average sec) KobePQ(average sec) KobePQ(best sec) percentage comparison
    1000 10000 1.222 2.79 1.221 -0.08%
    10000 1000 1.298 1.406 1.261 2.93%
    100000 100 1.433 1.192 1.159 23.64%
    1000000 10 1.535 1.214 1.214 26.44%
    10000000 1 1.652 1.293 1.265 30.6%
        可见,在缺省参数下,十万级以上KobePriorityQueue比STL版本要快了25%,在10万级以下应用,通过调整参数也可以获得和STL相当的效率。当然这里的弱点就是在某些应用时,需要调节算法参数,使得其不像STL那样稳定通用(毕竟堆的操作效率基本都是logN,使得其时间消耗很稳定快速)。另,根据CLRS,有一种叫FIB heap的结构在插入时可以比最小堆更快,平摊获得O(1)的效率,根据其wiki链接,也下了一份其实现做比较,但结果很令人遗憾,其方法的速度比heap和KobePQ要慢了好几个数量级,当然不排重是代码编写的问题。
        总之,和C++和Java都采用的以堆实现的优先队列算法比较,KobePriorityQueue基本表现得略胜一筹,在某些特定要求速度的领域,可以代替STL Heap。当然在要求稳定性的领域,还是用STL更安全些。

    November 01

    扩展boost内存管理库

    根据我上篇提到的boost::object_pool的一些问题,我试着把object_pool做了一些改进,改进完的性能测试如下(测试环境linux服务器,4G内存,4核CPU(3052MHz))
    #include "kobe_object_pool.hpp"
    using namespace std;
    using namespace boost;
    //--------------------------------------------------------------
    class SS
    {
    public:
    int value;
    float value2;
    SS(){}
    };
    //--------------------------------------------------------------
    int main(int agrc,char** argv)
    {
    boost::kobe_object_pool<SS,boost::default_user_allocator_new_delete> my_pool;
    int test_range=10000000;
    for(int i=0;i<test_range;i++)
        {
         SS* p=my_pool.construct();
         my_pool.destroy(p);
        }
    real    0m0.049s
    user    0m0.040s
    sys     0m0.000s
    //--------------------------------------------------------------
    boost::object_pool<SS> my_pool;
    for(int i=0;i<test_range;i++)
        {
         SS* p=my_pool.construct();
         my_pool.destroy(p);
        }
    real    0m0.125s
    user    0m0.120s
    sys     0m0.000s

    //--------------------------------------------------------------
    for(int i=0;i<test_range;i++)
        {
         SS* p=new SS();
         delete p;
        }
    real    0m0.695s
    user    0m0.690s
    sys     0m0.000s

    //--------------------------------------------------------------
    return 0;
    }

    可以看见,使用我的kobe_object_pool要比boost_object_pool快了1倍多,比常规的内存分配快了一个数量级。这里有一个说明:
    kobe_object_pool要求使用者调用完construct后一定要调用destroy,以保证对象的析构可以被正常唤起,否则将会有隐患的资源泄漏;而boost::object_pool则不然,它可以在pool析构时释放用户所遗留析构的object。这也是boost::object_pool的一个优点吧,但如果用户可以保证唤起析构,那么使用kobe_object_pool还是有性能优势的。

    October 29

    失败的boost::object_pool

        boost::object_pool绝对是一个超失败的设计!该内存池模块,基于sss(simple segregated storage),以32为长为block大小,成倍增长,本来挺好的设计思想,利用了chunks空间存free list既节省了overhead空间又节省free list空间,还包装了对象构造和析构,但就是毁在了它的释放操作。真是不看代码不知道,一看吓一跳,它的释放效率居然是O(N),原因在于它的free list不同于boost::pool的free list,它的free list是有序的,我理解设计者的目的,因为有序的free list可以保证在最终垃圾回收时的效率保持在O(N)(否则是O(N*N)),但设计者难道不知道释放chunk的使用频率远远大于最终回收时的一次吗?鬼才会使它呢,真是失败中的失败。。。遗憾啊
        补救的方法一个是使pool代替(但这样就会丧失面向对象操作的遍历,比如需要自己调用构造析构等),或者自己写一个object_pool的版本。
    PS:顺便搜了一下,拍的人还不少,http://lists.boost.org/boost-users/2007/03/25888.php

    September 24

    三个有意思的STL算法

      最近琐事太多,影响了“修行”,但愿新的开始能带来好运。前一阵糙读了一下STL ALOGRITHM里的算法,觉得有三个给我印象很深,拿到上面来分享一下。
      1,rotate。顾名思义,该算法是绕一点做旋转,当然了,这么说有点忽悠。其实很简单,比如abcdefg以f点rotate就是fgabcde,再以b点做rotate就是bcdefga。实现该算法不难,难的是把情况考虑仔细。首先,选用swap来做,这点一般人都能想到,原因很简单,swap在空间时间上都很高效。如果abcdefg以c做rotate,实际就是把ab看成一个泡,一直往后冒,一直冒到最后为止,这时ab被交换到了最后的位置,其他的也就自然被顺序“拱”到了前面,于是算法完成。但这里有个问题,如果轴在中点往后,那么这样的做法不对,如果以f为轴rotate,a在与g交换后算法就终止了,变成了gbcdefa,很明显算法不完全。所以这个算法最简单明了的实现应该按两种情况来,判断轴在中点的左还是右,然后分别处理。时间复杂度为O(n)。
      STL的做法不完全相同,但基本也是按照这个思路,只不过在第二种情况时,它的轴是变化的,一直向后移动,直到全部交换完成。比较起来我觉得我提到的做法最简单明了,当然了STL也不傻子,STL采用它的做法原因是有的迭代器是不能反向移动的。对STL的做法想了解的更深,可以读它的rotate实现。
      rotate的实现,并不复杂,其实提到它完全是为了给下面的两个算法打铺垫。
     
      2,stable_patrition。patrition很常用,做法也很常见,采用的是快排的前半身,以一个轴从头和从尾分别交换,碰到了即为完成。但是这里提到的是稳定的patrition,做法也不难想,如果对长度为n的数组做stable_patrition,那么开辟出一个和n同样大小的缓冲。从头扫描该数组,如果满足条件则把它放在前面(依次增长),否则则把它放在缓冲里(依次增长),很显然这样的结果是满足条件的序列和不满足条件的序列都为稳定,最后把两个序列合并即可,时间复杂度为O(n)。
      不过,问题没这么简单,如果要求空间复杂度为0呢?这里STL的实现用到了分治的思想,很显然2个数的stable_patrition不需要任何空间消耗,依次类推如果4个数呢?假设有一个数组A,被稳定分为了段A1段A2,同样B被稳定分为了段B1和段B2,如果A在B的前面,很显然:
      A1、B1满足条件,且A1在B1的前面;
      A2、B2不满足条件,且A2在B2的前面;
      如果对A2和B1做rotate操作,则可以保证A1和B1满足条件,且仍然为稳定分类,在前;A2和B2不满足条件,且仍然为稳定分类,在后。如此完成了stable_patrition。
      可以推出递推式为T(n)=2*T(n/2)+n,利用通用公式,求得T(n)=n*log(n),时间复杂度为N*Log(N)。当然STL并不是从最底层的2做起的,换句话说并不是自底向上,而是自顶向下逐步试探已分配的空间是不是大于等于目前分段的长度,如果大于等于则停止分段,采用缓冲区做法,如果不满足则继续分段,最后在归并。时间的复杂度在N*Log(N)和O(N)之间,当然空间消耗也就不能是1了。
     
      3,merge_without_buffer。对于两个有序数组的归并,早有定论,开辟空间,利用两个游标进行归并。这里要提到的是不使用任何缓冲区的两个有序数组的归并。有了上面两个思想的启发,应该不难想到:对于数组A和数组B,A、B皆有序,选B的中间数mb,查找mb在A中的位置(或者插入位置),由此得到了A和B的两个分段:
      A1、B1小于mb;
      A2、B2大于等于mb;
      于是对A2和B1做rotate操作,则再对A1B1和A2B2数组分别归并,这样问题T(n)化简为了两个T(n/2)的问题,最终可以到2个数的归并。问题的时间复杂度也为N*Log(N)。考虑rotate的两种情况,可以在选mb的时候,把长度大的一个数组作为数组B,这样在rotate时,前段小于后段的概率要大一些,这样能提高些许性能。 
    July 19

    最大特征值为1的补充证明

    前一天,有个搜狗实验室的给我发信,问我的论文《Pagerank二维线性收敛方法》里默认的pagerank矩阵最大特征值为1的推论怎么来的,我在百忙中发信回复了他,信件内容:
     
    只要证明其不存在大于1的特征值即可
    因为(M为n维行列式,V为n维PR向量)
    M*V=V => 必存在特征值为1的特征向量
    现在(&为特征值)
    M*V=&*V
    因为M为归一化后的行列式
    所以
    M*V=[ M1
          M2      *  V , M1的各项期望为1 , 设VM为V各项值的最大项
          M3     
          ...
          Mn]

    假设&大于1, 则&*V中必有一项大于VM
    但是M1-Mn的期望为1,也就是说他们和V的乘积都不大于Vm
    [如(1/3,1/3,1/3) * (4,5,6)T <= 6]
    这与假设矛盾 =》 不存在大于1的特征值
    October 07

    some mottos

      前天在内部活动中见到了Java之父James Gosling,这是我第二次近距离见到世界级顶级IT大牛,第一次是在年初的TCS tech week上见到了“快速排序”的发明者图灵奖得主Hoare博士(下图的第二排最右满头白发的老头)。大牛就是大牛,James对于并行运算的预测让我佩服不已,唯一聊以自慰的也就是我比他们还年轻:)
      下面总结一下,我印象里我最欣赏的IT业界的名言,共勉:
      
      1,没有看不懂的书,只有读不进去的书——出自某网友读TAOCP时的激励自己的话
      
      2,微软离破产只有18个月——据说是Bill口头禅,居安思危
     
      3,640k ought to be enough for anybody. ——Bill的名言,写程序的时候应该多想想
     
      4,软件的价值不在于它使了什么高深的技术,而在于有多少用户肯花钱去买——微软高管在评价linux时(非原话
      5,先把符号当钱,再把钱当符号——想起了“背一单词是1$”
     
      6, Premature optimization is the root of all evil——Knuth
     
      7,just for fun——Linus Torvalds,最后一句,有兴趣的做事才可能成功
    September 09

    <转载>毛泽东政治遗产还剩多少

         今年9月9日是中国共产党第一代领导人毛泽东逝世30周年纪念日。与30年前举国同悲、万民哭泣的哀悼相比,大多数中国人对毛泽东逝世纪念日不再有特别的感受,年轻一代甚至根本不清楚毛泽东的生卒日期。

        近日在北京某大学随机采访了五名学生,他们都不知道毛泽东逝世的日子。在被要求“用一句话评价毛泽东”时,五名学生中有二人说毛泽东建立了新中国,三人说他有功也有过。几名学生的回答基本符合文革后官方对毛泽东“功大于过”的评价。不过,中国社会不同阶层对毛泽东的评价也大不相同:很多知识分子认为毛泽东发动的“反右”和文革给中国带来深重灾难,历史还将重新审视毛泽东;在不少工人、农民以及一些痛恨腐败、敌视西方的人士眼里,毛泽东仍是正义、清廉的化身和对抗西方的英雄。
     
         作为创始人和领袖,毛泽东率领中共经过28年的残酷战争,终于在1949年建立了一个由中共主导的新中国。这一改天换地的创举,使后来的继任者无论与毛泽东有多大的歧见和恩怨,都不能或不敢抛弃毛泽东的旗帜。

         但毛泽东晚年的极端路线又使中国陷入严重危机。在毛泽东去世两年多后,中国进入邓小平领导的改革开放时代。从1978年开始,中国为推进改革开放,和知识分子联起手来从各方面否定毛泽东的文革路线,开启了一场影响深远的思想解放运动。

        对很多中国人来说,改革开放的过程,在一定程度上就是抛弃毛泽东的政治遗产和生活观念的过程。政治上,毛泽东被一步步抬下神坛,从一个“句句是真理”的圣人变成了“晚年犯了严重错误”的领导人;经济上,毛泽东深恶痛绝的私有经济开始蓬勃发展,中国人的整体生活水平得到空前的改善;文化上,毛泽东坚决反对的所谓“四旧”和“资产阶级生活方式”只是如今多元文化的一部分。

    维护毛泽东

        然而,否定毛泽东文革路线的根本目的还是要维护毛泽东建立的政体。30年来,每当反思毛泽东向反思专制制度方向发展时,就会被制止,同时以歌颂毛泽东打天下时的“功”掩盖他建国后的“过”。

        经过多年的重复,毛泽东的“功”早已被媒体宣传到极致,例如他如何领导长征、如何打败蒋介石,如何保持艰苦朴素的生活习惯等等,但对毛泽东1957年以后发动“反右”、“大跃进”和“文化大革命”摧残知识分子和传统文化,导致数以千万计中国人“非正常死亡”的“过”,却一直讳莫如深。这种宣传并不仅是为了捍卫毛泽东的个人声誉。

        令人尴尬的是:毛泽东的“功”大多都是好几十年前的事,从中可以利用的价值已十分有限,而毛泽东强化统治、发动文革的“过”却殷鉴不远,既不能深究,也不能让毛“七、八年再来一次文革”的预言应验。因此,对毛泽东的纪念往往只能“舍近求远”或干脆保持低调。

        有关毛泽东功过的话题恐怕还要持续很多年。一方面,毛泽东的斗争勇气和始终代表社会下层的平民精神,使他仍受到许多百姓的怀念;另一方面,毛泽东留下的阶级斗争理论和乌托邦式的共产主义理想等遗产已被彻底抛弃,但他在中国政治中刻下的“毛记”烙印仍清晰可见,成为中国现代化进程中难以逾越的障碍。

    于泽远

    August 08

    算法心经:数学的应用:概率的应用(上)

      终于写到重头戏了,如果说前面的微分积分还属于基础理论,而与我们日常的算法设计距离有点远的话,那么后面的概率、矩阵、空间立体几何,可就是和应用息息相关了。

      为什么要有概率
      概率是个数学概念,但它在计算机软件设计上有很多应用,数学概念是留给数学系的人讨论的事情,我们这里侧重的它的应用。那么为什么会在应用中用到概率,概率真正体现的是什么呢?我个人理解,概率的本质是体现了一种模糊的思想,既可以是A、又可以是B,甚至本身概率的值就是模糊的,因为没有人知道真正的概率是什么,我会在后面解释这句让人晕的话。
      理解了概率要表达模糊的思想后,我们会发现世界上绝对的事太少,甚至就是0、1写入都存在微小的写错概率,说不好的事情太多,也就决定了概率绝对是大有用处。

      概率的内容
      目前市面本科的概率书的标题一般为“概率和数理统计”,表明了概率实际分为两部分内容:概率和统计。统计是在概率分布的基础上,而单纯的概率统计不是这里要讲的重点,所以下面主要把概率理论部分分成了两块,“概率的计算”和“概率的分布”。
     
      概率的计算
      概率的求解绝对是数学的东西,这里不讨论,什么全概率、独立时间、贝叶斯公式,书上都有。这里举几个实际的例子,首先概率在算法设计分析时会碰到,就像我们在积分那节举的例子,计算出的公式几乎都基于vector里面的对象分布完全随机的,也就是每个对象在各个位置的概率都一样导出的,如果概率不一样呢?结果当然会不一样,有关计算不详加讨论。谈到了分布的概率,TAOCP《计算机程序设计艺术》在动态分配内存那章的课后题专门有一题讨论了,最佳匹配分配内存的一个最大弱点,最后的结果就是分配完空间的出现概率不平均。再有,BM字符串匹配比KMP算法的一个重要改进,就是假设字符串比较时,从后位首先出现不一样的概率比从头起出现字符不一样的概率大。甚至在搜索引擎里,pagerank的那个0.85都是模拟了用户从浏览器直接跳转的概率(尽管客观上起到了,避免矩阵奇异的效果)。
      概率计算很多都是基于经典的模型(后面会说明模型的思想在概率起到重要作用),高中数学排列组合上让我记忆犹新的一个模型就是问什么人排成一队,怎么分法时,会想象每人之间一个空格,然后对空格取组合运算。值得一提的计算模型是生日模型,由此引申的计算可以应用到判断hash的冲突的概率,比如做人机博弈时,每次alpha beta剪枝搜索(也可以不同次)需要对棋盘做hash,然后在每次再对棋局评估时,利用hash值看该棋局是否已经被评估过。应用概率,我们可以设计合理的hash code长度,然后利用生日模型,估算出该hash code长度下,棋盘的冲突概率,然后把概率限制在可接受的范围内。 因为这个计算模型太重要了,所以写一下,将M个球,放入到N个(N>M)桶时,每个桶的冲突概率是:1-N*(N-1)*(N-2)....(N-M+1)/power(N,M),可以看出,当N个足够大时,冲突概率几乎为0。
      最后说一个曾经让我犯晕的小题,看你完全理解概率计算没?
     
      题一:打雷后下雨的概率是80%,而打闪电后下雨的概率是70%,那么即打雷又打闪电后下雨的概率是多少?不会计算没关系,直觉告诉我们肯定比80%高,恩,没错,把这题改一改变成另外一题
     
      题二:打雷后下雨的概率是80%,但是没打闪电后下雨的概率是20%,那么打雷了但没打闪电后下雨的概率是多少?不会计算没关系,直觉告诉我们肯定位于80%和20%之间。

      哦???晕了,怎么两个题产生的效果不一样,一个是比最高的高,一个是两个概率之间,我们的直觉错了吗,还是忽略什么重要的东西,请独立思考1分钟再看答案。
     
      相信直觉没错的,看第一题怎么解:
      解=1-又打雷又闪电但没下雨的概率= 1 - 0.2 *0.3 = 94%
      第二题:
      解=0.8 *0.2 /( 0.8 *0.2 +(1-0.8)*(1-0.2) )= 0.16/0.32= 50%
      与直觉一样,相当于大家互相拖了一下后腿,而大家的力量一样,所以扯平了,哈哈,我故意没写第二题解的说明,你是否明白了呢?
     
      其实真正忽悠人的地方在于对独立事件的理解,题一中的打雷下雨是独立事件,类似于我踢一脚门塌的概率是80%,你踢一脚的概率是70%,我们同踢时门塌的概率是94%,但是如果我不踢,对你踢有影响吗?没影响,所以结果相当于一个合力。
      题二里的事件,并不是独立事件,相当于考虑利益去做某事的概率是80%,考虑危险去做该事的概率是20%,两个是互相影响的,隐含了这个条件,你考虑了利益决定不去做这事而又考虑了危险,这时去做该事的概率是0!!!同理,你考虑了危险决定不去做这事,但是你又考虑了利益,但结果仍然去做该事的概率是0!!!实际上是一个类似单票否决的机制,最后的概率只由两部分构成,考虑了利益考虑了危险做的概率和考虑了利益又考虑了风险但不做的概率,那么做的概率就是做的概率比这两个加起来的概率的值。
      题二的关键我们的潜意识在作崇,类似于认为厂长就是男的那种,所以这题也有脑筋急转弯的类型。
      BTW:这道题的思考来源网上的一篇著名的文章“贝叶斯过滤垃圾邮件”,该文章中有一个类似的公式,但啥都没说明直接就说叫联合概率公式,后来我查书发现没这个公式,所以才反复思考这个公式是怎么出来的。

      概率的分布
      前面提过,概率的分布是另一个大部分,而且是极其庞大的部分,总体上可以分为连续变量分布和离散变量分布,对计算机应用来说,离散研究得更多一些,不过由于理论深度太高,我的水平不允许我在这里推导公式,所以建议大家去看书,这里只提一提正态分布,随机变量合符合正态分布,这是一个极为有用的结论。用浏览器浏览页面,可以有前进和后退,那么在N次浏览后,应该是按照初始的URL呈正态分布,那么多维的正态分布怎样呢,比如前进到页面里的URL每个的概率是1/N,那么浏览和后退N次后,呈现的分布是什么呢?是不是预示某些阅读习惯呢?我也不知道,留做思考吧:)

      (非常)+重要的定理:大数定理
      这里采用正则语法,表明再怎么重要也不过分。前面提到过一句话,就是概率本身的值表达的意思是个模糊的含义,甚至该值都是模糊的。现在作出解释,事实上一个事件的概率,我们根本不知道,Miller投中一个三分的概率是什么?没人知道,也没法测量,所以说所谓的概率都是近似值,因为真值不存在,这好象是讲哲学了。不管你接受不接受这样的观点,大数定理是同样为我们解决这个问题来准备的。
      大数定理说明:当事件发生足够多次时,它的发生频率就是它单次发生的概率。
      不用去管它怎么证明,我们还是要惊呼,这个看似常识的定理为宇宙把概率模型和统计模型两大世界联系了起来!!!让我们知道了对概率的分析可以用统计的方法去解决,这个定理,就是搭起统计和概率的桥梁。Miller的三分命中率终于可以算了,让他投1000个,看他进了几个:)


      还有一个公式,也可以起到桥梁作用,那就是贝叶斯公式,P(A/B)=P(B/A)*p(A)/P(B),这个公式太深奥,以至都创立了两大学术派,我只有用的份,不敢多作半点解释,那么这唯一半点解释就是建立正推反推的模型,我们会在后面的三维概率模型中用到它。

    概率的三维空间模型:
      这是我写的概率应用的重点,重在应用举例,首先说明一点,为什么要用三维空间模型呢,是因为我考虑任何问题都爱往矩阵上靠,一想到矩阵自然而然就联想到了空间模型,如果你不接受这个观点,没关系,看看有没有空间的味道。

    概率一维空间模型的应用:
      从概率点说起。空间模型里最低级的就是一个点, 那么在概率世界里,点就是一个概率值,不管概率怎么得出的,反正有个23%的概率,那么它就是一个点了,它可以代表任何东西,数或者一个事件的概率,甚至一系列的事件的概率,总之,只要你想把它看成一个封装的点,它就是点。
      有了点,自然需要线,也就是一维概率模型的内容:线。一个点到另一个点有一个概率,那么把这样的一些点连起来,就形成了一条线。A推B,B推C,C推D....,那A能不能推C呢?当然可以了,但是推了,就不是一维模型了,我们会在后面的二维模型里讲。这样依此串成串后,会发现和一个大名鼎鼎的名字能联系一起,那就是马尔可夫链,没错,这就是马尔可夫链了。
      马尔可夫链有什么用?哇塞,用处太多了,按照书上的理论,可以做个简单的聊天机器人,完全基于统计模型。利用大数定理,建立统计频率等于概率的思想,我们统计上万篇文章,按语素分词(如何按语素分词?不是我要讲的重点),例如“今吃了吗?”分成“今”和“吃了吗”两部分,然后我们发现在这上万篇文章里,”今吃了吗“后面跟的最多就是“吃了”和“没吃”,那么我们取最多的发生频率的回答随机做出回答,比如回答“没吃”。接下来,语素变成了“吃了吗”和“没吃”,然后再在上万篇里发现“吃了吗-没吃”后面跟的“没吃回家吃去吧”最多,那么我们就把这句话选成回答。呼呼,好简单,是地,这就是模型的威力,有了模型,万事不难。

      上面的例子是经典书籍的例子,我再举一个我真实解决的例子。
      研二的时候,我的上铺去给一家手机游戏公司兼职,我对做手机游戏也很有兴趣,不免时常和他探讨一下。他做的街头对打游戏,双缓冲呀切图啊这些自然不在话下,不过在测试阶段遇到一个问题,游戏里面的NPC太傻。NPC和PC有很多动作,比如跑,跳,横拳,下蹲,防,下防等等,他设计是基础是随机出招,然后会根据NPC与PC的距离调整一个好斗值,当然这个好斗值也会跟血有关,当NPC的血快没时,它会过多采用进攻动作。而在时间快结束,如果NPC发现自己的血多于PC时会降低好斗值,反之增加,而好斗值直接关系它发招的进攻招的比例。但这么设计有个很重要的弱点,当PC用些固定招术取胜时,NPC就会变得像傻子一样,比如PC跳起落下再扫堂腿,NPC就挂,结果PC反复使这招,结果NPC反复挂。如此的现象,让我不得不想起曾经的FC游戏,魂斗罗啊,赤色要素啊,我每次遇到大BOSS的时候,BOSS发子弹时,我都会躲到一个固定角落猫起来,每次都打不到我,此时多自豪自己为人类啊。
      为了影响玩家感受的自豪感,游戏NPC必须克服这个缺点,那怎么办呢?概率一维模型coming!!!
      我们为PC动作建立4元组马尔可夫链,比如对动作A,B,C,D,E建立两个四元组(A,B,C,D)和(B,C,D,E),以此类推。在游戏中维护该一堆四元组,随着游戏的进行,就会出现一些重复项了,比如B到C到D到E的出现次数很多,也就是从B到C到D到E的概率很高,OK,我们记下,那么也会随着游戏的进程,各种各样的四元组越来越多,那么就把频率(概率)的小的删除,始终把四元组们维持在一个在手机内存里可接受的范围内,(怎么存储四元组来使存取最快,与我谈的主线无关)。哈哈,我们达到目的了,我们可以记录下游戏中NPC的一些习惯了,那么下一步呢?那就是预测呗,如前假设,B到C到D到E的概率很高,当PC再次在游戏中打出B+C+D的连招时,NPC提前可以预测出PC的下一招极有可能是E,于是就采用对E的防御招术。NPC终于智慧了,好兴奋啊,试试吧,把代码一往原程序一加,效果确实比原来好多了。
      一维概率模型是这么厉害啊!!
    July 29

    奔腾不再

      时间很有限,可是还是忍不住写一点,说起奔腾,对于我们这一代的人来说简直就是像初恋情人一样另人回味,如今,它就要走了,怎么能不撰文加以纪念呢?
     
      奔腾前传:
      最早玩电脑还是那土土的苹果机,那时还经常传出某某学生因为多动了一下键盘,被老师从机房拖出去的事(可能那时候的机器对于老师学校来说太重要了)。自己拥有的第一台电脑是94年的80486 DX66,记得那时候最开心的事就是带一堆软盘到同学家去考游戏。这台电脑带给我的最大收获就是熟练的掌握了使用PC TOOLS,GAMEMASTER改游戏,无论是改游戏存档还是改内存数据(当时并不知道数据是放内存里的)都练的巨熟。
     
      奔腾一:
      95年到,WINDOWS95来了,记得那时有几个几年前就能用GWBASIC写猫捉老鼠游戏的能人,天天讨论着WIN95安装啥的,我却还傻傻享受着我的DOS游戏。中考结束,我利用假期把仙剑穿了三遍,抽出时间陪同学一起疯狂的赶装机疯,奔腾133是那时候的主流,内存的主流是16M,不少同学买奔133的唯一目的就是能装WIN95、能玩超时空游俠、古墓丽影。96年初,vincent家装了个P166,32M内存,我那个羡慕啊,至今清晰得记得从村里带回的那块芯片的价格:3600 RMB
     
      奔腾二:
      你是否还记得那个奔腾MMX的广告,穿着五颜六色衣服的太空人代表着多媒体时代。不过那时的我转向了TV GAME,整天琢磨的是SFC、MD,和这个奔二的时代几乎毫无关系。不过在一个暑假里,为了能玩新出的FIFA98,我去村里租了一台P2 233,终于搭上了P2的末班车。
     
      奔腾三:
      上大学了,终于攒足了足够的理由说服家里更新电脑,舍去那台心爱的DX66,迎来了新宠P3 550。这台电脑太重要了,我终于不用再去网吧实现红警、星际梦了。尽管后来这台电脑被我使的连风扇都不转了,我还是强忍着主板的温度报警,硬是把它虐到了大四毕业。其实它更多的贡献是,我在它身上学到了编程,并利用编程赚到了一生第一笔自己的钱。真要谢谢这个P3兄弟了。
      奔三维持了多年,不知从什么时候开始,几年前想都不敢想的1G主频,现在已经成为了现实,128M的内存居然成为了过时配置,连买显卡都要看显存了。这就是奔四时代的到来。大学里的买电脑潮,几乎都是围绕奔三展开的,会攒台电脑简直是那时候很自豪的一件事,尤其在泡妞时贼管用(经常有女生打电话过来,能帮我攒台电脑吗),和海龙的小商小贩斗智斗勇也成为了家常便饭。
      奔三带来了网络,人们知道了上网,知道了伊妹,知道了QQ,同时害怕起了"黑客"无聊的累;奔三带来了游戏,人们知道了红警,知道了星际,知道了暴雪,同时包起了网吧老板的夜;奔三带来了网游,人们知道了MUD,知道了传奇,知道了奇迹,同时养肥了个叫陈天桥的人。
      各色各样的人在奔三的时代里有失有得。
     
      奔腾四:
      随着大四即将技术,同学中逐渐有了奔四1.5、1.7,我的奔四时代是随着那台P4 2.2的笔记本来的,并伴随了我三年多。
     
      奔腾不再:
      终于等到了奔腾不再奔腾的这一天,随着热闹的双核时代到来,它被宣布要离我们远去了,“酷睿2”马上登场了。整整十年,1996到2006,奔腾这个词伴随了80一代的成长,使我们能够自恋地保持着时代青年的称谓。在这棵叫奔腾的芯片上,不仅仅跑着Gates的宝贝和亿万的程序,它安慰了多少人的孤独,享受了多少人的无聊,赐给了多少人的欢愉,沟通了多少人的世界,承载了多少人的梦想,带来了多少人的财富。
      可以肯定的是,我的下一台电脑将不会再有奔腾的标志,也许就连那个Intel Inside都不会出现,没准换成了Intel Outside & AMD instead,但是Intel奔腾带给我们的记忆是无论如何也抹不掉的。

      谢谢你,奔腾!