1
\$\begingroup\$

I am attempting to complete the coin flip streaks problem from automate the boring stuff with python. My code works fine but my only concern is the phrasing of the task.

Does the question want us to find the number of samples (100 flips) that contain at least one streak and divide that by the total number of samples (10,000)

Or are we supposed to find the total number of streaks (6H or 6T) in all samples and divide that by 10,000?

My solution was for the first option but I make no distinction between one streak and several.

My code and the question are listed below.

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.

import random
import re

totalRuns = 0            
streakScore = 0

def coinFips():
    global streakScore,totalRuns
    # Return results into a single string for regular expressions
    results = ''
    for i in range(100):
      value = random.randint(0, 1)
      if value == 0:
        results += "H"
      elif value == 1:
        results += "T"
    # Use regular expressions to check results sting
    match = re.findall(r'(HHHHHH|TTTTTT)', results)
    if len(match) > 0:
      streakScore = streakScore +  1
    totalRuns = totalRuns + 1
    
for i in range(10000):
  coinFips()
 
print("The Chance of a streak is: {}%".format(round(streakScore/totalRuns * 100)))
\$\endgroup\$
4
  • \$\begingroup\$ What results do you get? Should be around 80.7% \$\endgroup\$ Nov 28, 2020 at 2:25
  • \$\begingroup\$ Around 80%, which according to other post I read that is correct. \$\endgroup\$
    – JerryTn
    Nov 28, 2020 at 2:27
  • \$\begingroup\$ for programming challenge type questions, please include the link to original as well :) \$\endgroup\$
    – hjpotter92
    Nov 28, 2020 at 2:52
  • \$\begingroup\$ Your reading seems correct. In the context of the question it doesn't matter how many streaks the string has. It only matters that a streak is there. \$\endgroup\$
    – vnp
    Nov 28, 2020 at 19:46

1 Answer 1

1
\$\begingroup\$

The assignment

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.

Now this requirement you didn't match, so your program doesn't contain the required functionality, even if it does match the required result.

Note that there is a bit of a problem with the assignment, as you might see a streak of 7 as two streaks of 6, or a streak of 12 as two streaks of 6. I presume, given the assignment, that you may use any streak of 6 or more. This is not what your program currently does, by the way; I presume from the code that a streak of 12 will be seen as two streaks of 6.

[EDIT] Rereading the question, it seems that you do match the assignment, if you assume that the "often" part depends on how often the streak of 6 comes up in the randomly generated distributions, instead of in one distribution. That seems to be the case if I take the rest of the context in, but the assignment isn't stated very clearly.

our 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.

You fail to meet this requirement: you haven't specified two methods, which is generally the way you would split things up in programming.

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.

So if you'd create the method that counts the streaks, and the streak count is higher than 0 then you would increase the counter. You do that perfectly :)

As indicated, you haven't separated it from the main functionality. Worse, you update global variables from a function.

If this course is about structured programming then you might fail even though your application is likely to generate the right answer.

Code review

totalRuns = 0            
streakScore = 0

This is fine, but in that case you need to update them from global code. If not the variables should be local to a function.

def coinFips():

This method name is misspelled. Does the method really only perform coin flips? If so, shouldn't there be a parameter that indicates how many? And return an array of results?

global streakScore,totalRuns

Every time you type global, you should wonder if it really helps design / readability, and refrain from using globals if it does not. I would add a space before totalRuns, but that's kind of personal.

results = ''

Why not use a (character) array instead? That way you can assign it the right size from the start and then fill it by indexing the coin flip...

for i in range(100):

100 is a literal, a so called "magic value". Here you should use a parameter (or, in other cases, a constant).

# Use regular expressions to check results sting

Another spelling mistake. Spelling mistakes are not terrible, but if you make too many they act as red flags to a reviewer. They are more likely to take a deeper look.

match = re.findall(r'(HHHHHH|TTTTTT)', results)

It will probably not be terribly performant, but it is a nice way to test and easy to program. But note the problem indicated in the first section of the review.

Instead you probably want to use something like ([H]{6,}|[T]{6,}). Also, you'll find that regular expressions are hard to parameterize (what if you want to use 7 or any other number instead of 6?). Some kind of state machine would impress me (even) more.

print("The Chance of a streak is: {}%".format(round(streakScore/totalRuns * 100)))

Here too much is happening for me. The calculation should be on it's own line. If your function ends with ))) then your application is getting too much LISP like if you ask me. You can also see that it is getting too cramped because you suddenly don't use spacing around the operator / anymore.


The other lines seem fine to me. There isn't too much happening on them except for the last line - which is good. And the variable naming seems fine too. Spacing and readability are fine. The application code is concise too.

Conclusion: you are well on your way, but the part where you "divide-and-conquer" is lacking. This is not much of a problem in the above code of course, but it may become a problem for larger, more complex systems.

\$\endgroup\$
1
  • \$\begingroup\$ Or summed up, more methods, more constants, more parameters, fewer globals. \$\endgroup\$ Dec 7, 2020 at 2:44

Your Answer

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

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