自动化无聊的事情 - 硬币翻转条纹

Automate the boring stuff - Coin flip streaks

我知道现在有很多关于它的问题,即使是同一个问题,但我想我尝试了一些不同的方法。

任务是对 10.000 个样本进行 100 次翻转,然后计算所有样本出现 6 次正面或反面条纹的概率 - 据我所知。但是在之前的问题中,编码问题被描述为有点模糊。因此,如果你们能指出代码中的错误,那就太好了:)

我试图尽可能地懒惰,这导致我的 macbook 工作非常努力。这是我的代码。当前值与之前值的比较的第一次迭代是否有问题(据我了解,我会将索引 -1(然后是索引 100?)与当前值进行比较?)

import random

#variable declaration

numberOfStreaks = 0
CoinFlip = []
streak = 0

for experimentNumber in range(10000):
    # Code that creates a list of 100 'heads' or 'tails' values.
    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        

    # Code that checks if there is a streak of 6 heads or tails in a row.
    for i in range(len(CoinFlip)):
        if CoinFlip[i] == CoinFlip[i-1]:  #checks if current list item is the same as before
            streak += 1 
        else:
            streak = 0

        if streak == 6:
            numberOfStreaks += 1

print('Chance of streak: %s%%' % (numberOfStreaks / 100))

我哪里弄得一团糟?我真的看不出来!

您需要重置CoinFlip列表。您当前的程序只是不断地附加到 CoinFlip,这使得列表非常长。这就是你的表现不好的原因。我还添加了一个 i==0 的检查,这样您就不会与列表的末尾进行比较,因为从技术上讲,这不是连胜的一部分。

for experimentNumber in range(10000):
    # Code that creates a list of 100 'heads' or 'tails' values.
    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

    # Code that checks if there is a streak of 6 heads or tails in a row.
    for i in range(len(CoinFlip)):
        if i==0:
            pass
        elif CoinFlip[i] == CoinFlip[i-1]:  #checks if current list item is the same as before
            streak += 1
        else:
            streak = 0

        if streak == 6:
            numberOfStreaks += 1

    CoinFlip = []

print('Chance of streak: %s%%' % (numberOfStreaks / (100*10000)))

我也认为你需要除以 100*10000 才能得到真正的概率。我不确定为什么他们 "hint" 建议只除以 100。

我无法对 Stuart 的回答发表评论,因为我最近加入并且没有声望,所以这就是为什么这是一个单独的答案。我是编程新手,所以如果我错了,请任何人纠正我。我只是在自己学习过程中解决同样的问题。

首先,我不确定为什么当范围长度相同时您使用多个 for 循环,所以我将它们结合起来并继续得到相同的结果。

此外,我注意到最终计算结果以百分比形式显示,但并未从原始计算结果转换为百分比。

例如,5/100 = .05 -> .05 * 100 = 5%

因此,我添加了一个将小数转换为百分比并将其四舍五入到小数点后4位的函数。

最后,把硬编码改成变量,显然没关系,只是为了解释我改变的东西。

    import random

    #variables
    n_runs = 10000
    flips_per_run = 100
    total_instances = n_runs * flips_per_run
    coinFlip = []
    streak = 0
    numberOfStreaks = 0

    for experimentNumber in range(n_runs):
        # Code that creates a list of 100 'heads' or 'tails' values.'
        for i in range(flips_per_run):
            coinFlip.append(random.randint(0,1))
            if i==0:
                pass
            elif coinFlip[i] == coinFlip[i-1]:
                streak += 1
            else: 
                streak = 0

            if streak == 6:
                numberOfStreaks += 1

        coinFlip = []

    #calculation for chance as a decimal    
    chance = (numberOfStreaks / total_instances)
    #function that converts decimal to percent and rounds
    def to_percent(decimal):
        return round(decimal * 100,4)
    #function call to convert result
    chance_percent = to_percent(chance)
    #print result 
    print('Chance of streak: %s%%' % chance_percent)

输出:连胜几率:0.7834% 而不是 .007834%

import random
numStreaks = 0
test = 0
flip = []

#running the experiment 10000 times

for exp in range(10000):
    for i in range(100): #list of 100 random heads/tails

        if random.randint(0,1) == 0:
            flip.append('H')
        else:
            flip.append('T')

    for j in range(100): #checking for streaks of 6 heads/tails

        if flip[j:j+6] == ['H','H','H','H','H','H']:
            numStreaks += 1
        elif flip[j:j+6] == ['T','T','T','T','T','T']:
            numStreaks += 1
        else:
            test += 1 #just to test the prog
            continue
print (test)
chance = numStreaks / 10000
print("chance of streaks of 6: %s %%" % chance )

我开始的时候更复杂,现在看到你的代码我想我想不出更复杂的了 "logic" :)

找不到写第二部分的工作思路!

import random

number_of_streaks = 0
coin_flips = []
streak = 0

for experiment_number in range (10000):
    # Code that creates a list of 100 'heads' and 'tails' values

def coin(coin_fl):  # Transform list into plain H or T
    for i in coin_flips[:-1]:
        print(i + ' ', end = '')

for i in range(100):    # Generates a 100 coin tosses
    if random.randint(0, 1) == 0:
        coin_head = 'H'
        coin_flips = coin_flips + [coin_head]
    else:
        coin_tail = 'T'
        coin_flips = coin_flips + [coin_tail]

coin(coin_flips)

以下是对最初提供的代码的一组小修改,可以正确计算估算值。

我已经用 #### 前缀的注释标记了修改,并参考后面的解释对它们进行了编号。

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))

这些变化的解释

以下是对这些变化的简要说明。每个在很大程度上是独立的,解决了代码的不同问题。

  1. 每次实验开始时CoinFlip列表的clearing/creating
    • 如果没有这个,新元素会添加到之前实验
    • 的列表中
  2. 承认任何翻转,即使是单个 'H''T'(或 10),都代表连续 1
    • 如果不进行此更改,代码实际上需要六次与初始硬币抛掷的后续匹配,总共连续七次(一个不太直观的替代更改是将 if streak == 6: 替换为 if streak == 5:
  3. 从第二次翻转开始检查,使用 range(1, len(CoinFlip))(n.b。列表为 zero-indexed)
    • 当代码沿着列表往回看时,for 循环中 range() 以 0 开头会错误地将索引 0 与索引 -1 进行比较(the last element of the list)
  4. (移动示波器并)在每次检查前重置 streak 计数器
    • 如果不进行此更改,实验中的初始连胜可能会添加到之前实验的部分连胜中(请参阅测试代码 以获得建议的演示)
  5. 一旦我们发现连胜就退出检查

书上的这个问题有些表述不当,最后一部分可以解释为"check if [at least?] a [single?] streak of [precisely?] six [or more?] is found"中的任何一个。此解决方案将 check 解释为布尔值评估(即我们仅记录此列表是否包含连续记录),并解释 a non-exclusively(即我们允许计算更长的条纹或多个条纹;问题中提供的代码也是如此)。

(可选 6.)测试代码

注释掉的 "example / test" 允许您在每个实验中将通常随机生成的翻转切换为相同的已知值。在这种情况下,应计算为 100% 的固定列表。如果您不同意对任务规范的解释并禁用 (5.) 中描述的检查退出,您可能希望程序报告 200%,因为在每个实验中有两个明显的 6 条纹。结合此输入禁用 break 会准确报告。

您应该始终使用这种类型的技术(使用已知输入、验证输出)来说服自己代码是否如其声称或您期望的那样工作。

固定输入 CoinFlip = [ 'H', 'H', 'H', 'H', 'T', 'T', 'T' ] 可用于突出显示 (4.) 修复的问题。如果还原,代码将计算包含连续六个 HT 的实验(全部使用此输入)的百分比为 50%。虽然 (5.) 修复了一个独立的问题,但删除添加的 break 会进一步加剧错误并将计算的百分比提高到 99.99%。对于此输入,包含连续六次的计算百分比应为 0%。

您会发现此处提供的完整代码生成了大约 80% 的估计值。这可能令人惊讶,但 book 的作者暗示可能是这种情况:

A human will almost never write down a streak of six heads or six tails in a row, even though it is highly likely to happen in truly random coin flips.

- Al Sweigart, Coin Flip Streaks

您还可以考虑其他来源。 WolframAlpha计算得到"streak of 6 heads in 100 coin flips"的几率大约是1分之2。在这里,我们估计连续出现 6( 或更多)头的几率 or 连续出现六(或更多) 尾巴,你可以预期更有可能。作为这种累积效应的一个更简单、独立的例子:假设从一副普通的扑克牌中挑选一颗心的几率是 52 分之 13,但是挑选一颗心 一颗方块的概率是52 中的 26.


计算注意事项

作者在计算百分比时也采取了捷径,这也可能有助于理解。这可能会让初学者在看最后的计算时感到困惑。

Recall, a percentage is calculated:

\frac{x}{total}\times100

We know that total number of experiments to run will be 10000

\frac{x}{10000}\times100

Therefore

\frac{x}{10000}\times100=\frac{100x}{10000}=\frac{x}{100}

后记: 我冒昧地将最后一行的 100 更改为 100.0。这允许代码在 Python 2 中正确计算百分比。这对于 Python 3 不是必需的,如问题和书中所述。

我的业余尝试

import random

#reset strakes
numberOfStreaks = 0
#main loop
for experimentNumber in range(10000):

    # Code that creates a list of 100 'heads' or 'tails' values.
    # assure the list is empty and all counters are 0
    coinFlip=[]
    H=0
    T=0
    for fata in range(100):
        # generate random numbers for head / tails
        fata = random.randint(0,1)
        #if head, append 1 head and reset counter for tail
        if fata == 0:
            coinFlip.append('H')
            H += 1
            T = 0
        #else if tail append 1 tail and reset counter for head
        elif fata == 1:
            coinFlip.append('T')
            T += 1
            H = 0

    # Code that checks if there is a streak of 6 heads or tails in a row.
    # when head and tail higher than 6 extract floored quotient and append it to numberOfStreaks,
    # this should take into consideration multiple streaks in a row.

    if H > 5 or T > 5:
        numberOfStreaks += (H // 6) or (T // 6) 

print('Chance of streak: %s%%' % (numberOfStreaks / 100))

输出:

Chance of streak: 3.18%

这段代码给出了大约 54% 的正确概率,正如在上一个 post 上面

中对 wolfram alpha 所检查的那样
import random
numberOfStreaks = 0

for experimentNumber in range(10000):
    # Code that creates a list of 100 'heads' or 'tails' values.
    hundredList = []
    streak = 0
    for i in range(100):
        hundredList.append(random.choice(['H','T']))
    # Code that checks if there is a streak of 6 heads or tails in a row.
    for i in range(len(hundredList)):
        if i == 0:
            pass
        elif hundredList[i] == hundredList[(i-1)]:
            streak += 1
        else:
            streak = 0

        if streak == 6:
            numberOfStreaks += 1
            break
        
print('Chance of streak: %s%%' % (numberOfStreaks / 100))

我认为所有的答案都增加了问题!!!杰出的!!! 但是,如果我们正在寻找 6 个连续的相同硬币翻转,它不应该是 'streak == 5' 吗? 例如,THHHHHHT,streak == 6 在这里没有帮助。

仅翻转 100 次的代码:

coinFlipList = []

for i in range(0,100):
    if random.randint(0,1)==0:
        coinFlipList.append('H')
    else:
        coinFlipList.append('T')
print(coinFlipList)

totalStreak = 0
countStreak = 0
for index,item in enumerate(coinFlipList):
    if index == 0:
        pass
    elif coinFlipList[index] == coinFlipList[index-1]:
        countStreak += 1
    else:
        countStreak = 0
    if countStreak == 5:
        totalStreak += 1
print('Total streaks %s' %(totalStreak))

如果我遗漏了什么,请告诉我。