Coin Flip Streak de Automate the Boring Stuff con Python
Pido disculpas de antemano si la pregunta se ha repetido tantas veces. Esta es una tarea de práctica de Automate the Boring Stuff with Python. En resumen, la tarea consiste en escribir un código que lleva a cabo un experimento de comprobar si hay una racha de 6 'caras' o 'cruces' en 100 lanzamientos de monedas, luego la replica 10.000 veces y da un porcentaje de la tasa de éxito.
Cuando escribí el código, traté de ser diferente haciendo que el código fuera aplicable a cualquier racha en una serie de experimentos predeterminados (en mi caso, la muestra fue 1 millón de lanzamientos de moneda). También traté de encontrar la racha más larga posible en dicho experimento.
También quiero disculparme de antemano porque los comentarios fueron terriblemente prolijos.
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)
Intenté aumentar la muestra a 10 millones de lanzamientos de monedas. Sin embargo, el programa se ejecutará de 9 a 10 más lento cada vez que se llame a la función.
Mi solicitud es: ¿alguien puede verificar si el resultado (probabilidad de n-racha) es correcto o no y hay alguna forma de acortar el código y el tiempo de proceso?
Respuestas
Loco
torai(1, 10000)
Esto debería imprimir algo 50 %, ya que es el recuento individual. Pero en cambio, imprime
1-streak count: 0
1-streak prob (percent): 0.0
longest streak: 19
process time: 0.046875 second
Evite demasiados comentarios
Hay demasiados comentarios en su código, lo que hace que el código parezca innecesariamente complicado. Lo que recomiendo es el uso de docstrings . En mi opinión, no es muy importante aquí, pero es mejor que un millón de comentarios.
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
Simplifica # 1
for i in range(len(tosses)):
if i == 0:
continue
Para mí, está claro que desea omitir el primer elemento. En ese caso, puede especificar el punto de partida pararange()
for i in range(1, len(tosses)):
Simplifica # 2
for i in range(pop):
tosses.append(random.randint(1,2))
Dado que esta va a ser una secuencia inmutable, use una tupla , con un generador
tosses = tuple(random.randint(1, 2) for _ in range(pop)
Simplifica # 3
if curlongstr > longeststr:
longeststr = curlongstr
Tu condición es simple. El nuevo valor es siempre el mayor de los dos
Solo usa la max()función
longeststr = max(longeststr, curlongstr)
Simplifica # 4
truepeak = []
for i in peak:
truepeak.append(i+1)
Está creando una lista completamente nueva y la completa con exactamente los mismos elementos que, peakexcepto con una constante 1agregada. Muy ineficaz. Agregue los valores con el +1del principio o use el +1cuando sea necesario.
for i in peak:
if i + 1 == seq:
apr.append(i + 1)
Pero de nuevo, todo lo que hace apres obtener su longitud, por lo que no tiene ningún sentido mantener tantas listas cuando todo lo que tiene que hacer es mantener un contador. Eso también elimina la necesidad de mantenerpeak
Calcule los lanzamientos sobre la marcha
Después de eliminar todos los bucles anteriores, todavía quedarán 2. Uno para calcular los lanzamientos y el otro los repasa para calcularlos. Lo que propongo es revisarlo solo una vez y hacer un seguimiento de dos cosas. El giro actual y el giro anterior
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}")
Aparentemente, mi comprensión de lectura y matemáticas están a la par con las de un niño pequeño. Así que quiero rectificar el código anterior porque, como acabo de aprender, encontrar una racha # de un millón de lanzamientos es diferente a cien lanzamientos replicados y luego miles de veces (también aplicando la entrada de la respuesta de Aryan).
El siguiente código solo calculará la probabilidad de al menos un par # de varios lanzamientos.
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)
El resultado es:
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
Cualquier entrada para este nuevo código es muy apreciada. Gracias :)