6
\$\begingroup\$

The question is from Automate the Boring Stuff with Python and asks us to find the number of streaks of six heads or six tails that come up when a coin is tossed 100 times.

Coin Flip Streaks:

For this exercise, we’ll try doing an experiment. If you flip a coin 100 times and write down an “H” for each heads and “T” for each tails, you’ll create a list that looks like “T T T T H H H H T T.” If you ask a human to make up 100 random coin flips, you’ll probably end up with alternating head-tail results like “H T H T H H T H T T,” which looks random (to humans), but isn’t mathematically random. 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. Humans are predictably bad at being random.

Write a program to find out how often a streak of six heads or a streak of six tails comes up in a randomly generated list of heads and tails. Your program breaks up the experiment into two parts: the first part generates a list of randomly selected 'heads' and 'tails' values, and the second part checks if there is a streak in it. Put all of this code in a loop that repeats the experiment 10,000 times so we can find out what percentage of the coin flips contains a streak of six heads or tails in a row. As a hint, the function call random.randint(0, 1) will return a 0 value 50% of the time and a 1 value the other 50% of the time.

My Solution:

def streakFinder(inputList):
    import copy
    count=0
    delList=copy.copy(inputList)            # The list is copied to another reference so that it can be safely modified without affecting the original list
    try:                         # When the last element remains, there is no element left that can calculate the difference-->['H','H']
        while len(delList)!=0:
            i=0
            if delList[i]=='H':
                j=delList.index('T')
            else:
                j=delList.index('H')
            if j-i>=6:
                count+=1
            del delList[i:j]
    except:                     # The error generated is dealt here.
        if len(delList)>=6:
            count+=1
    return count


The inputList is the randomly generated list of H and T resulting from 100 coin tosses.

My solution gives me the correct answer. However, I would like to know if there is a better way to approach or solve this problem. If there are any errors, please point them out as I'm a beginner.

\$\endgroup\$
0

2 Answers 2

6
\$\begingroup\$

Python has an official style-guide, PEP8, which recommends using lower_case for both functions and variables. Since this is a programming challenge, you can probably not change the function name, but the variable names are fully under your control. PEP 8 also recommends surrounding = with spaces when used for assignment and the same for binary operators (like !=, but also augmented assignment like +=).

Imports should usually go into the global scope, at the top of your code. One exception are functions that are used only for testing and would pollute the global namespace unnecessarily, but this is not the case here.

In general, empty collections are falsey in Python, and non-empty collections are truthy. This means that instead of while len(delList)!=0 you can just do while delList.

You have a potential bug / the problem description is not explicit enough / their tests don't have enough coverage. You count all streaks of at least length six, the text reads such that only streaks of exactly length six count. But apparently their tests don't throw longer streaks at your code, or implement something different from the problem description as well.

Don't be afraid to use Python's standard library, it has many tools that can make your life a lot easier. In this case I would use itertools.groupby to group the tosses by their result. After this it becomes very easy to get the length of each streak and count all instances of that length being at least six using a generator expression with the built-in function sum:

from itertools import groupby

def streakFinder(tosses):
    return sum(1 for _, streak in groupby(tosses) if len(list(streak)) >= 6)

This should be significantly faster than your solution as well, because it only goes through the list exactly once. Your code has repeated calls to list.index, which starts iterating at the front every time.

Note that the random toss generation, which in the problem description is recommended to use random.randint and which you do not show, could be very short using random.choices:

import random

tosses = random.choices(["H", "T"], k=100)
\$\endgroup\$
5
\$\begingroup\$

List copying

Are you sure you need a deep copy? Probably you can get away with a shallow copy, in which case

del_list = list(input_list)

Type hints

def streakFinder(inputList):

can probably be

def streak_finder(input_list: Iterable[int]) -> int

Note that even if you always pass in a list, you don't strictly need to pass in a list; you can pass in any iterable. So indicate the broadest useful type to your callers.

Also note the use of snake_case in those names.

Too-broad except

(Nearly) never just except. If you have an infinite loop, for instance, this will prevent Ctrl+C break from working. Instead, catch the specific exception type that you're anticipating.

\$\endgroup\$
9
  • 2
    \$\begingroup\$ except ValueError: for example \$\endgroup\$
    – Reinderien
    Mar 21, 2020 at 13:54
  • 1
    \$\begingroup\$ del_list = input_list[:] is a shorter way to copy the list. \$\endgroup\$
    – AJNeufeld
    Mar 21, 2020 at 14:51
  • 2
    \$\begingroup\$ It's shorter but it always strikes me as less legible and more of a hack. You have to understand implicit array slicing to understand why that works. \$\endgroup\$
    – Reinderien
    Mar 21, 2020 at 14:55
  • 1
    \$\begingroup\$ Nope - that actually does copy the list. \$\endgroup\$
    – Reinderien
    Mar 22, 2020 at 15:19
  • 2
    \$\begingroup\$ The former is a direct assignment. The latter is a slice, and slices always copy to a new list. \$\endgroup\$
    – Reinderien
    Mar 22, 2020 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.