1

I am trying to build a program that will create a random list of Heads or Tails ('H', 'T') and then count how many times either the 'H' or the 'T' is repeated 6 times. I think I have figured out the random list, which works fine when i run it independently. But when it comes to the counting, I'm not sure how to make python detect a sequence of 6 identical letters in a list.

for x in range(0, len(coin_list)-1):
    if coin_list[x] == coin_list[x+1] and coin_list[x] == coin_list[x+2] and \
    coin_list[x] == coin_list[x+3] and coin_list[x] == coin_list[x+4] and \
    coin_list[x] == coin_list[x+5]:
       streak_counter+=1
    else:
        continue

    if streak_counter ==6: 
        numberOfStreaks+=1
        streak_counter=0
    else: 
        continue

First this block checks each value in the list coin_list and also checks if all 5 values that come after it are identical to the first value. If they are, then the streak_counter variable is increased by one. Then the program checks if the streak_counter variable is equal to 6, if it is, then the numberOfStreaks variable is increased by one, and the streak_counter is reset to 0.

When I run this program I don't get my expected outcome. The program always returns that numberOfStreaks is equal to 0, and therefore the percentage of streaks is 0%.

Not sure where I'm going wrong here. Any help would be much appreciated.

import random
numberOfStreaks = 0


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

    # Code that checks if there is a streak of 6 heads or tails in a row.
    for x in range(0, len(coin_list)-1):
        if coin_list[x] == coin_list[x+1] and coin_list[x] == coin_list[x+2] and \
        coin_list[x] == coin_list[x+3] and coin_list[x] == coin_list[x+4] and \
        coin_list[x] == coin_list[x+5]:
           streak_counter+=1
        else:
            continue

        if streak_counter ==6: 
            numberOfStreaks+=1
            streak_counter=0
        else: 
            continue


print(numberOfStreaks)
print('Chance of streak: %s%%' % (numberOfStreaks / 100))
HJGrant
  • 11
  • 1
  • 1
    Convert your list to a string. Then just count substrings equal `HHHHHH` or `TTTTTT`. – Błotosmętek Mar 07 '20 at 18:58
  • Write one function to create the data, and one function to count the streaks. Write a test for the counter. This will let you post a dataset and say "It should say 7 but is actually said 2" – Kenny Ostrom Mar 07 '20 at 19:03
  • What do you want in case of 'HHHHHHHH' ? – Kenny Ostrom Mar 07 '20 at 19:20
  • 1
    Thing of what the first "if" block is doing - if the current value matches 5 next values, you consider that as one streak. But then the second "if" expects this counter to be 6, which means the current value and 5 previous values must have passed the first "if" - so altogether 5 previous values, current value, and 5 next value must all be the same. So you are actually looking for a streak of 11 instead of 6. – linus Mar 07 '20 at 19:22
  • 3
    This could benefit from some rubber ducky debugging. You're overthinking it, but not paying attention to the simple stuff. – Kenny Ostrom Mar 07 '20 at 19:23
  • @Błotosmętek: that will fail for sequences as long and longer than 2*max_streak. – Jongware Mar 07 '20 at 19:28

3 Answers3

2

what is wrong with the current implementation (generation of coin list)

In the code that generates your list, you create an empty list for every entry that is supposed to be in your list.

for i in range(100):
    coin_list=[]   # <-- here you create an empty list
    random_num=random.randint(0, 1)
    if random_num == 1:
        coin_list.append('H')
    elif random_num == 0:
        coin_list.append('T')

checking the length of your coin list:

len(coin_list)

output: 1

ideas to fix it

You can fix this by defining the list outside of the loop:

coin_list=[]
for i in range(100):

    random_num=random.randint(0, 1)
    if random_num == 1:
        coin_list.append('H')
    elif random_num == 0:
        coin_list.append('T')

Since you are using random anyway, you can look into random.choice:

coin_list = []
coins = ['H', 'T']
for i in range(100):
    coin_list.append(random.choice(coins))

In both of the corrected cases, the length of the list is 100.

what is wrong with streak counting

Next, streak_counter is not defined before it is referenced, So it should be defined:

streak_counter = 0

The loop you write to check for streaks will run into the following error:

IndexError: list index out of range

This is because you let x from 0 to the length of your list -1, but then use x+2,3,4,5 to access list items. When your loop reaches the 4th item from the back of the list, it will try to access an item outside of the list, causing the error.

for x in range(0, len(coin_list)-1):
    if coin_list[x] == coin_list[x+1] and coin_list[x] == coin_list[x+2] and \
    coin_list[x] == coin_list[x+3] and coin_list[x] == coin_list[x+4] and \
    coin_list[x] == coin_list[x+5]:  # < -- here you are accessing x+5
       streak_counter+=1
    else:
        continue

    if streak_counter ==6: 
        numberOfStreaks+=1
        streak_counter=0
    else: 
        continue

A fix for this would be to let x run from 0 to len(list)-5, this would allow the loop to complete. However, the way that you check for the streak is a bit peculiar:

for x in range(0, len(coin_list)-5):
    print(x)
    if coin_list[x] == coin_list[x+1] and coin_list[x] == coin_list[x+2] and \
    coin_list[x] == coin_list[x+3] and coin_list[x] == coin_list[x+4] and \
    coin_list[x] == coin_list[x+5]:
        streak_counter+=1
    else:
        continue

The if statement checks if there are six consecutive, identical entries in your list. If that is the case, you increment the streak_counter.

Next, you check if the streak_counter is 6, then you count this as a streak and increment numberOfStreaks.

if streak_counter ==6: 
    numberOfStreaks+=1
    streak_counter=0
else: 
    continue

The problem here is that for streak_counter to be 6, the if coin_list[x] == ... condition must have been fulfilled six times. So, you need at least 11 consecutive identical values to register a streak of six.

attempts to fix it

Now, how to solve this, I think depends on how you count streaks: Is a sequence of 7 heads one streak of seven, or two streaks of six?

For the former case, you could do something like this:

s = ''.join(coin_list) # convert everything to string

tails_streaks = []
heads_streaks = []

for tails in s.split('H'): # remove all heads and check if what remains is at least 6 long
    if len(tails) >=6:
        tails_streaks.append(len(tails))

for heads in s.split('T'): # ditto for tails
    if len(heads) >=6:
        heads_streaks.append(len(heads))

If you want to count all occurences of six consecutive identical results (which means counting one coin multiple times):

tails_sixes = []
heads_sixes = []

for tails in s.split('H'):
    if len(tails) >=6:
        tails_sixes += 1 + len(tails) - 6 

for heads in s.split('T'):
    if len(heads) >=6:
        heads_sixes += 1 + len(tails) - 6
warped
  • 8,947
  • 3
  • 22
  • 49
  • Thank you mate. This is a great help. I see why my question/program was confusing, I had already identified 6 identical letters in a row, and then wanted to count the times I counted the sequence, which doesn't make sence. Thanks for taking the time to point out all the points you found bro, I know us noobs are a pain sometimes haha. – HJGrant Mar 09 '20 at 15:06
  • @HJGrant You are very welcome. If my post answers your question, please consider accepting it. – warped Mar 09 '20 at 15:08
0

1) As written, you should increment your sequence count when streak_counter == 1 because you increment it by 1 when you detect a streak. You're probably getting 0 because the odds of a streak of 11 are much lower.

2) Like Błotosmętek said, it would be easier to just make a string (str1 = ''.join(coint_list)) then use the count method of strings (str1.count('HHHHHH')). If you want to count all 6-length runs (e.g. HHHHHHH = 2 runs), peruse the answers here.

Update: I did some testing and found that the regex method in the linked answer for all runs of 6 is much faster. Here's the test script I used

from random import randint as randi
import re
import timeit


# Create test list
longg = ['H' if randi(0,1) == 1 else 'T' for i in range(10000)]
longg = ''.join(longg)


def regVer(s):
    matches = re.finditer(r'(?=(H{6}|T{6}))',s)
    return len(list(matches))

def listVer(s):
    n = 6
    li = [s[i:i+n] for i in range(len(s)-n+1)]
    count = 0
    for i in li:
        if i == 'H'*6 or i == 'T'*6:
            count += 1
    return count

print('regCount: {}'.format(regVer(longg)))
print('Time elapsed: {}'.format(timeit.timeit('regVer(longg)', number=100, globals=globals())))
print('listCount: {}'.format(listVer(longg)))
print('Time elapsed: {}'.format(timeit.timeit('listVer(longg)', number=100, globals=globals())))

Which gave the following output:

regCount: 356
Time elapsed: 0.070616307
listCount: 356
Time elapsed: 0.281572281

Maybe I'm just being dumb about how to do the list comprehension though?

Katerina
  • 174
  • 7
0

Use zip to form the streak and count the ones that have only one value:

from random import choice
from itertools import islice

flipCount = 20
streakSize = 6

coinList    = [choice("HT") for _ in range(flipCount)]
allHeads    = tuple("H",)*streakSize
allTails    = tuple("T",)*streakSize
streakCount = sum( s==allHeads or s==allTails
                   for s in zip(*(islice(coinList,i) for i in range(streakSize))))

output:

print("".join(coinList))
print(streakCount)

# HHHHTTHHHHHHHTTHTHHH
# 2
Alain T.
  • 40,517
  • 4
  • 31
  • 51