'Automate the boring stuff - Chapter 4: Coin Flip Solution

I am struggling with the solution of the 'Coin Flip' practice project at the end of chapter 4 in 'Automate the boring stuff' for python programming.
I have two solutions, both yielding a totally different result (first one is clearly false). I am not sure, what is the right solution to the answer.

Solution 1:

import random
nextFlip = []
numberOfStreaks = 0

# Code that creates a list of 10000 'heads' or 'tails' values.

for expNum in range(10000):                
    select = random.randint(0,1)
    if select == 0:
        nextFlip.append('H')
    elif select == 1:
        nextFlip.append('T')
    
# Code that checks if there is a streak of 6 heads or tails in a row.

    for i in range(0,(len(nextFlip)-6)):
        if nextFlip[i] == nextFlip[i+1] == nextFlip[i+2] == nextFlip[i+3] == nextFlip[i+4] == nextFlip[i+5] != nextFlip[i+6]:
            numberOfStreaks +=1


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

Solution 2:

import random
nextFlip = []
hlist = 0
tlist = 0
numberOfStreaks = 0

# Code that creates a list of 10000 'heads' or 'tails' values.

for expNum in range(10000):                
    select = random.randint(0,1)
    if select == 0:
        nextFlip.append('H')
    elif select == 1:
        nextFlip.append('T')

    
# Code that checks if there is a streak of 6 heads or tails in a row.

    for i in range(0,(len(nextFlip)-6)):
        if nextFlip[i] == 'H':
            hlist += 1
            if hlist == 6:
                numberOfStreaks +=1
            
        elif nextFlip[i] == 'T':
            tlist += 1
            if tlist == 6:
                numberOfStreaks +=1

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

Maybe someone can help me and tell me what I did wrong.



Solution 1:[1]

I'm just learning with the same book too. I found this easier to treat as a string, rather than a list. I believe that the problem is asking you to find whether a streak of 6 or more consecutive heads or a streak of 6 or more consecutive tails occurs in 100 tosses of a coin. For each 100 tosses, the result is 0 or 1 (I don't think you count multiple streaks). The 10,000 runs is to gain a reasonable accuracy and avoid sampling error.

import random

number_of_streaks = 0

for experiment_number in range(10000):
    
    # Code that creates a list of 100 'heads' or 'tails' values
    coin_list = ''
    for i in range(100):
        if random.randint(0, 1) == 0:
            coin_list = coin_list + 'H' # heads
        else:
            coin_list = coin_list + 'T' # tails
    
    # Code that checks if there is a streak of 6 heads or tails in a row
    if 'HHHHHH' in coin_list or 'TTTTTT' in coin_list:
        number_of_streaks +=1

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

Further to advice in comments from @rchome, I added a simple timer to this original code (https://stackoverflow.com/a/1557584/17555691) and it gave the following response:

Chance of streak: 80.06% --- 0.8480644226074219 seconds ---

Next, I made a small change, using append to a list and then converting to a string:

# Code that creates a list of 100 'heads' or 'tails' values
coin_list = []
for i in range(100):
    if random.randint(0, 1) == 0:
        coin_list.append('H') # heads
    else:
        coin_list.append('T') # tails

# Code that checks if there is a streak of 6 heads or tails in a row
coin_string = ''.join(coin_list)
if 'HHHHHH' in coin_string or 'TTTTTT' in coin_string:
    number_of_streaks +=1

Output from this version:

Chance of streak: 80.26% --- 0.739051342010498 seconds ---

Then made a change, using recommendation from @Alain T:

# Code that creates a list of 100 'heads' or 'tails' values
flips   = "".join(random.choice('HT') for _ in range(100))

# Code that checks if there is a streak of 6 heads or tails in a row
if 'HHHHHH' in flips or 'TTTTTT' in flips:
    number_of_streaks +=1

Output from this version: Chance of streak: 80.74% --- 0.4248924255371094 seconds ---

The above results were quite typical on repeated executions.

Solution 2:[2]

This seems to work:-

import random

N = 10_000
S = 6
HEAD = 'H'
TAIL = 'T'
T = [HEAD if random.randint(0, 1) else TAIL for _ in range(N)]
c = T[0]
s = 1
STREAKS = 0
for t in T[1:]:
    if t == c:
        s += 1
        if s == S:
            STREAKS += 1
            s = 0
    else:
        c = t
        s = 1
print(STREAKS)

Solution 3:[3]

You could generate the random flips using a comprehension and store it in a string to make processing of the streaks easier. Since streaks can overlap, you need to examine subranges starting at every position:

flips   = "".join(random.choice("HT") for _ in range(1000))

streaks = sum(flips[i:i+6] in ('HHHHHH','TTTTTT') for i in range(1000))

The sum() function will convert boolean values to 1 or zero (True is 1), so adding up the result of the streak conditions produces the total.

Solution 4:[4]

#Defining Inputs and importing modules
import random
numberOfStreaks = 0
mylist=[]
countH=0
countT=0
conuterTail=[]
conuterHead=[]
for experimentNumber in range(10000):
# Code that creates a list of 10000 'heads' or 'tails' values.
    rndnum=random.randint(0,1);
    if rndnum==0:
        countH=0 # if the random number is "0" then "HEAD" counts will be reset
        mylist.append(['T'])
        countT+=1
    else:
        countT=0 # if the random number is "1" then "TAIL" counts will be reset
        mylist.append(['H'])
        countH+=1
# Code that checks if there is a streak of 6 heads or tails in a row.
    if countT==6:
        conuterTail.append(countT)
    elif  countH==6:
        conuterHead.append(countH);


numberOfStreaks=len(conuterHead)+len(conuterTail)

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

Solution 5:[5]

I understood the question to be asking: what is the chance that a streak of 6 consecutive like coin tosses would be contained within a set of 100 random coin tosses. I thought this should include instances of multiple streaks within the same set of tosses. Whenever a streak of values reached six in a row, the count was reset to zero, in order to capture the next streak. Some iterations contained no streaks, while others contained multiple. A large number of iterations were averaged to smooth variances. The average value returned was 158-160%, meaning very likely.

import random
numberOfStreaks = 0
countH = 0
countT = 0
iterations_of_experiment = 10000
for experimentNumber in range(iterations_of_experiment):
    #Code that creates a list of 100 'heads' or 'tails' values
    setOfHundred = [] #set list to empty each iteration
    
    for i in range(100):
        if random.randint(0, 1) == 0:
            countT = 0  #Set Tails count to zero on head flip
            setOfHundred.append('H')
            countH += 1
            if countH == 6:
                numberOfStreaks += 1
                countH = 0 #Reset counter to zero to capture -
        else:              #multiple streaks
            countH = 0
            setOfHundred.append('T')
            countT += 1
            if countT == 6: 
                numberOfStreaks += 1
                countT = 0

print('In ' + str(iterations_of_experiment) + \
    ' iterations of 100 coin tosses, there were ' + str(numberOfStreaks) + \
    ' streaks of 6 consecutive like coin flips.')
print('Chance of streak: ' + str(numberOfStreaks / iterations_of_experiment * 100) + '%')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Alain T.
Solution 4 Farshid Ghobadzadeh
Solution 5 geanakuch