以下是对最初提供的代码的一组细微修改,可正确计算估计值。
我已经用 #### 前缀的 cmets 标记了修改,并参考下面的解释对其进行了编号。
import random
#variable declaration
numberOfStreaks = 0
for experimentNumber in range(10000):
# Code that creates a list of 100 'heads' or 'tails' values.
CoinFlip = [] #### (1) create a new, empty list for this list of 100
for i in range(100):
CoinFlip.append(random.randint(0,1))
#does not matter if it is 0 or 1, H or T, peas or lentils. I am going to check if there is multiple 0 or 1 in a row
#### # (6) example / test
#### # if uncommented should be 100%
#### CoinFlip = [ 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'T', 'T', 'T', 'T' ]
# Code that checks if there is a streak of 6 heads or tails in a row.
streak = 1 #### (2, 4) any flip is a streak of (at least) 1; reset for next check
for i in range(1, len(CoinFlip)): #### (3) start at the second flip, as we will look back 1
if CoinFlip[i] == CoinFlip[i-1]: #checks if current list item is the same as before
streak += 1
else:
streak = 1 #### (2) any flip is a streak of (at least) 1
if streak == 6:
numberOfStreaks += 1
break #### (5) we've found a streak in this CoinFlip list, skip to next experiment
#### if we don't, we get percentages above 100, e.g. the example / test above
#### this makes some sense, but is likely not what the book's author intends
print('Chance of streak: %s%%' % (numberOfStreaks / 100.0))
这些变化的解释
以下是对这些更改的简要说明。每个都在很大程度上是独立的,解决了代码的不同问题。
- 在每个实验开始时清除/创建 CoinFlip 列表
- 确认任何翻转,即使是单个
'H' 或 'T'(或 1 或 0),都代表连续 1
- 如果不进行此更改,代码实际上需要六次后续匹配才能完成初始硬币翻转,总共连续七次(一个稍微不太直观的替代更改是将
if streak == 6: 替换为 if streak == 5:)
- 从第二次翻转开始检查,使用
range(1, len(CoinFlip))(注意列表是零索引的)
- (移动范围和)在每次检查之前重置
streak 计数器
- 如果不进行此更改,则实验中的初始条纹可能会添加到先前实验的部分条纹中(请参阅测试代码以获取建议的演示)
- 一旦我们发现一个条纹就退出检查
本书中的这个问题有些不明确,最后一部分可以解释为“检查 [at least?] a [single?] [精确吗?] 六个 [或更多?] 的连续发现”。此解决方案将 check 解释为布尔评估(即我们只记录此列表包含或不包含条纹),并且非排他性地解释 a(即我们允许更长的条纹或多个条纹要计算;正如问题中提供的代码一样)。
(可选 6.)测试代码
注释掉的“示例/测试”允许您在每个实验中将通常随机生成的翻转切换为相同的已知值。在这种情况下,应计算为 100% 的固定列表。如果您不同意对任务规范的解释并禁用 (5.) 中描述的检查退出,您可能会期望程序报告 200%,因为在每个实验中都有两个不同的六个条纹。结合此输入禁用 break 会准确报告。
您应该始终使用这种类型的技术(使用已知输入、验证输出)来说服自己,代码是否按其声称或您期望的那样工作。
固定输入 CoinFlip = [ 'H', 'H', 'H', 'H', 'T', 'T', 'T' ] 可用于突出显示 (4.) 修复的问题。如果还原,代码将计算包含六个连续H 或T 的连续实验的百分比(全部使用此输入)为 50%。虽然 (5.) 修复了一个独立问题,但删除添加的 break 会进一步加剧错误并将计算的百分比提高到 99.99%。对于此输入,计算得到的包含六个连胜的百分比应为 0%。
您会发现此处提供的完整代码产生了大约 80% 的估计值。这可能令人惊讶,但book 的作者暗示可能是这种情况:
一个人几乎永远不会连续写下六个正面或六个反面,即使它很可能发生在真正随机的硬币翻转中。
- 艾尔·斯威加特,Coin Flip Streaks
您还可以考虑其他来源。 WolframAlpha 计算得出“在 100 次抛硬币中连续出现 6 次正面”的机会约为 1 比 2。在这里,我们正在估计连续 6 个(或更多)正面或连续六个(或更多)反面的机会,这你可以期待更有可能。作为这种累积效应的一个更简单、独立的例子:考虑从一副普通扑克牌中挑选红心的几率是 52 分之 13,但挑选红心或方块的几率是 52 分之 26 .
计算注意事项
这也有助于理解作者在计算百分比时也采取了捷径。这可能会使查看最终计算的初学者感到困惑。
召回,计算百分比:
我们知道要运行的实验总数将是 10000
因此
后记: 我冒昧地将最后一行中的100 更改为100.0。这允许代码在 Python 2 中正确计算百分比。这对于 Python 3 不是必需的,如问题和书中所述。