Os números são grandes demais para serem publicados, então aqui estão eles no Pastebin: num 1 , num 2 .
O primeiro número é um 600^2 = 360000
. O segundo número é o mesmo, exceto para as seguintes alterações:
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 para 271088937720654725553339294593617693056
.
Explicação
Vamos dar uma olhada na primeira metade do 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)
Portanto, se pudermos encontrar dois números de entrada para que as somas de S[i][j]*13^i*19^j
sejam o mesmo módulo 16^20
para a matriz inicial de 600 e a matriz compactada, então terminamos.
Para tornar as coisas um pouco mais fáceis, consideraremos apenas 600^2 = 360000
números de entrada com dois dígitos, para que a matriz de 600 caracteres tenha apenas 600 por 600 quadrados de dígitos. Isso facilita a visualização das coisas e é válido desde então 10^360000 ~ 2^(2^20.19) < 2^(2^30)
. Para simplificar ainda mais as coisas, consideraremos apenas as cadeias de entrada cujo quadrado de dígitos é simétrico ao longo da diagonal principal, para que a matriz original e a matriz compactada sejam iguais. Isso também nos permite ignorar a reversão inicial da string e a numeração do índice da direita para a esquerda, que se cancelam.
Para começar, podemos considerar o primeiro número como um 360000
. Para obter o segundo número, queremos modificar isso alterando alguns dígitos, para que as somas sejam o mesmo módulo 16^20
, preservando a simetria do quadrado do dígito. Conseguimos isso encontrando uma lista de triplos (i, j, k)
para que
sum of k*(13^i 19^j + 19^i 13^j) == 0 mod 16^20
onde 1 <= k <= 8
é o valor para aumentar o dígito 1 (alterando para um dígito de 2 para 9 - poderíamos ter incluído 0, mas não precisamos dele) e 0 <= i < j < 600
somos pares de índices.
Uma vez que temos os (i, j, k)
trigêmeos, mudamos os dígitos no (i, j)
e (j, i)
para 1+k
obter o segundo número. Os trigêmeos foram encontrados usando um algoritmo ganancioso de retorno, e para o segundo número acima do dígito quadrado se parece com:
188181811111711 ...
815112111711111 ...
851611111111111 ...
116114118112111 ...
811115111111111 ...
121451111111111 ...
811111111111111 ...
111111111111111 ...
111811111111111 ...
171111111111111 ...
111111111111111 ...
111211111111111 ...
711111111111111 ...
111111111111111 ...
111111111111111 ...
............... .
............... .
............... .
Por exemplo, (i, j, k) = (0, 1, 7)
corresponde à alteração dos dígitos (0, 1)
(posição 600*0 + 1 = 1
) e (1, 0)
(posição 600*1 + 0 = 600
) para 1 + 7 = 8
.
Aqui está o backtracker no Python 3, embora uma inspeção mais detalhada tenha revelado que tivemos muita sorte, pois nenhum backtracking realmente aconteceu:
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
Para um bônus, aqui está uma porta não tão eficiente do hash no Python 3. Era inútil.