Coin Flip Streak da Automate the Boring Stuff with Python

Nov 18 2020

Mi scuso in anticipo se la domanda è stata ripetuta tante volte. Questa è un'attività pratica da Automate the Boring Stuff with Python. In breve, l'attività comporta la scrittura di un codice che esegue un esperimento per verificare se c'è una serie di 6 "teste" o "croce" in 100 lanci di monete, quindi lo replica 10.000 volte e fornisce una percentuale del tasso di successo.

Quando ho scritto il codice, ho cercato di essere diverso rendendo il codice applicabile a qualsiasi serie in una serie di esperimenti predeterminati (nel mio caso, il campione era di 1 milione di lanci di monete). Ho anche cercato di trovare la serie più lunga possibile in quel suddetto esperimento.

Voglio anche scusarmi in anticipo per il fatto che i commenti erano terribilmente prolissi.

import random, copy, time

def torai(seq,pop): # seq is for #=streak, pop is for total sample/population/experiment
    # Creating a random chance of heads and tails
    tosses = []
    for i in range(pop):
        tosses.append(random.randint(1,2)) # 1 and 2 for head and tail, and vice versa

    # Defining initial values for the main loop
    streak = 0 # Iterated streak
    curlongstr = 0 # Current longest streak
    longeststr = 0 # Longest streak evaluated
    peak = [] # Record local streaks from 'tosses' list

    # The main loop
    for i in range(len(tosses)): # Looping based on list indexes
        if i == 0:  # Conditional for preventing tosses[0] == tosses[-1]
            continue

        elif tosses[i] == tosses[i-1]: # Conditional for checking if an i element has the same value as the previous element value, i-1
            streak += 1 # Adding tally mark if the line above is fulfilled
            if i == len(tosses)-1: # A nested conditional for adding the last tally mark from 'tosses' into the overall list of steaks 'peak', see lines 27-33
                peak.append(streak)

        elif tosses[i] != tosses[i-1]: # Conditional for checking if an i element value is different than the previous element value, i-1
            curlongstr = copy.copy(streak) # Creating a variable by returning a copy of streak before it resets to 0, see line 31
            if curlongstr > longeststr: # A nested conditional for comparing the current longest streak and the longest streak that has happened when looping the 'tosses' list
                longeststr = curlongstr
            streak = 0 # This is where streaks ended and then resets to 0, so before that, the value of the streak is copied first, see line 28
            if curlongstr > streak: # After streak is reset to 0, the value of current long streak is compared to 0, so that we create a list of streaks from 'tosses' list
                peak.append(curlongstr)

    truepeak = []
    for i in peak: # Example: a 2-streak is equal to either [1,1,1] or [2,2,2], a 4-streak is either [1,1,1,1,1] or [2,2,2,2,2]
        truepeak.append(i+1)

    apr = []
    # Loop for finding how many #-streaks happened
    for i in truepeak:
        if i == seq:
            apr.append(i)

    print('%s-streak count: ' %seq, len(apr)) # Total of #-streaks happened in 'tosses' list
    print('%s-streak prob (percent): ' %seq, (len(apr)/pop)*100) # Calculating probability if how many #-streak happened in given n times tosses
    print('longest streak: ',longeststr + 1) # Similar reason as line 36
    print('process time: ',time.process_time(), 'second\n')

    return (len(apr)/pop)*100

x = torai(2,1000000)
y = torai(6,1000000)
z = torai(10,1000000)
print(x, y, z)

Ho provato ad aumentare il campione a 10 milioni di lanci di monete. Tuttavia, il programma verrà eseguito 9-10 più lentamente ogni volta che la funzione è stata chiamata.

La mia richiesta è: chiunque può verificare se il risultato (probabilità di n-serie) è corretto o meno e ci sono modi per ridurre il codice e il tempo di elaborazione?

Risposte

6 AryanParekh Nov 18 2020 at 17:04

Bug

torai(1, 10000)

Questo dovrebbe stampare qualcosa in giro 50 %, poiché è il conteggio individuale. Ma invece, stampa

1-streak count:  0
1-streak prob (percent):  0.0
longest streak:  19
process time:  0.046875 second

Evita troppi commenti

Ci sono troppi commenti nel codice, il che rende il codice inutilmente contorto. Quello che raccomando è l'uso di docstrings . IMO Non è molto importante qui, ma è meglio di un milione di commenti

def torai(seq,pop): 
    tosses = []
    for i in range(pop):
        tosses.append(random.randint(1,2))
    streak = 0
    curlongstr = 0
    longeststr = 0
    peak = []
    for i in range(len(tosses)): 
        if i == 0:  
            continue
        elif tosses[i] == tosses[i-1]: 
            streak += 1 
            if i == len(tosses)-1: 
                peak.append(streak)

        elif tosses[i] != tosses[i-1]: 
            curlongstr = copy.copy(streak) 
            if curlongstr > longeststr: 
                longeststr = curlongstr
            streak = 0 
            if curlongstr > streak: 
                peak.append(curlongstr)

    truepeak = []
    for i in peak: 
        truepeak.append(i+1)

    apr = []
    

    for i in truepeak:
        if i == seq:
            apr.append(i)

    print('%s-streak count: ' %seq, len(apr)) 
    print('%s-streak prob (percent): ' %seq, (len(apr)/pop)*100) 
    print('longest streak: ',longeststr + 1) 
    print('process time: ',time.process_time(), 'second\n')

    return (len(apr)/pop)*100

Semplifica # 1

    for i in range(len(tosses)): 
        if i == 0:  
            continue

Per me è chiaro che vuoi saltare il primo elemento. In tal caso, è possibile specificare il punto di partenza perrange()

    for i in range(1, len(tosses)): 

Semplifica # 2

    for i in range(pop):
        tosses.append(random.randint(1,2))

Poiché questa sarà una sequenza immutabile, usa una tupla , con un generatore

tosses = tuple(random.randint(1, 2) for _ in range(pop)

Semplifica # 3

            if curlongstr > longeststr:
                longeststr = curlongstr

La tua condizione è semplice. Il nuovo valore è sempre il più grande dei due
Usa la max()funzione

            longeststr = max(longeststr, curlongstr)

Semplifica # 4

truepeak = []
    for i in peak:
        truepeak.append(i+1)

Stai creando un elenco completamente nuovo e riempilo con gli stessi identici elementi peaktranne che con l' 1aggiunta di una costante . Molto inefficiente. O aggiungi i valori con +1dall'inizio o usa +1dove necessario.

    for i in peak:
        if i + 1 == seq:
            apr.append(i + 1)

Ma di nuovo, tutto ciò che devi fare aprè ottenere la sua lunghezza, quindi non ha assolutamente senso mantenere così tante liste quando tutto ciò che devi fare è tenere un contatore. Ciò elimina anche la necessità di mantenerepeak


Calcola i lanci mentre procedi

Dopo aver rimosso tutti i loop precedenti, ne rimarranno ancora 2. Uno per calcolare i lanci e l'altro li esamina per calcolarli. Quello che ti propongo è di ripeterlo solo una volta e di tenere traccia di due cose. Il lancio corrente e il lancio precedente

def torai(seq, iterations ):
    total_streaks = 0

    previous_flip = random.randint(1, 2)
    for _ in range(1, iterations):
        current_flip = random.randint(1, 2)

        if current_flip == previous_flip:
            total_streaks += 1

        # other calculations

        current_flip = previous_flip

    print(f"Total streaks: {total_streaks}")
yfr Nov 19 2020 at 17:58

Apparentemente, la mia comprensione della lettura e matematica sono alla pari con quelle di un bambino. Quindi voglio rettificare il codice sopra perché, come ho appena imparato, trovare un # -streak un milione di lanci è diverso da cento lanci replicati poi mille volte (applicando anche l'input dalla risposta di Aryan).

Il codice seguente calcolerà solo la probabilità di almeno una coppia # da un numero di lanci.

import random, copy, time, sys

def oddstreak(strk,totoss,sample):
    '''
    Return the probability of AT LEAST #-streak in a number of tosses
    and a predetermined sample value.

        Parameter:
            strk (int)   : streak value
            totoss (int) : number of tosses
            sample (int) : number of repetition

        Return:
            probability (float) : probability of #-streak(s)

    For sanity checking, just uncomment every print list variable and set
    'totoss' and 'sample' to a smaller value, i.e., 50 and 3.
    '''

    if int(strk) == 1:
        sys.exit('2-steak is a minimum value. Enter value greater than 1')

    streakchecker = list()
    for i in range(sample):
        tosses = tuple(random.randint(1, 2) for _ in range(totoss))
        #print(tosses)

        localstreak = 0
        streak = 0
        sancheck = list()
        for i in range(1,len(tosses)):
            if tosses[i] == tosses[i-1]:
                streak += 1
                if i == len(tosses)-1:
                    sancheck.append(streak)

            elif tosses[i] != tosses[i-1]:
                localstreak = copy.copy(streak)
                streak = 0
                if localstreak > streak:
                    sancheck.append(localstreak)
        #print('sancheck: ', sancheck)

        for n in sancheck:
            if n != (int(strk) - 1):
                continue
            elif n == (int(strk) - 1):
                streakchecker.append(1)
                break
        #print(streakchecker)

    probability = sum(streakchecker)/sample*100
    print('Percentage of appeareance of AT LEAST a %s-streak (H or T): %g percent' % (strk, probability))
    print('Process time: ',time.process_time(), 'second\n')

    return probability

oddstreak(6,100,100000)

Il risultato è:

Percentage of the appearance of AT LEAST a 6-streak (H or T) in a number of coin tosses: 54.542 percent
Process time:  11.0 second

Qualsiasi input per questo nuovo codice è molto apprezzato. Grazie :)