在一篇老日志中,我提到了一个经典的概率问题:平均需要抛掷多少次硬币,才会首次出现连续两个正面?它的答案是 6 次。它的计算方法大致如下。
首先,让我们来考虑这样一个问题: k 枚硬币摆成一排,其中每一枚硬币都可正可反;如果里面没有相邻的正面,则一共有多少种可能的情况?这可以用递推的思想来解决。不妨用 f(k) 来表示摆放 k 枚硬币的方案数。我们可以把这些方案分成两类:最后一枚硬币是反面,或者最后一枚硬币是正面。如果是前一种情形,则我们只需要看前 k – 1 枚硬币有多少摆法就可以了;如果是后一种情形,那么倒数第二枚硬币必须是反面,因而这种情形下的方案数就取决于前 k – 2 枚硬币的摆放方案数。因此我们得到, f(k) = f(k – 1) + f(k – 2) 。由于摆放一枚硬币有两种方案,摆放两枚硬币有三种方案,因而事实上 f(k) 就等于 Fk+2 ,其中 Fi 表示 Fibonacci 数列 1, 1, 2, 3, 5, 8, …的第 i 项。
而“抛掷第 k 次才出现连续两个正面”的意思就是,最后三枚硬币是反、正、正,并且前面 k – 3 枚硬币中正面都不相邻。因此,在所有 2k 种可能的硬币正反序列中,只有 Fk-1 个是满足要求的。也就是说,我们有 F1 / 4 的概率在第二次抛币就得到了连续两个正面,有 F2 / 8 的概率在第三次得到连续两个正面,有 F3 / 16 的概率在第四次得到连续两个正面⋯⋯因此,我们要求的期望值就等于:
这个无穷级数就等于 6:
不过,最后这一步来得也太假了,因为我们借助了强大的 Mathematica 。今天重新翻到这篇旧日志时,我就在想,究竟怎样求出这个无穷级数呢?这个数列的某些特征让我联想到了生成函数这一数学工具,用生成函数处理这样的数列非常合适。我在很早很早以前就写过介绍生成函数的文章,但遗憾的是,我对生成函数运用的了解,仅仅局限于教材和网络上给出的各种经典例子,从没有亲自用到过生成函数。今天算是我第一次真正使用生成函数,深感生成函数之妙。如果你是第一次看到生成函数的应用,想必你会大吃一惊,这种诡异的方法竟然能把答案搞出来!整个过程用到了很多生成函数的经典处理手段,这让它足以打败教材上的各种经典例子,成为了我最爱的生成函数应用例题之一。
让我们先来说说什么是生成函数吧。生成函数就是对数列进行编码的一种方式。我们可以用一个含有无限多项的多项式(注:这个说法是不准确的,有无限多项的不能叫多项式) a1 · x1 + a2 · x2 + a3 · x3 + … 把整个数列的全部信息装进去,其中第 i 次项系数就表示数列的第 i 项。因此,Fibonacci 数列的生成函数就可以写成:
厉害就厉害在,我们可以把生成函数表示成一个更简单的形式。先来看看 g(x)·x 的结果:
再看看 g(x) + g(x)·x 的结果:
你会发现, Fibonacci 数列的递推性质,使得上面这行式子与 g(x) 本身非常相像。事实上,如果把 g(x) 的每一项都除以 x ,再减去最前面多出来的 1 ,就能得到上面的这行式子了。因此,我们有:
我们甚至可以就此解出 g(x) 来:
于是,整个无穷级数 g(x) 被我们化简为了一个关于 x 的代数式!注意,虽然这个等式只在 x 充分小(小到级数 g(x) 收敛)的时候才有意义,不过这并不妨碍我们用这个代数式来代表 Fibonacci 数列的生成函数。我们可以把 Fibonacci 数列看作是生成函数的一个“展开”:
也就是说,这么一个小小的代数式就容纳了 Fibonacci 数列的全部信息!
生成函数是如此地具有代表性,以至于在研究数列时,我们常常会给出它的生成函数。在数列百科全书 OEIS 中,生成函数几乎是必不可少的一项。例如,在 Fibonacci 数列 的描述中,FORMULA 一栏的第一行就是 G.f.: x/(1-x-x^2) ,说的就是 Fibonacci 数列的生成函数(generating function)。
更绝的是,我们还可以直接对数列的生成函数进行变换,从而得到新的数列。比方说,在生成函数上再乘以一个 x ,我们就会让每一项的 x 的指数加 1 ,从而让整个数列右移一位,得到了一个新的数列 Fi-1,即 0, 1, 1, 2, 3, 5, …
现在,我们需要用各种代数运算手段,对等式左边的生成函数进行变换,让等式右边的展开式变成本文开头的那个数列。什么操作能够同时让数列第 1 项除以 2 ,第 2 项除以 4 ,第 3 项除以 8 ,以此类推,让所有的第 i 项都除以 2i 呢?我们可以把所有的 x 都用 x / 2 来替代:
化简一下:
这就是数列 Fi-1 / 2i 的生成函数了。接下来,我们想要让第 i 项系数乘以一个 i ,也就是想要让每一项的系数都乘以该项的次数,这该怎么办呢?最神奇的地方出现了——我们对生成函数进行求导:
也就是:
不过,求导的同时,x 的次数也移动了一位。我们在生成函数上再乘以 x ,把 x 的次数纠正回来:
这就是本文最初的那个数列的生成函数了。令 x = 1 ,便有:
Tada!
太惊奇了……
有了这篇之后,下面的问题就变得容易解决了:
http://projecteuler.net/index.php?section=problems&id=137
厉害啊。。。。
这是……母函数?
这个……俺在大一学各种数列的极限中有使用过。
不得不说Matrix67你花了点功夫做了公式效果就是不一样啊,上一次的看的头昏了。这一次简洁明了,用你的说法就是“想得出来”。
m牛啊。。你的”一篇老日志”的链接会被定向到另外一篇155上面…不是3638..你去瞧瞧..
前排。。Orz
刚才那个回复还没看正文。。。
看完表示必须要再来回复一次
怎么可以这么牛!!!
那么,Mathematica这个软件是如何机械化地计算这个无穷级数的结果呢?而且这个软件非常肯定地告诉你,这是答案是一个确定值。。。。有什么办法机械地解题么?
LS你的ID万年不变啊。。。
赞,不过此方法解决这个问题显得有点复杂了。http://cos.name/2011/01/different-ways-to-solve-a-tossing-problem/ 中鞅那一节有公式的。对于本题目就是1/(0.5^2)+1/(0.5)=6;对于正正正,就是1/(0.5^3)+1/(0.5^2)+1/(0.5)=14,那个公式是适合于任意有限离散分布的,不局限于抛掷均匀硬币的个案。
这个问题提了这么久了,作为一个门外汉请问,连续6个正面就是直接套这个公式吗?
生成函数就是这么用的。。
a2=0
a1=0.5*(1+a2)+0.5*(1+a0)
a0=0.5*(1+a1)+0.5*(1+a0)
得a1=1,a0=6
这里a_n是已经看到n个正面时,还需要抛硬币的次数的期望。
亮点在最后的Tada。。。
用错位相减能做到否?
GaRBl*ng:可以,不过要做两次,先求Sigma(F_k/2^k),并利用Fibonacci数列递推公式。
11楼高明。本题生成函数解法实质上也是错位相减。
tada 有什么含义?
前排……
求导。。。这个方法太强了
那个。。。g(1)=1 是怎么回事?Fibnacci数列明明没极限啊
\ 这就是那个x 必须小到g(x)收敛吗?
本月的UYHIP,用生成函数来做也非常简单
本月的UYHIP,用生成函数来做也十分简单
本月的UYHIP,用生成函数来做也非常的简单
推荐一本生成函数的书 http://www.math.upenn.edu/~wilf/gfologyLinked2.pdf
没仔细算,感觉上x=1在函数g以0点为原点的收敛园外面
好吧。。用惯了生成函数的表示看一眼就知道怎么做
生成函数?..去wiki看看
看到一个正面,抛掷数的期望是2。当看到第一个正面后,有两种情况:(1)以1/2的概率,再抛一次就能看到两个正面;(2)以1/2的概率,再抛一次得到背面,那整个过程相当于从零开始。设看到2次正面的抛掷数期望为x,则x = 1/2 * (2+1) + 1/2 * (2 + 1 + x),所以x = 6。用同样的思路,根据n-1个正面的情况,可以很容易递推到n个正面的情况。
表示具体数学上有个更强的结论…
实际上斐波那契数列的通项求出后更加简洁,我估计学数学分析都有思路
这个应当是递归更简洁吧.(1)抛出2个连续正面,问题结束;抛出(2)一个反面或(3)先正后反,接下来问题都回到原样。因此x=1/4*2+1/2*(1+x)+1/4*(2+x),即x=6
好像和递推方法有联系咯 应该挺有逻辑性的
构造状态机表示毫无压力
太精彩了
设当前非正面时期望x,当前一个正面时期望y
x = 0.5 * (x + 1) + 0.5 * (y + 1)
y = 0.5 * 1 + 0.5 * (x + 1)
连立得
x = 0.5x + 0.5 + 0.5 * (2 + 0.5x)
x = 0.75x + 1.5
0.25x = 1.5
x = 6
实际上斐波那契数列的通项求出后更加简洁,我估计学数学分析都有思路