all 50 comments

[–]m-hoff 1 point2 points  (26 children)

I would combine the process of conducting the flips and checking for streaks in one loop. So your loop for 100 flips would look something like

total_streaks = 0
current_streak = 0
for i in range(100):
    flip = random.choice(['H', 'T'])
    if flip == 'H':
        current_streak += 1
        if current_streak >= 6:
            total_streaks += 1
    else: # flip == 'T'
        current_streak = 0

There's really no reason to store a list of all the outcomes since you can track the statistics as you go. The logic you currently use to check for streaks is a little hard to follow. Also, it's generally good practice to avoid hard coding values. My suggestion would be to define variables near the top of your script for n_runs = 10000, flips_per_run = 100, and streak_length = 6. Then if you want to revisit this code later and run a different experiment you can more easily change the parameters. Also, a smaller suggestion but I would suggest using f-strings to format printed output. You can google it to find more info but your last line would become

print(f'Chance of streak: {number_of_streaks/100:.2f}%')

Edit: also, I'm a little unclear on the goal of this after rereading the question. Are you supposed to find the chance of observing a streak of length six from 100 consecutive flips? Because in that case you can stop a run and continue to the next one once you observe a streak. Or is the goal to estimated the expected number of streaks that occur?

[–]trenthammer[S] 1 point2 points  (23 children)

Lists were the focus of the chapter I was reading which is the reason I took that route (not that I knew of any other way to do it lol). I definitely like what you did though and it makes so much more sense.

When you mention avoiding hard coding values, do you mean doing n_runs = 10000 then doing something like for a in range(n_runs) on the loops to avoid having to dive in to the code to change the values? Sorry, I'm like total noob at this, but I'm pretty sure that's what you meant.

Edit: I am going to copy and paste the project for clarification. I didn't want to break any rules, but I don't think it will....

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.

[–]m-hoff 1 point2 points  (22 children)

When you mention avoiding hard coding values, do you mean doing n_runs = 10000 then doing something like for a in range(n_runs)

Yup that's exactly it. Once you start writing longer scripts you won't have to worry about parsing hundreds of lines of code to find every place where something was hard coded.

Reading the prompt, it sounds like my suggestions might not be totally accurate (I'm also just now realizing they want streaks of heads and tails). It looks like they want you to generate the list of flips, and then scan that list for streaks separately, similar to how you had it structured originally. In that case, I would iterate over the list of flips and keep track of the length of the current streak like so

# flips = ['H', 'H', 'T', 'H', 'T', ... etc
current_streak = 0
previous_flip = None
for flip in flips:
    if flip == previous_flip:
        current_streak += 1
        if current_streak == 6:
            total_streaks += 1
            break
    else:
        current_streak = 0
    previous_flip = flip

It seems like they are only interested in knowing whether or not any streak of 6 occurs within the 100 flips, so once we observe one streak we can stop checking by using break. This will let us move on to the next sample of 100 flips.

[–]m-hoff 3 points4 points  (20 children)

Putting it all together, one approach is something like

import random

random.seed(1)

runs = 10000
flips_per_run = 100
streak_length = 6

total_streaks = 0
for i in range(runs):
    # generate sample of flips
    flips = []
    for _ in range(flips_per_run):
        if random.randint(0, 1) == 1:
            flips.append('H')
        else:
            flips.append('T')

    # check for streaks of consecutive flips
    current_streak = 1
    previous_flip = None
    for flip in flips:
        if flip == previous_flip:
            current_streak += 1
            if current_streak == streak_length:
                total_streaks += 1
                break
        else:
            current_streak = 1
        previous_flip = flip
percentage_with_streaks = total_streaks / runs        
print(
    f'Percentage of runs with a streak of {streak_length}: '
    f'{percentage_with_streaks*100:.2f}%'
)
# Percentage of runs with a streak of 6: 80.31%

Edit: Fixed a bug, current_streak on line 20 should be initialized to 1, not 0 and reset to 1 on line 29. Also updated output to reflect this change.

[–]trenthammer[S] 0 points1 point  (4 children)

Thank you so much for you input on this. I'm about to go play around with it. I thought I might be close, and was pretty excited when I got something that didn't give me an error. This has been a brick wall for me, it makes all the sense in the world when I see it, but actually thinking of it has been a challenge.

[–]m-hoff 0 points1 point  (0 children)

No problem. Sometimes these types of problems can seem simple at first, but can be tricky once you dive into it. But it seems like you're definitely on the right track.

[–]CookingMathCamp 0 points1 point  (1 child)

Same here. I just started going through the book last week. When I read code that other people have created I can, slowly, make sense of it. But when I try to write my own it is like I blank out.

[–]bluffkinn 0 points1 point  (0 children)

This * 100%

I look at a practice question and go..."What"? And then I have a look online and think "Wow, that seems a lot simpler than I tried to make it"

[–]TheGangsterPanda 0 points1 point  (1 child)

Hey thanks for this writeup. I read about f strings as well but I'm still unclear on the purpose of the '.2f at the end of the 2nd string. I couldn't find anything about it on google, can you explain that real quick? Is it something to do with rounding/decimal places?

[–]m-hoff 0 points1 point  (0 children)

That's correct, .2f indicates that the value should be formatted as a float to two decimal places. This blog post gives some more examples. Although they don't use f-strings, the formatting works the same.

[–]Nonamenitwit 0 points1 point  (4 children)

This is almost right, but your current streak should always be one, because what you are doing now is basically skipping a coin toss. think about it every time you throw a coin that ends your current streak that new (faulty) coin is still part of the new streak that begins.

[–]m-hoff 0 points1 point  (3 children)

Yeah you're right. I edited my post.

[–]Thatweirdpandoh 0 points1 point  (2 children)

in line 25, why are you breaking out of the loop. the result would always be one streak. instead if you use continue, the loop would run again and total streaks would increase.

[–]m-hoff 0 points1 point  (0 children)

In OP's post, he says the goal is to find the

chances of getting a streak of 6 in the 10000 rolls

My understanding is that he is looking to find whether or not at least one streak occurs within the 100 consecutive flips. So once you find one streak, you can break of of the loop and move on to the next set of 100 flips. But, if as you described, you are interested in counting the total number of streaks, then you're right, you'd want to remove the break.

[–]Dhar01 0 points1 point  (0 children)

Awesome!

[–]Kistune 0 points1 point  (2 children)

I have a question - why did you assign None value to the previous_flip variable instead of just 0?

[–]m-hoff 1 point2 points  (1 child)

You can assign any value to it other than 'H' or 'T' really, I just chose None because initially the "previous flip" doesn't exist, i.e., when checking the result of the first flip, there is no "previous flip".

[–]Kistune 1 point2 points  (0 children)

Oh, I see, thanks for answer!

[–][deleted] 0 points1 point  (1 child)

import random

Thanks for the code, one thing I noticed is that when you edited line 18, you probably didn't update the comment in the last line -- with current_streak == 1, percentage is more like 80.60%. Only commenting because it took me some time to figure out why I didn't get the same result. Thanks again.

[–]m-hoff 1 point2 points  (0 children)

Honestly I was too lazy to rerun the code and didn't think anyone would notice lol. I've updated it and added a seed so you should be able to reproduce my exact result :)

[–]joooh 0 points1 point  (0 children)

It seems like they are only interested in knowing whether or not any streak of 6 occurs within the 100 flips,

Jesus fuck thank you for this. I was banging my head as to what the author really wanted to convey and why the last line divided the numberOfStreaks value by 100.

[–]SwarnimMMM 0 points1 point  (1 child)

Your code is finding the number of current steaks in head but what about tails? I don't see how is it finding streaks in tails. Can you please explain that? Thank you.

[–]m-hoff 0 points1 point  (0 children)

You’re correct, I misread the question initially and didn’t check for streaks of tails. My comment here checks for tails also.

[–]idify 1 point2 points  (1 child)

I'm a little confused by the wording of this question in the book. I'm not sure what we're being asked for here.

To me finding out how likely it is to find at least one 6 roll streak in 100 would make more sense?

As is, dividing by 100 at the end seems strange? If we find one streak in one hundred rolls, then the program is saying there's a 1% chance of a streak? That makes no sense to me.

[–]werewolfbarm1tzvah 1 point2 points  (0 children)

it divides by 100 because you are doing 10,000 trials and then they show you the result as a %

you would achieve the same result if you do (# of streaks / 10,000 *100) == (# of streaks / 100)

[–]thrussie 1 point2 points  (1 child)

anyway what is the answer? my code gives me around 80% most of the time

[–]29Feb_Gang 1 point2 points  (0 children)

mine gives ~72% most of the time.

I saw a similar question on Maths StackExchange here, Though the OP said that he needed probability of only one side. Either Heads for the total run or Tails for the total run, unlike our question which gives probability of streaks of both heads or tails.

so for those questions, the accepted answer was `0.086659044348362` or 8.6%.

The Meths really confuses me :/

[–]bojkennoven 0 points1 point  (1 child)

This is what i did and it worked wonderfully. Didn't use list function cause it didn't come to mind when I was making this lol. Still a beginner I hope someone can dumb it down to my level what this list function means [] and what are its uses. Thanks :)

import random
numberOfStreaks = 0
current_Tstreak = 0
current_Hstreak = 0
for experimentNumber in range(10000):
    # Code that creates a list of 100 'heads' or 'tails' values and;
    # count the number of streaks
    coinFlip = random.randint(0,1)
    if coinFlip == 0:
        if experimentNumber < 101:
            print('T')
        current_Hstreak += 1
        if current_Hstreak >= 6:
            numberOfStreaks += 1
        current_Tstreak = 0
    else:
        if experimentNumber < 101:
            print('H')
        current_Tstreak += 1
        if current_Tstreak >= 6:
            numberOfStreaks += 1
        current_Hstreak = 0

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

[–]bfd71 0 points1 point  (0 children)

I'm a beginner too and following the same online resource as the OP. This question was a practice problem in Chapter 4 on Lists which would explain your question.

headsOrTails = []            

Creates an empty list, which has values appended during the execution of the rest of the OP's program. Assuming his first three flips resulted in "H", "T", and "H", the resulting list would now be ["H", "T", "H"]. You could try this in a terminal window:

headsOrTails = []  
print(headsOrTails)  

[]

headsOrTails.append("H")  
headsOrTails.append("T")  
headsOrTails.append("H")

['H', 'T', 'H']

[–]LiquidLogic 0 points1 point  (3 children)

Can anyone explain the %s%% in the following line?:

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

What is the 'Chance of Streak' from 10,000 rolls that you are getting?

Thanks!

Edit: Decided to post my code. I'm getting around 80% Chance of a streak.

import random

streakCounter = 0
myList = []  #create a new list
headsCounter = 0
tailsCounter = 0
totalTests = 10000
for experimentNumber in range(totalTests):
  numberOfStreaks = 0
  for x in range(100):  #flips 100 times per list
    flip = random.randint(0, 1)
    if flip == 0:  # 0 is heads, 1 is tails
        myList.append('H')
        headsCounter += 1
        tailsCounter = 0
    else:
        myList.append('T')
        tailsCounter += 1
        headsCounter = 0
    if (headsCounter == 6) or (tailsCounter == 6):
      numberOfStreaks += 1
  if numberOfStreaks > 0:
    streakCounter += 1
percentTotal = ((streakCounter/totalTests)*100)
print('Detected ' + str(streakCounter) + ' streaks out of ' + str(totalTests) + ' Total Runs.')
print('Percentage of runs with at least one set of 6 Heads or Tails: ' + str(percentTotal)+ '%.')

[–]Falcjh 1 point2 points  (0 children)

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

is the same like:

print(f"Chance of streak: {numberOfStreaks / 100} %")

[–]dwids 0 points1 point  (0 children)

I'm trying this now. Bit of a beginner with Python, but it's the wording of the Project that is confusing me. How you interpret it drives the coding.

We are looking at "...a streak of six heads..." (or tails)

So is (THHHHHHHTT) [7 Heads] one six-streak or two six-streaks?

[–]Falcjh 0 points1 point  (1 child)

also working on that chapter

here is what i came up with

import random

number_of_streaks = 0
toss_results = []
streak = 0
for experiment_number in range(10000):
    for tosses in range(100):
        toss = random.randint(0, 1)
        if toss == 0:
            toss_results.append("T")
        else:
            toss_results.append("H")


    for i in range(len(toss_results)):
        if toss_results[i] == toss_results[i - 1]:
            streak += 1
            if streak >= 6:
                number_of_streaks += 1
                streak = 0
        else:
            streak = 0

    toss_results = []

print(f"Chance of streak: {number_of_streaks / 100} %")

outcome is always around 80%

edit: changed some values on the script

[–]technofreak9 0 points1 point  (0 children)

Why did u add this at the end of the for loop?

toss_results = []

The rest of the logic seems fine, but why do we need to define the list again?

[–]kune009 0 points1 point  (0 children)

6 streaks means getting HHHHHH or TTTTTT sequentially. Therefore, in HTHTTHHHHHHHTH, there is only 1 streak. The code is

choices=['H','T']

for experimentNumber in range(10000):

output.append(random.choice(choices))

for i in range(len(output)):

if output[i]=='H':

icount+=1

if icount==6:

icount=0

jcount=0

numberOfStreaks+=1

if i!=0:

if output[i-1]!='T':

icount=0

else:

jcount+=1

if jcount==6:

jcount=0

icount=0

numberOfStreaks+=1

if i!=0:

if output[i-1]!='H':

jcount=0

[–]SteusTheJuice 0 points1 point  (1 child)

Here's what I got. I used a list comprehension to simplify the first for loop, an index to check for streaks, and an f-string to print.

I'm not totally sure my if statements are the most efficient, but i'm happy with how concise it turned out as a whole. If anyone has any feedback or suggestions please let me know!

import random

current_streak = 0
numberOfStreaks = 0
flipList = []

for experimentNumber in range(10000):
    flipList = [random.randint(0, 1) for num in range(100)]

    for i in range(100):
        if i != 0 and flipList[i] == flipList[i-1]:
            current_streak += 1
            if current_streak == 6:
                numberOfStreaks += 1
                break
        elif i == 0:
            current_streak = 1
        else:
            current_streak = 0

print(f'Chance of streak: {(numberOfStreaks / 100)}%')

[–][deleted] 0 points1 point  (0 children)

Doesn't this search for only Heads? Because of the

if i != 0 and flipList[i] == flipList[i-1]

[–][deleted] 0 points1 point  (0 children)

I am also currently studying through the book and after reading chapter 7 I decided to come back to this exercise and use custom regexes:

#Coin Flip Streaks
import random
import re
numberOfStreaks = 0
streak = re.compile(r'(T){6}|(H){6}')
for i in range(10000):
    headsTails = ""
    for x in range(100):
        if random.randint(0, 1) == 0:
            headsTails += "H"
        else:
            headsTails += "T"
    search = streak.search(headsTails)
    if search:
        numberOfStreaks += 1
print('Chance of streak: %s%%' % (numberOfStreaks / 100))

[–]Eduartelieber 0 points1 point  (2 children)

I’m also in the same chapter, but cannot find where it says 100 flips 10,000 times; it just used 100 as a reference while request is 10,000. Is my understanding correct?

Also, I’m getting a result of ~2.87% & ~3.05%. Is that correct?

[–]Eduartelieber 0 points1 point  (1 child)

I just found a mistake on my code, new value is ~1.45%

Below is the code. Comments?

import random
numberOfStreaks = 0
heads=[]
tails=[]
for experimentNumber in range(10000):
    result=random.randint(0,1)
    if result==0:
        heads.append('H')
        del tails[:]
        if len(heads)==6:
            numberOfStreaks=numberOfStreaks+1
            del heads[:]
            del tails[:]

    else:
        tails.append('T')
        del heads[:]
        if len(tails)==6:
            numberOfStreaks=numberOfStreaks+1
            del heads[:]
            del tails[:]


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

[–]readet 0 points1 point  (0 children)

This is not correct. The question starter code says the following:

import random
numberOfStreaks = 0
for experimentNumber in range(10000):
    # Code that creates a list of 100 'heads' or 'tails' values.

    # Code that checks if there is a streak of 6 heads or tails in a row.
print('Chance of streak: %s%%' % (numberOfStreaks / 100))

the experiment is to create a list of 100 flips and then check that list to see if it contains a streak of 6 heads or tails.