非传递性骰子:A比B好,B比C好,A不一定比C好

    在数学中,比较运算是有传递性的。如果A>B,且B>C,那么一定有A>C。但现实生活中却不一定是这样。三个人两两之间进行比赛,有可能A比B要强,B比C要强,但C反过来赢了A。事实上,这种现象即使在数学中也是存在的。在一些概率事件中,类似的“大小关系”很可能并不满足传递性。
    右边有四颗骰子,分别用A、B、C、D来表示。我让你先选择一颗你自己认为最好的骰子,然后我再从剩下的三个骰子中选一个。抛掷各自所选的骰子后,谁掷出的数字大,谁就赢了。那么,你应该选哪颗骰子好呢?
    其实,不管你选哪一个骰子,我获胜的概率总是要大一些,因为剩下的三个骰子中总有一个骰子比你的要好。事实上,在这四颗骰子中,A赢B的概率是2/3,B赢C的概率是2/3,C赢D的概率是2/3,D赢A的概率还是2/3,因此不管你选的是哪一个骰子,只要我选择它左边的那一个(如果你选的是最左边的,则我选择最右边的),我总保证有2/3的概率获胜。你认为这样的事情有可能吗?对你来说这样的事情合乎情理吗?

    如果你不信的话,我们可以一起来算一算:
    A和B比时,只要A扔出4的话A就赢了,这有2/3的概率;
    B和C比时,只要C扔出2的话B就赢了,这有2/3的概率;
    C和D比时,若C扔出6则C一定能赢,若C扔出2则胜负几率对等,因此C获胜的概率是(1/3) + (2/3)*(1/2) = 2/3;
    D和A比时,若A扔出0则D一定能赢,若A扔出4则胜负几率对等,因此D获胜的概率是(1/3) + (2/3)*(1/2) = 2/3。

趣题:一个与Hamilton回路有关的问题

    今天在回访网站流量来源时看到了一个很牛B的东西,和大家分享一下。
    给定一个顶点数为100000的图G,问是否存在Hamilton回路。现在,A宣称自己已经找到了一个Hamilton回路,但B不信,要A证明给他看。你能否想出一个办法使得,A可以让B相信自己有了正确的答案,但B依然不知道答案是什么。这种方法既科学又有趣,整个过程不需要第三者参与,仅仅靠AB两人之间的交流即可。这种方法可以让B有充分的理由相信A找到了Hamilton回路,但能保证B仍然得不到任何与正确答案有关的线索。

    首先,A生成一个100000的全排列P,然后用这个排列P把原图G的顶点标号打乱(对标号进行置换),这样就得到了一个同构的图G'。然后A把图G'告诉给B。注意,目前判断两个图是否同构还没有有效的P算法,因此除非A把排列P也告诉了B,否则B不知道G'和G是不是真的同构。接下来B从下面这两个问题中随机抽一个问题让A作答:叫A证明G与G'同构(即叫A给出排列P,确保他没有作假),或者叫A指出G'中的一条Hamilton回路。反复进行“构造G'—抽问”的过程,每次A答对后B都会更加确信A确实找到了原图G的Hamilton回路,来个十几二十次后A作假的嫌疑基本上可以被排除了。这是因为,如果A不知道原图G中的Hamilton回路,这两个问题他是不可能同时答对的,既然B是抽查的,A不可能每次总能答对。同时,除非B同时知道了两个问题的答案,否则B永远不知道原图G的Hamilton回路是什么。仅仅知道G'的Hamilton回路是没有用的,因为此时B连G和G'是否同构都不知道,更别提找出它们之间的对应关系了。

来源:http://www.zju88.cn/cgi-bin/bbstcon?board=Algorithm&file=M.1200769543

趣题:经典二分问题的一个扩展

    SETI@home可以在杂乱的射电数据中搜寻独特的讯号,你能在大街上的嘈杂声中清晰分辨出一个尖细的女声大叫“亚美蝶”。这些现象都表明,有时对集合里的所有元素进行整体考察可以很快找出我们所要找的个体。去年我们搞合唱比赛时,我又想到了一个绝佳的例子:你可以在合唱声中清楚地听到是否有人跑调。考虑这样一个问题,假如合唱团里有一个人唱歌始终走调,但我听不出来是谁走调,只能听出当前正在唱歌的人中是否有唱走调了的人。那么,我如何才能迅速地揪出那个唱走调的人?利用经典二分法,我们可以在log2(n)次合唱后找出唱走调了的人。每一次,我都把剩下的人平均分成两组,然后选其中一组来合唱:如果听不到走调的声音,这一组的人就全部过关;如果听到有人走调,那另一组里的人都可以被排除了。递归地对剩下的组进行同样的操作,log2(n)次操作后必定可以找出那个唱歌走调的人。
    现在的问题变得有些麻烦了。假如我们知道合唱队里有一个人唱歌爱跑调,但他不是总会跑调。具体地说,他只有1/2的概率唱错,但其余1/2的时间里他却唱得很准。现在,传统的二分法不再适用了,因为没有走调声已经不能起到排除的作用了。你能想出多少种可行的算法来找出那个人?下面提出一些可行的方法,你认为哪种方法更好?你能求出这些算法所需要的检测次数的期望值各是多少吗?

    1. 不断地随机生成一个大小为n/2的子集并对其进行检测,直到某次不能通过检测为止,然后递归地对其进行操作。
    2. 所选的子集大小为n/2是最优的吗?把上面这种方法的n/2改成n/a,常数a的最优值是多少?
    3. 检测次数的期望值还可以更小吗?我们想到,每次都重新生成一个新的集合其实并不科学,新集合本身是否包含老鼠屎也是得碰碰运气的。因此,对方法1的一个合理改进是:把集合平均划分为两个部分,交替对它们进行检测直到某次检测没通过为止,然后对该组递归操作下去。这种方法真的比前两种好吗?它所需要的期望次数是多少?
    4. 尝试对方法3进行改进。如果把集合平均划分成3份并循环进行检测,效果会不会更好一些?

    1. 选取的子集有1/2的概率覆盖了我们要找的那个人,子集里有他而他这次恰好又唱走调了则有1/4的概率。因此,不管规模有多大,平均需要4次才能把规模缩小一半。因此,检测次数的期望值为4*log2(n)。为了方便比较期望值的大小,后面的答案我们一律表示成一个常数乘以log2(n)的形式。
    2. 类似地,平均需要2a次检测才能把规模缩小到原来的1/a,因此总共花费的检测次数为2a*log2(n)/log2(a)。对函数求导,可得当a为e时函数值达到最小。此时的检测次数期望值为2e*log2(n)/log2(e)≈3.7683 * log2(n)。
    3. 这个就经典了。设方法3里把规模缩小一半所需要的检测的期望次数为m,下面我们来看m应该等于多少。把n个人平均分成两组,我们要找的老鼠屎有1/2的概率在第一组,有1/2的概率在第二组。因此,第一次就测出问题来有1/4的可能,第二次就测出问题也有1/4的可能。对于剩下的1/2种情况,局面变得又和最开始一样,只是平均需要的检测次数比原来多了2。根据期望值的定义,有m=(1/4)*1 + (1/4)*2 + (1/2)*(m+2),解得m=3.5。总的检测次数就是3.5 * log2(n),它比前面两种方法都要好。你可能不同意上面求m的方法。这没啥,如果你不断对m进行迭代,你会发现展开出来的式子就是最标准的期望值定义。
    4. 类似地,有m=(1/6)*1 + (1/6)*2 + (1/6)*3 + (1/2)*(m+3),解得m=5。于是,把规模缩小到原来的1/3平均需要5次检测,总的检测次数为5*log2(n)/log2(3)≈3.1546 * log2(n)。

题目来源:IBM Ponder This Dec07
原文还从熵的角度探寻了问题的最优算法,感兴趣的读者可以去看一看

概率学的创立:Chevalier de Méré问题

    在1717年,法国流行这样一个赌博游戏:连续抛掷一个骰子四次,赌是否会出现至少一个1点。经过试验,赌徒Chevalier de Méré发现至少出现一个1点比不出现的几率似乎要稍微大一些。他总是赌“会出现”,每次算下来他总是赢。在这个赌博游戏的一个“加强版”中,赌徒们需要猜测,连续抛掷两个骰子24次,是否会出现至少一对1点。Chevalier de Méré想,两个骰子同时掷出1点的几率显然是单个骰子掷出1点的几率的1/6,为了补偿几率的减小则必须要抛掷骰子24次。因此,两个赌博游戏换汤不换药,赌“出现”获胜的几率应该是一样的。但奇怪的是,他每次都赌会出现一对1点,结果几乎每次的最终结果都是输。他感到百思不得其解,于是向数学家Pascal寻求一个合理的解释。Pascal与大数学家Fermat用信件进行了交流,最终提出了概率问题的若干原理,创立了概率学。
    我们可以简单算一下,虽然直观感觉两个问题的概率应该相等,但实际上前者发生的概率大于0.5,后者发生的概率小于0.5,虽然两者相差并不多。

    问题1:连续抛掷一个骰子4次,至少出现一个1点的概率是多少?
    解答:在所有6^4种可能的情况中,一个1点都没有的情况有5^4种,因此至少出现一个1点的概率是(6^4-5^4)/6^4≈0.5177

    问题2:连续抛掷两个骰子24次,至少出现一对1点的概率是多少?
    解答:在所有36^24种可能的情况中,一对1点都没有的情况有35^24种,因此至少出现一对1点的概率是(36^24-35^24)/36^24≈0.4914

    谁能用一句话解释清楚,为什么赌徒Chevalier de Méré的直觉是错误的?不用Ctrl+A了,这次没有藏啥东西。

参考资料:http://www.cut-the-knot.org/Probability/ChevalierDeMere.shtml

KMP算法与一个经典概率问题

    考虑一个事件,它有两种概率均等的结果。比如掷硬币,出现正面和反面的机会是相等的。现在我们希望知道,如果我不断抛掷硬币,需要多长时间才能得到一个特定的序列。

序列一:反面、正面、反面
序列二:反面、正面、正面

    首先,我反复抛掷硬币,直到最近的三次抛掷结果形成序列一,然后我记下这次我抛掷了多少次才得到了我要的序列。重复执行这个过程,我可以算出得到序列一平均需要的抛掷次数。同样地,反复抛掷硬币直到序列二产生,它所需要的次数也有一个平均值。你认为这两个平均值哪一个大哪一个小?换句话说,出现序列一平均所需的抛掷次数少还是出现序列二平均需要的次数少?

    大多数人会认为,两个序列会以同样快的速度出现,因为在所有“正”和“反”的8种三元组合里,“反正反”和“反正正”各占1/8,其概率是均等的。而事实上,我们将会看到掷出序列二所需的次数更少一些。不妨考虑这样一个问题:在由“正”和“反”构成的n位01序列中,有多少个序列以序列一结尾但之前不曾出现过序列一?有多少个序列以序列二结尾但之前不曾出现过序列二?当n比较小时,两者答案是一样的(例如n=3时符合要求的情况都是唯一的),但到后来n越大时,两者的差距越明显:后者的个数总比前者的个数要多一些。不妨看一看n=6的情况。对于序列一,只有以下5个序列是符合要求的:

  • 反反反反正反
  • 反正正反正反
  • 正正正反正反
  • 正反反反正反
  • 正正反反正反

    但对于序列二来说,符合条件的序列就有7个:

  • 反反反反正正
  • 反正反反正正
  • 反反正反正正
  • 正反反反正正
  • 正正反反正正
  • 正正正反正正
  • 正反正反正正

    你可以通过计算机编程枚举,计算一下n为其它值的情况。计算结果和刚才也一样:在n位01序列中,以序列二结尾但之前不含序列二的情况不会少于以序列一结尾但之前不含序列一的情况。这说明,抛掷第n次硬币后恰好出现了序列二,其概率不会小于恰好出现序列一的概率。显然,当n渐渐增大时,这个概率应该呈下降趋势;同时,随着n的增长,两个序列各自出现的概率由相等开始慢慢拉开差距,第n次抛掷产生序列二的概率下降得要缓慢一些,或者说更多的情况集中发生在n更小的时候。因此总的来说,出现序列二所需要的抛掷硬币次数的期望值更小。
    虽然我们通过一系列的观察验证了这个结论,并且我们也相信这个结论是正确的(虽然没有严格的证明),但我们仍然不是很接受这个结论。这种情况是有悖于我们的直觉的,它与我们的生活经验不相符合。此刻,我们迫切需要一个解释,来说明这种出人意料的反常现象产生的原因。

    如果不亲自做几次试验的话,你很难体会到这种微妙的差距。考虑整个游戏的实际过程,“反正正”序列显然会出现得更早一些。假如某一次我们得到了序列“反正”。如果我们需要的是“反正反”序列,那么下一次抛掷结果为反面将结束本轮的抛掷,而下一次是正面则前功尽弃,你必须再次从零开始。如果我们需要的是“反正正”序列,那么下一次抛掷结果为正面将结束本轮的抛掷,而下一次是反面的话我至少不会惨到一切归零,这相当于我已经有了一个反面作为新的开头,只需再来两个正面即可。这样看的话,提前掷出“反正正”的可能性更大一些。
    反复体会上面的想法,了解KMP算法的网友会恍然大悟:这就是KMP算法的基本思路!考虑这样一个问题:我们在当前字串中寻找子串“反正正”第一次出现的位置。假如当前已经能匹配模式串的前两个字“反正”,主串中的下一个字是“正”则匹配成功,主串的下一个字是“反”则将使模式串的当前匹配位置退到第一个字。考虑一个更复杂的例子:我们希望在主串中寻找子串abbaba,现在已经在主串中找到了abbab。如果主串下一个字符是a,则成功匹配;如果主串下一个字符是b,则模式串最多能匹配到的位置退到了第三个字符,我只需要从abb开始继续匹配,而不必一切从头再来。
    我们可以用KMP算法完美地解决上面的问题。首先预处理出一个数组c,c[i,0]表示模式串匹配到了第i个字符,主串下一个字符为0(反)时,模式串的匹配位置将退到哪里;同样地,c[i,1]表示模式串匹配到了第i个字符,主串下一个字符为1(正)时,新的模式串匹配位置在什么地方。设f[i,j]表示第i次抛掷硬币后恰好匹配到模式串第j位有多少种情况,则f[i,j]=Σf(i-1,k) + Σf(i-1,l),其中k满足c[k,0]=j,l满足c[l,1]=j。将f[i,j]除以2的i次方,我们就得到了相应的概率值。或者更直接地,设P[i,j]表示第i次抛掷硬币后,最远能匹配到的模式串位置是第j位的概率,则P[i,j]=Σ( P(i-1,k)/2 ) + Σ( P(i-1,l)/2 )。注意,我们还应该添加一种特殊的概率值P[i,*],它表示在主串第i个字符以前已经成功匹配过的概率,这样的话下表中每一列的和才能为1。

    来看一看程序的输出结果:
Pattern 1: s[]="aba"
主串位置       1        2       3       4       5       6       7       8       9      10
匹配到s[0]  0.5000  0.2500  0.2500  0.2500  0.2188  0.1875  0.1641  0.1445  0.1270  0.1113
匹配到s[1]  0.5000  0.5000  0.3750  0.3125  0.2813  0.2500  0.2188  0.1914  0.1680  0.1475
匹配到s[2]  0.0000  0.2500  0.2500  0.1875  0.1563  0.1406  0.1250  0.1094  0.0957  0.0840
匹配到s[3]  0.0000  0.0000  0.1250  0.1250  0.0938  0.0781  0.0703  0.0625  0.0547  0.0479
已找到匹配  0.0000  0.0000  0.0000  0.1250  0.2500  0.3438  0.4219  0.4922  0.5547  0.6094

Pattern 2: s[]="abb"
主串位置       1        2       3       4       5       6       7       8       9      10
匹配到s[0]  0.5000  0.2500  0.1250  0.0625  0.0313  0.0156  0.0078  0.
0039  0.0020  0.0010
匹配到s[1]  0.5000  0.5000  0.5000  0.4375  0.3750  0.3125  0.2578  0.2109  0.1719  0.1396
匹配到s[2]  0.0000  0.2500  0.2500  0.2500  0.2188  0.1875  0.1563  0.1289  0.1055  0.0859
匹配到s[3]  0.0000  0.0000  0.1250  0.1250  0.1250  0.1094  0.0938  0.0781  0.0645  0.0527
已找到匹配  0.0000  0.0000  0.0000  0.1250  0.2500  0.3750  0.4844  0.5781  0.6563  0.7207

    这下我们可以清楚地看到,序列二提前出现的概率要大得多。注意到,根据我们的概率定义,表格中每一列的数字之和都是1。同时,倒数第二行的数字之和(有无穷多项)也应该为1,因为最后一行的概率就是倒数第二行的概率值累加的结果,而根据最后一行概率的定义,主串无穷长时已找到匹配的概率应该为1。因此,我们也可以把倒数第二行看作是模式串在主串第i个位置首次匹配成功的概率。我们可以根据这一结果近似地计算出抛掷次数的期望值。

Matrix67原创
转贴请注明出处