Files
hello-algo/ru/docs/chapter_divide_and_conquer/hanota_problem.md
2026-01-20 15:08:42 +08:00

8.6 KiB
Raw Blame History

Задача о Ханойских башнях

В сортировке слиянием и построении двоичного дерева мы разбиваем исходную задачу на две подзадачи, размер которых составляет половину исходной задачи. Однако для задачи о Ханойских башнях мы используем другую стратегию разбиения.

!!! question

Даны три стержня, обозначенные как `A`, `B` и `C`. В начальном состоянии на стержне `A` находится $n$ дисков, расположенных сверху вниз в порядке возрастания размера. Наша задача -- переместить эти $n$ дисков на стержень `C`, сохранив их исходный порядок (как показано на рисунке ниже). При перемещении дисков необходимо соблюдать следующие правила.

1. Диск можно снять только с верхушки одного стержня и поместить на верхушку другого стержня.
2. За один раз можно переместить только один диск.
3. Меньший диск всегда должен находиться над большим диском.

Пример задачи о Ханойских башнях

Обозначим задачу о Ханойских башнях размера i как $f(i)$. Например, f(3) представляет задачу о Ханойских башнях по перемещению 3 дисков с A на C.

Рассмотрение базового случая

Как показано на рисунке ниже, для задачи f(1), то есть когда имеется только один диск, мы можем просто переместить его с A на C.

=== "<1>" Решение задачи размера 1

=== "<2>" hanota_f1_step2

Как показано на рисунке ниже, для задачи f(2), то есть когда имеется два диска, поскольку необходимо всегда соблюдать условие, что меньший диск находится над большим, нужно использовать B для завершения перемещения.

  1. Сначала переместить верхний меньший диск с A на B.
  2. Затем переместить больший диск с A на C.
  3. Наконец, переместить меньший диск с B на C.

=== "<1>" Решение задачи размера 2

=== "<2>" hanota_f2_step2

=== "<3>" hanota_f2_step3

=== "<4>" hanota_f2_step4

Процесс решения задачи f(2) можно обобщить следующим образом: переместить два диска с A на C, используя B. При этом C называется целевым стержнем, а B -- буферным стержнем.

Разбиение на подзадачи

Для задачи f(3), то есть когда имеется три диска, ситуация становится немного сложнее.

Поскольку решения f(1) и f(2) уже известны, мы можем мыслить с точки зрения стратегии «разделяй и властвуй», рассматривая два верхних диска на A как единое целое, и выполнить шаги, показанные на рисунке ниже. Таким образом, три диска успешно перемещаются с A на C.

  1. Сделать B целевым стержнем, а C -- буферным стержнем, и переместить два диска с A на B.
  2. Переместить оставшийся один диск с A непосредственно на C.
  3. Сделать C целевым стержнем, а A -- буферным стержнем, и переместить два диска с B на C.

=== "<1>" Решение задачи размера 3

=== "<2>" hanota_f3_step2

=== "<3>" hanota_f3_step3

=== "<4>" hanota_f3_step4

По сути, мы разбиваем задачу f(3) на две подзадачи f(2) и одну подзадачу $f(1)$. После последовательного решения этих трех подзадач исходная задача также решается. Это показывает, что подзадачи независимы, и их решения можно объединить.

Таким образом, мы можем обобщить стратегию «разделяй и властвуй» для решения задачи о Ханойских башнях, показанную на рисунке ниже: разбить исходную задачу f(n) на две подзадачи f(n-1) и одну подзадачу f(1), и решить эти три подзадачи в следующем порядке.

  1. Переместить n-1 дисков с A на B, используя C.
  2. Переместить оставшийся 1 диск непосредственно с A на C.
  3. Переместить n-1 дисков с B на C, используя A.

Для этих двух подзадач f(n-1) можно применить рекурсивное разбиение тем же способом, пока не будет достигнута наименьшая подзадача f(1). А решение f(1) известно -- требуется только одна операция перемещения.

Стратегия «разделяй и властвуй» для решения задачи о Ханойских башнях

Реализация кода

В коде мы объявляем рекурсивную функцию dfs(i, src, buf, tar), которая перемещает i дисков с верхушки стержня src на целевой стержень tar, используя буферный стержень buf:

[file]{hanota}-[class]{}-[func]{solve_hanota}

Как показано на рисунке ниже, задача о Ханойских башнях формирует рекурсивное дерево высотой n, где каждый узел представляет подзадачу, соответствующую вызову функции dfs(), поэтому временная сложность составляет O(2^n), а пространственная сложность -- $O(n)$.

Рекурсивное дерево задачи о Ханойских башнях

!!! quote

Задача о Ханойских башнях происходит из древней легенды. В храме в древней Индии у монахов было три высоких алмазных стержня и $64$ золотых диска разного размера. Монахи постоянно перемещали диски, веря, что в тот момент, когда последний диск будет правильно размещен, этот мир закончится.

Однако, даже если монахи будут перемещать диски каждую секунду, потребуется примерно $2^{64} \approx 1.84×10^{19}$ секунд, что составляет около $5850$ миллиардов лет, что намного превышает текущую оценку возраста Вселенной. Поэтому, если эта легенда правдива, нам не стоит беспокоиться о конце света.