рекурсивное решение для Ханойских башен работает так, что если вы хотите переместить N дисков с колышка A на C, вы сначала переместите N-1 с A на B, затем переместите нижний на C, а затем переместите опять N-1 диски от B до C. По сути,
hanoi(from, to, spare, N):
hanoi(from, spare, to, N-1)
moveDisk(from, to)
hanoi(spare, to, from, N-1)
Очевидно, что Ханой (_, _, _, 1) делает один ход, а Ханой (_, _, _, k) делает столько ходов, сколько 2 * Ханой (_, _, _, k-1) + 1 Таким образом, длина решения увеличивается в последовательности 1, 3, 7, 15, ... Это та же последовательность, что и (1 << k) - 1, что объясняет длину цикла в опубликованном вами алгоритме. </p>
Если вы посмотрите на сами решения, для N = 1 вы получите
FROM TO
; hanoi(0, 2, 1, 1)
0 2 movedisk
Для N = 2 вы получите
FROM TO
; hanoi(0, 2, 1, 2)
; hanoi(0, 1, 2, 1)
0 1 ; movedisk
0 2 ; movedisk
; hanoi(1, 2, 0, 1)
1 2 ; movedisk
А для N = 3 вы получите
FROM TO
; hanoi(0, 2, 1, 3)
; hanoi(0, 1, 2, 2)
; hanoi(0, 2, 1, 1)
0 2 ; movedisk
0 1 ; movedisk
; hanoi(2, 1, 0, 1)
2 1 ; movedisk
0 2 ; movedisk ***
; hanoi(1, 2, 0, 2)
; hanoi(1, 0, 2, 1)
1 0 ; movedisk
1 2 ; movedisk
; hanoi(0, 2, 1, 1)
0 2 ; movedisk
Из-за рекурсивного характера решения столбцы FROM и TO следуют рекурсивной логике: если вы берете среднюю запись в столбцах, части выше и ниже являются копиями друг друга, но с переставленными числами. Это очевидное следствие самого алгоритма, который не выполняет никакой арифметики с числами колышков, а только их переставляет. В случае N = 4 средний ряд находится в точке x = 4 (отмечены тремя звездами выше).
Теперь выражение (X & (X-1)) отменяет наименее установленный бит X, поэтому оно отображается, например, числа от 1 до 7 так:
1 -> 0
2 -> 0
3 -> 2
4 -> 0 (***)
5 -> 4 % 3 = 1
6 -> 4 % 3 = 1
7 -> 6 % 3 = 0
Хитрость в том, что поскольку средняя строка всегда имеет точную степень двойки и, следовательно, имеет ровно один установленный бит, часть после средней строки равна части перед ней при добавлении значения средней строки (в данном случае 4 ) к строкам (т.е. 4 = 0 + 4, 6 = 2 + 6). Это реализует свойство «copy», добавление среднего ряда реализует часть перестановки. Выражение (X | (X-1)) + 1 устанавливает младший нулевой бит, который имеет единицы справа, и очищает их, поэтому он имеет свойства, аналогичные ожидаемым:
1 -> 2
2 -> 4 % 3 = 1
3 -> 4 % 3 = 1
4 -> 8 (***) % 3 = 2
5 -> 6 % 3 = 0
6 -> 8 % 3 = 2
7 -> 8 % 3 = 2
Что касается того, почему эти последовательности действительно дают правильные номера колышков, давайте рассмотрим столбец FROM. Рекурсивное решение начинается с Ханоя (0, 2, 1, N), поэтому в среднем ряду (2 ** (N-1)) у вас должен быть Moveisk (0, 2). Теперь по правилу рекурсии, в (2 ** (N-2)) вам нужно иметь Moveisk (0, 1) и в (2 ** (N-1)) + 2 ** (N-2) MoveDisk ( 1, 2). Это создает шаблон «0,0,1» для колышков from, который виден с различными перестановками в таблице выше (проверьте строки 2, 4 и 6 для 0,0,1 и строки 1, 2, 3 для 0,0 , 2 и строки 5, 6, 7 для 1,1,0, все перестановочные версии одного и того же шаблона).
Теперь из всех функций, обладающих этим свойством, они создают копии самих себя со степенью двойки, но со смещением, авторы выбрали те, которые производят по модулю 3 правильные перестановки. Это не очень сложная задача, потому что есть только 6 возможных перестановок из трех целых чисел 0..2, и перестановки развиваются в логическом порядке в алгоритме. (X | (X-1)) + 1 не обязательно тесно связана с проблемой Ханоя или не должна быть; достаточно того, что у него есть свойство копирования и что оно производит правильные перестановки в правильном порядке.