Liczby są zbyt duże, aby je opublikować, więc tutaj są na Pastebin: num 1 , num 2 .
Pierwsza liczba to 600^2 = 360000
jedynki. Drugi numer jest taki sam, z wyjątkiem następujących zmian:
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
Oba hash do 271088937720654725553339294593617693056
.
Wyjaśnienie
Rzućmy okiem na pierwszą połowę kodu:
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)
Jeśli więc możemy znaleźć dwie liczby wejściowe, aby sumy S[i][j]*13^i*19^j
były tego samego modulo 16^20
zarówno dla początkowej tablicy o szerokości 600, jak i tablicy spakowanej, to jesteśmy skończeni.
Aby trochę ułatwić, rozważymy tylko 600^2 = 360000
cyfry cyfrowe, tak że tablica o szerokości 600 ma po prostu kwadrat 600 x 600 cyfr. Ułatwia to wizualizację i jest ważny od tego czasu 10^360000 ~ 2^(2^20.19) < 2^(2^30)
. Aby jeszcze bardziej uprościć sprawę, rozważymy tylko takie ciągi wejściowe, których kwadrat cyfrowy jest symetryczny wzdłuż głównej przekątnej, tak że pierwotna tablica i tablica spakowana są takie same. Pozwala nam to również zignorować początkowe odwrócenie łańcucha i numerację indeksów od prawej do lewej, które się wzajemnie znoszą.
Na początek możemy przyjąć pierwszą liczbę 360000
. Aby uzyskać drugą liczbę, chcemy to zmienić, zmieniając niektóre cyfry, aby sumy były takie same modulo 16^20
, zachowując symetrię kwadratu cyfry. Osiągamy to, znajdując listę trójek (i, j, k)
, aby
sum of k*(13^i 19^j + 19^i 13^j) == 0 mod 16^20
gdzie 1 <= k <= 8
jest kwota na zwiększenie cyfry 1 o (tj. przejście na cyfrę od 2 do 9 - moglibyśmy uwzględnić 0, ale nie potrzebowaliśmy jej) i 0 <= i < j < 600
są parami indeksów.
Raz mamy (i, j, k)
trojaczki, zmieniamy cyfry na (i, j)
i (j, i)
aby 1+k
uzyskać drugą liczbę. Trojaczki zostały znalezione przy użyciu chciwego algorytmu cofania, a dla drugiej liczby powyżej cyfry kwadrat wygląda następująco:
188181811111711 ...
815112111711111 ...
851611111111111 ...
116114118112111 ...
811115111111111 ...
121451111111111 ...
811111111111111 ...
111111111111111 ...
111811111111111 ...
171111111111111 ...
111111111111111 ...
111211111111111 ...
711111111111111 ...
111111111111111 ...
111111111111111 ...
............... .
............... .
............... .
Na przykład (i, j, k) = (0, 1, 7)
odpowiada zmianie cyfr (0, 1)
(pozycji 600*0 + 1 = 1
) i (1, 0)
(pozycji 600*1 + 0 = 600
) na 1 + 7 = 8
.
Oto backtracker w Pythonie 3, chociaż dokładniejsza analiza wykazała, że mieliśmy szczęście, ponieważ tak naprawdę nie nastąpiło cofnięcie:
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
Jako bonus, oto niezbyt wydajny port skrótu w Pythonie 3. To było bezużyteczne.