1.3节习题2. 用rand(1,6)来实现随机掷硬币
这个问题比较简单,就是把rand(1,6)产生的结果分类而已.
最简单的分类就是 1 2 3 当成正面, 4 5 6当成反面
稍微有点水平的分类就是 奇数当成正面 偶数当成反面, 这种分类编程上可能漂亮一些.
本章习题1.5 用rand(1,2)来实现公平的掷骰子
这也就是要求用掷硬币来实现掷骰子
一个直接的答案就是每次投掷3次硬币,规定正代表1,反代表0
三次的结果按 T1T2T3 组成一个二进制数
这个二进制的结果1-6分别代表骰子的结果,0和7忽略.
按道理这个题目应该终结了,可是作者偏偏给了一个提示,就是
要求算法可以终止还是不要求算法终止,两种情况是不同的.
这句话我的理解是:
如果算法不要求终止,就用之前的办法就可以,然后连续不断的输出随机数即可. 这种情况下,产生n个结果需要调用rand(1,2)的次数是3n.
如果要求算法终止,比如知道需要生成n个随机数,做法可以有什么不同呢? 我的理解是可以利用之前每3次投硬币丢到的2次结果. 初看起来是无法利用的,比如产生的结果是7,那代表那个数字呢? 其实是可以利用的.但是方法应该有不同. 我们举个简单的例子比如已知需要投2次骰子,其实只要能等几率产生36种可能的结果就行了. 比如
(1,1) | 0 |
(1,2) | 1 |
(1,3) | 2 |
... | ... |
(1,6) | 5 |
(2,1) | 6 |
(2,2) | 7 |
... | ... |
(6,6) | 35 |
这个表格,左边是掷骰子的结果,右边是对应起来的二进制的结果(为了方便写成十进制而已).
从右边到左边有什么规律没有?
实际上,如果将左边骰子的结果改变一下,从1-6对应到0-5,左边实际上可以认为是六进制,所以现在的算法就是把掷硬币的结果进行转换,改成六进制即可.那掷硬币的结果怎么处理呢?就是让结果反复地除以6啊.这样程序实现的问题也就解决了.
这个方法跟上面的方法的区别不仅仅在于此,最主要的区别在于利用了之前投硬币过程中无法利用的结果.
第二种方法,生成n个骰子的结果,需要投掷的硬币次数是ceil(log2(6^n)),这个结果基本上等于n*log2(6),显然优于上面的3n.