I numeri sono troppo grandi per essere pubblicati, quindi eccoli su Pastebin: num 1 , num 2 .
Il primo numero è 600^2 = 360000
uno. Il secondo numero è lo stesso, ad eccezione delle seguenti modifiche:
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
Entrambi gli hash 271088937720654725553339294593617693056
.
Spiegazione
Diamo un'occhiata alla prima metà del codice:
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)
Quindi, se riusciamo a trovare due numeri di input in modo che le somme di S[i][j]*13^i*19^j
siano lo stesso modulo 16^20
sia per l'array iniziale da 600 che per l'array zippato, allora abbiamo finito.
Per rendere le cose un po 'più semplici, prenderemo in considerazione solo 600^2 = 360000
i numeri di input -digit, in modo che l'array da 600 sia solo un 600 per 600 quadrati di cifre. Questo rende le cose più facili da visualizzare ed è valido da allora 10^360000 ~ 2^(2^20.19) < 2^(2^30)
. Per semplificare ulteriormente le cose, considereremo solo tali stringhe di input il cui quadrato delle cifre è simmetrico lungo la diagonale principale, in modo che l'array originale e l'array zippato siano gli stessi. Questo ci consente anche di ignorare l'inversione della stringa iniziale e la numerazione dell'indice da destra a sinistra, che si annullano a vicenda.
Per iniziare, possiamo prendere il primo numero come 360000
uno. Per ottenere il secondo numero, vogliamo modificarlo cambiando alcune cifre in modo che le somme siano lo stesso modulo 16^20
, preservando la simmetria del quadrato delle cifre. Lo realizziamo trovando un elenco di triple in (i, j, k)
modo che
sum of k*(13^i 19^j + 19^i 13^j) == 0 mod 16^20
dov'è 1 <= k <= 8
l'importo per aumentare la cifra 1 di (ovvero cambiando in una cifra da 2 a 9 - avremmo potuto includere 0 ma non ne avevamo bisogno) e 0 <= i < j < 600
sono coppie di indici.
Una volta che abbiamo i (i, j, k)
tre gemelli, abbiamo cambiare le cifre in (i, j)
e (j, i)
per 1+k
ottenere il secondo numero. Le terzine sono state trovate usando un avido algoritmo di backtracking e per il secondo numero sopra il quadrato delle cifre appare come:
188181811111711 ...
815112111711111 ...
851611111111111 ...
116114118112111 ...
811115111111111 ...
121451111111111 ...
811111111111111 ...
111111111111111 ...
111811111111111 ...
171111111111111 ...
111111111111111 ...
111211111111111 ...
711111111111111 ...
111111111111111 ...
111111111111111 ...
............... .
............... .
............... .
Ad esempio, (i, j, k) = (0, 1, 7)
corrisponde alla modifica delle cifre (0, 1)
(posizione 600*0 + 1 = 1
) e (1, 0)
(posizione 600*1 + 0 = 600
) in 1 + 7 = 8
.
Ecco il backtracker in Python 3, anche se un'attenta ispezione ha rivelato che siamo stati abbastanza fortunati, poiché in realtà non è accaduto alcun backtracking:
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
Per un bonus, ecco una porta non molto efficiente dell'hash in Python 3. Era inutile.