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
crearPines 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
posindica la posición actual en el string que se está procesando. caracterActuales 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:
- 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. - Caso recursivo:
- Para cada posición del PIN:
- Si el dígito es conocido (no es “*”), solo puede ponerse ese valor si no es igual al anterior.
- 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.
- Para cada posición del PIN:
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.
