HHH, HHT y el comando «yield» de Python

Variable aleatoria X: tiramos una moneda al aire sucesivamente y contamos el número de veces que lo hacemos hasta obtener el patrón HHH (tres caras) en las tres últimas tiradas.

Variable aleatoria Y: lo mismo, pero hasta que salga el patrón HHT.

Entonces las medias de X e Y son iguales, ¿verdad? Pues no. (¿Alguien sabría decirme cuál de las combinaciones, HHH o HHT, tiende, en promedio, a aparecer antes? Pueden darse explicaciones muy complejas, pero existe una muy simple e intuitiva).

Este pequeño (¿y sorprendente?) ejercicio probabilístico me ha servido de excusa para practicar con el comando yield en Python, del que vine a tener noticia recientemente.

El código es:

from random import choice

def cadena():
        a, b, c = (None, None, None) 
        while(True):
                a, b, c = b, c, choice("HT")
                yield a,b,c

niter = 100000
HHH = [0] * niter
HHT = [0] * niter

for i in range(niter):
        cont = 1
        gen = cadena()

        while( True ):
                seq = gen.next()
                if seq == ('H', 'H', 'H') and HHH[i] == 0:
                        HHH[i] = cont

                if seq == ('H', 'H', 'T') and HHT[i] == 0:
                        HHT[i] = cont

                cont += 1

                if( HHH[i] * HHT[i] > 0 ):
                        break

for x in HHH:
        print x;
for x in HHT:
        print x;

La magia está en el comando yield en la función cadena. Al hacer

gen = cadena()

se inicia el iterador. Cada vez que uno llama entonces a

seq = gen.next()

se ejecuta una iteración del bucle

while(True):
     a, b, c = b, c, choice("HT")
     yield a,b,c

en la función cadena, que recuerda entre llamada y llamada el estado de las variables (internas a ella) a, b y c (además de devolver, como haría return, el valor generado.

Si tengo un poco de tiempo, veré si puedo implementar esta misma solucíón (sospecho que podría ser posible) con el paquete iterators de R.

11 comentarios sobre “HHH, HHT y el comando «yield» de Python

  1. Esteban Moro 26 octubre, 2012 9:56

    Yo lo haría así (con -1 y 1 para la cara y cruz )

    #inicializamos la cadena de 3 caras o cruz
    cadena <- sample(c(-1,1),3,replace=T)

    #hacemos la siguiente línea cada vez que queramos una nueva tirada
    cadena <- c(cadena[-1],sign(runif(1)-0.5))

    Y ya está

  2. Juanjo 26 octubre, 2012 10:55

    Hola:

    La combinación HHT tiende, en promedio, a aparecer antes. El motivo es muy simple, y no se me ocurre mejor forma de explicarlo que con las dos cadenas de Markov que se ven en el documento siguiente: https://dl.dropbox.com/u/769309/IMG_20121026_104840.jpg

    En pocas palabras, en el caso HHH, cuando ya se han obtenido las dos primeras H, un resultado adverso (una T) nos sitúa en la casilla de salida. En el caso HHT, por el contrario, tras haber obtenido las dos primeras H, un resultado adverso (una H) no nos lleva a la casilla de salida sino al estado HH.

    Saludos.

    Juanjo

  3. Daniel 26 octubre, 2012 11:30

    Bonita Paradoja. No la conocía.

  4. Juanjo 26 octubre, 2012 11:41

    Hola otra vez:

    Por concretar un poco más, necesitas en promedio 8 lanzamientos para obtener la secuencia HHT y 14 para obtener la secuencia HHH.

    Carlos, ¿concuerda este resultado con lo que has obtenido tú mediante simulación?

    Saludos.

    Juanjo

  5. datanalytics 26 octubre, 2012 12:01

    @Esteban Moro Enteramente de acuerdo. Es sencillo programar la cadena como cuentas.

    No osbtante, uno de mis objetivos era «practicar» con el comando «yield» y presentar un ejemplo en el que tenga «cierto» sentido utilizarlo. Tal vez es matar una mosca a cañonazos.

    La implementación que sugieres, en todo caso, tiene un «miniproblema»: las dos líneas que generan la cadena tienen que estar dentro del código que utiliza los resultados. El iterador aisla perfectamente la generación de la secuencia de su uso. Además, a través del método next().

    Vamos, que pienso que es una implementación más limpia y posiblemente más escalable.

  6. Bruno 28 octubre, 2012 20:06

    @Juanjo

    Debo ser muy torpe, porque no veo que esto sea así…creo que el patrón HHT aparece más en una sucesión de tiradas grande, por lo que dice Juanjo, pero no entiendo que ninguno de los dos aparezca antes: si estoy en el estado HH y sale H, ya ha aparecido antes el patrón HHH, en definitiva, ambos sucesos necesitan situarse en HH y ahí la probabilidad de que salga H o T es, obviamente, del 50% ¿me estoy perdiendo algo?

  7. datanalytics 28 octubre, 2012 20:20

    @Bruno El asunto no es que salga siempre antes el HHT. Es que en promedio sale antes.

    Efectivamente, cuando llegas a HH, tienes un 50% de probabilidades de que salga luego H o T. El quid es que si sale H, vuelves a tener un 50% de probabilidades de que luego salga T (por lo tanto, aparezca el patrón HHT). Sin embargo, si sale T, tienes que «volver a empezar» para obtener HHH.

  8. datanalytics 31 octubre, 2012 1:01

    Finalmente me voy a tener que poner con Scala. ¿Ese pedazo de código hace lo mismo que mi bloque de Python?

    Dime que sí y comienzo mañana mismo con Scala.

  9. David 31 octubre, 2012 1:45

    Si, lo mismo. De hecho, tambien hace el calculo del promedio, que en tu bloque no has puesto.

Los comentarios están desabilitados.