Ir al contenido

PINsmith

Autor
Santiago Chavarro

Análisis
#

Dado un patrón de PIN donde algunos dígitos son conocidos y otros están ocultos con “*”, se deben listar todos los posibles PINs que:

  • Cumplen con el patrón dado (los dígitos conocidos deben aparecer en sus posiciones fijas).
  • No tienen dos dígitos iguales consecutivos (por ejemplo, “112” no es válido).
  • Los dígitos en posiciones desconocidas ("*") pueden ser cualquier número del 0 al 9, respetando la restricción anterior.
  • La salida debe estar ordenada lexicográficamente.

Este enfoque permite predecir posibles combinaciones válidas sin realizar un ataque de fuerza bruta directo al sistema, aprovechando la información parcial obtenida y evitando repetir intentos similares que podrían levantar sospechas.

Código
#

n = input()

def crearPin(n, pos = 0, caracterActual = ''):
    if(int(len(n)) == pos):
        print(caracterActual)
        return
    if(str(n[pos]) != '*'):
        if(pos == 0 or caracterActual[-1] != n[pos]):
            crearPin(n, pos+1, caracterActual + str(n[pos]))
    else:
        for i in range(10):
            if(pos == 0 or caracterActual[-1] != str(i)):
                crearPin(n, pos+1, caracterActual + str(i))

crearPin(n)

Funcionamiento del código
#

  • La función crearPin es recursiva y va construyendo cada PIN candidato posición por posición.
  • Si la posición actual ya tiene un dígito fijo (no es “*”), solo agrega ese dígito si no es igual al anterior en el PIN en construcción.
  • Si la posición actual es “*”, prueba con cada dígito del 0 al 9, verificando que no sea igual al anterior para cumplir la política de seguridad.
  • Cuando alcanza la longitud total del PIN, imprime la combinación generada.

Detalles importantes del código
#

  • Parámetro pos indica la posición actual en el string que se está procesando.
  • caracterActual es el PIN parcial construido hasta la posición anterior.
  • La validación pos == 0 or caracterActual[-1] != n[pos] asegura que no haya dígitos consecutivos iguales.
  • La función itera exhaustivamente solo en posiciones con “*”, generando todas las combinaciones válidas.

Recursividad explicada paso a paso
#

Piensa en la tarea de construir un PIN como armarlo dígito por dígito. La función recorre el patrón del PIN desde el primer dígito (posición 0) hasta el último, y en cada llamada decide qué valor colocar en la posición actual:

  1. Caso base:
    Cuando la función alcanza la última posición, significa que el PIN está completo. En ese momento, imprime el PIN construido. Es el punto donde la recursión termina.
  2. Caso recursivo:
    • Para cada posición del PIN:
      1. Si el dígito es conocido (no es “*”), solo puede ponerse ese valor si no es igual al anterior.
      2. Si el dígito es desconocido ("*"), la función prueba todos los valores posibles (del 0 al 9), pero solo si no son iguales al anterior.

En ambos casos, después de elegir el dígito, la función se llama nuevamente para colocar el siguiente dígito.

¿Por qué la recursividad es útil aquí?
#

  • Permite explorar todas las combinaciones posibles de manera organizada.
  • Elimina automáticamente PINs inválidos al aplicar las restricciones justo antes de expandir una rama nueva.
  • Simplifica el código, evitando bucles anidados complejos.

Referencias
#