Coin Flip Streak da Automate the Boring Stuff with Python
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
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}")
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 :)