Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

miércoles, 5 de octubre de 2022

La secuencia de Van Eck en Python (y un poco acerca de la palabra reservada yield)

Buscando tutoriales ya no recuerdo de qué, me encontré con esto Van Eck Sequence Using Python - Roy Tutorials (roytuts.com)

¡La Secuencia de Van Eck! Este link también la menciona:

The Van Eck Sequence | IB Maths Resources from Intermathematics 

Y ambos la explican de una forma horrible. En serio, a partir de esas explicaciones tan cutres intenté hacer mi propio algoritmo (sin mirar los de otros) y fue horrible. Tuve que ir a esta otra web Van Eck's sequence - EverybodyWiki Bios & Wiki y jugar un poco frustrarme dos horas armando mi propio código para hallar la verdadera explicación de cómo generar la secuencia de Van Eck:

1. Empieza con cero. En este ejercicio vamos a empezar siempre de cero.

2. Coge el último número de la secuencia.

3. Si ese número no se halla al menos dos veces en la secuencia, añade cero al final. Si se halla al menos dos veces, coge las dos últimas posiciones, réstalas, y el resultado se añade al final de la secuencia.

El código que me salió es:

vanEckSeq = [0]

for _ in range(0, 20):
    valor = vanEckSeq[-1]    
   
    if vanEckSeq.count(valor) < 2:
        vanEckSeq.append(0)
    else:
        #extraer últimos dos indices
        vanEckSeq0 = [c[0] for c in enumerate(vanEckSeq) if c[1] == valor]
               
        index1 = vanEckSeq0[-1]
        index2 = vanEckSeq0[-2]
       
        vanEckSeq.append(index1 - index2)
       

print(vanEckSeq)


No está mal porque una de las condiciones que me impuse es que no podía usar bucles anidados, es decir nada de:

for _ in range(0, 20):
    for _ in range(0, whatever): 
 
Y además usa una función muy bonita de Python: enumerate que devuelve una lista de tuplas cuyos elementos son (posición del elemento en la lista, elemento de la lista).
Mi código se parece al del primer link, pero en el primer link hace algo que no me gusta: evaluar y usar todos los números naturales hasta un límite determinado, cuando en la definición de la secuencia de Van Eck se usan los valores que ya posee la secuencia.

Mi código además permite hacer cosas como esta:


def VanEck():
    vanEckSeq = [0]

    while(True):
        valor = vanEckSeq[-1]
       
        if vanEckSeq.count(valor) < 2:
            vanEckSeq.append(0)
        else:
            #extraer últimos dos indices
            vanEckSeq0 = [c[0] for c in enumerate(vanEckSeq) if c[1] == valor]
                   
            index1 = vanEckSeq0[-1]
            index2 = vanEckSeq0[-2]
           
            vanEckSeq.append(index1 - index2)
           
        yield vanEckSeq[-1]
   
   
generator = VanEck()
   
for _ in range (0, 20):
    print (next(generator))

# ... some code....
print (next(generator))
# ... some other half million lines of code....
print (next(generator))


Puedo generar una función que devuelve el último valor de la secuencia de forma indefinida. En casi todos los tutoriales que he revisado del uso de la palabra resevada yield se usa un bucle para mostrar cómo funciona. Esto no demuetra el poder de yield, el cual permite obtener un generador el cual devolverá el siguiente valor de la operación cuando se necesite, no es necesario que esté dentro de un bucle. yield permite que Python guarde el estado de todas las variables dentro de la función (que en este caso devuelve un generador), para que estén listas para la siguiente llamada next() del generador.

Codifiqué esto en Python 3.9 con Visual Code en Windows 10.