Los números son demasiado grandes para publicar, así que aquí están en Pastebin: num 1 , num 2 .
El primer número son 600^2 = 360000
unos. El segundo número es el mismo, excepto por los siguientes cambios:
Positions to change to "2": 605, 1811, 3001, 6603
Positions to change to "4": 1805, 3003, 57348, 208895
Positions to change to "5": 602, 1201, 2405, 3004
Positions to change to "6": 1203, 1802
Positions to change to "7": 12, 609, 5401, 7200
Positions to change to "8": 1, 2, 4, 6, 600, 1200, 1808, 2400, 3600, 4803
Ambos hash a 271088937720654725553339294593617693056
.
Explicación
Echemos un vistazo a la primera mitad del código:
lW% e# Read input number as string, and reverse
600/ e# Split every 600 digits, forming a 2D array
_z e# Duplicate and zip, swapping rows and columns
{ }% e# For both arrays...
JfbDb e# Find sum of S[i][j]*13^i*19^j, where S are the character values
e# and the indices are from right to left, starting at 0.
GK# e# Take modulo 16^20
... ... e# (Rest of code irrelevant)
Entonces, si podemos encontrar dos números de entrada para que las sumas S[i][j]*13^i*19^j
sean del mismo módulo 16^20
tanto para la matriz inicial de 600 anchos como para la matriz comprimida, entonces hemos terminado.
Para facilitar un poco las cosas, consideraremos solo 600^2 = 360000
números de entrada de dígitos, de modo que la matriz de 600 de ancho sea solo un cuadrado de 600 por 600 de dígitos. Esto hace que las cosas sean más fáciles de visualizar y es válido desde entonces 10^360000 ~ 2^(2^20.19) < 2^(2^30)
. Para simplificar aún más las cosas, solo consideraremos las cadenas de entrada cuyo cuadrado de dígitos es simétrico a lo largo de la diagonal principal, de modo que la matriz original y la matriz comprimida sean las mismas. Esto también nos permite ignorar la inversión inicial de la cadena y la numeración del índice de derecha a izquierda, que se cancelan mutuamente.
Para comenzar, podemos tomar el primer número como 360000
unos. Para obtener el segundo número, queremos modificar esto cambiando algunos de los dígitos para que las sumas sean del mismo módulo 16^20
, mientras se preserva la simetría del cuadrado del dígito. Logramos esto al encontrar una lista de triples (i, j, k)
para que
sum of k*(13^i 19^j + 19^i 13^j) == 0 mod 16^20
donde 1 <= k <= 8
es la cantidad para aumentar el dígito 1 (es decir, cambiar a un dígito de 2 a 9; podríamos haber incluido 0 pero no lo necesitamos) y 0 <= i < j < 600
son pares de índices.
Una vez que tenemos los (i, j, k)
trillizos, que cambiar los dígitos en (i, j)
y (j, i)
para 1+k
conseguir el segundo número. Los trillizos se encontraron usando un algoritmo de retroceso codicioso, y para el segundo número sobre el cuadrado del dígito se ve así:
188181811111711 ...
815112111711111 ...
851611111111111 ...
116114118112111 ...
811115111111111 ...
121451111111111 ...
811111111111111 ...
111111111111111 ...
111811111111111 ...
171111111111111 ...
111111111111111 ...
111211111111111 ...
711111111111111 ...
111111111111111 ...
111111111111111 ...
............... .
............... .
............... .
Por ejemplo, (i, j, k) = (0, 1, 7)
corresponde a cambiar los dígitos (0, 1)
(posición 600*0 + 1 = 1
) y (1, 0)
(posición 600*1 + 0 = 600
) a 1 + 7 = 8
.
Aquí está el backtracker en Python 3, aunque una inspección más cercana reveló que tuvimos bastante suerte, ya que en realidad no hubo retroceso:
n = 16**20
L = [(k *(pow(13,i,n)*pow(19,j,n) + pow(19,i,n)*pow(13,j,n)) % n, i, j, k)
for i in range(600) for j in range(600) for k in range(1, 9) if i < j]
L.sort(reverse=True)
stack = [(n, 0, [])]
while stack:
k, index, result = stack.pop()
if k == 0:
print(result)
break
if index == len(L):
continue
stack.append((k, index+1, result)) # Don't include triplet
if L[index][0] <= k:
stack.append((k - L[index][0], index+1, result + [L[index][1:]])) # Include
Como beneficio adicional, aquí hay un puerto no tan eficiente del hash en Python 3. Era inútil.