Числа слишком велики, чтобы их можно было публиковать, поэтому они находятся на Pastebin: num 1 , num 2 .
Первый номер 600^2 = 360000
один. Второй номер такой же, за исключением следующих изменений:
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
Оба хеша 271088937720654725553339294593617693056
.
объяснение
Давайте посмотрим на первую половину кода:
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)
Итак, если мы сможем найти два входных числа, чтобы суммы S[i][j]*13^i*19^j
были одинаковыми по модулю 16^20
как для исходного массива шириной 600, так и для сжатого массива, то мы закончили.
Чтобы упростить задачу, мы рассмотрим только 600^2 = 360000
-значные входные числа, так что массив шириной 600 - это просто 600 на 600 квадратных цифр. Это делает вещи проще для визуализации и действует с тех пор 10^360000 ~ 2^(2^20.19) < 2^(2^30)
. Чтобы еще больше упростить ситуацию, мы рассмотрим только такие входные строки, у которых квадрат цифр симметричен вдоль главной диагонали, так что исходный массив и сжатый массив совпадают. Это также позволяет нам игнорировать начальное обращение строк и нумерацию индексов справа налево, которые компенсируют друг друга.
Чтобы начать нас, мы можем взять первый номер, чтобы быть 360000
теми. Чтобы получить второе число, мы хотим изменить это, изменив некоторые цифры так, чтобы суммы были одинаковыми по модулю 16^20
, сохраняя при этом симметрию квадрата цифр. Мы достигаем этого, находя список троек (i, j, k)
так, чтобы
sum of k*(13^i 19^j + 19^i 13^j) == 0 mod 16^20
где 1 <= k <= 8
- величина, на которую нужно увеличить цифру 1 (т. е. изменить ее на цифру от 2 до 9 - мы могли бы включить 0, но она нам не нужна) и 0 <= i < j < 600
это индексные пары.
После того как мы (i, j, k)
тройня, мы меняем цифры на (i, j)
и (j, i)
для того 1+k
чтобы получить второй номер. Триплеты были найдены с использованием алгоритма жадного возврата, и для второго числа над цифрой квадрат выглядит так:
188181811111711 ...
815112111711111 ...
851611111111111 ...
116114118112111 ...
811115111111111 ...
121451111111111 ...
811111111111111 ...
111111111111111 ...
111811111111111 ...
171111111111111 ...
111111111111111 ...
111211111111111 ...
711111111111111 ...
111111111111111 ...
111111111111111 ...
............... .
............... .
............... .
Например, (i, j, k) = (0, 1, 7)
соответствует изменению цифр (0, 1)
(позиция 600*0 + 1 = 1
) и (1, 0)
(позиция 600*1 + 0 = 600
) на 1 + 7 = 8
.
Вот обратный трекер в Python 3, хотя при ближайшем рассмотрении выяснилось, что нам повезло, так как никакого обратного отслеживания на самом деле не произошло:
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
Для бонуса, вот не очень эффективный порт хэша в Python 3. Он был бесполезен.