Сортировка «Ханойская башня»


    Ханойские башни
    Про знаменитую игру Эдуарда Люка́ на Хабре не писа́л только ленивый. Кажется, все покровы сорваны и что-то ещё по поводу алгоритма добавить уже невозможно. Но нет, у данной темы есть ещё скрытые ресурсы. Сегодня, в частности, мы переделаем алгоритм решения этой головоломки в полноценную сортировку. (Зачем? Just for fun. В пятницу можно.)

    Данный пост посвящается памяти истинного гуру программирования Андрея Mrrl Астрелина, который когда-то мне просто и доходчиво объяснил этот алгоритм. Псевдокод ниже по тексту — его авторства.


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

    Кроме того, размеры дисков предполагаются уникальными. Не будем цепляться за это ограничение и разрешим повторы.

    Если позволить себе только эти две вольности, то начальные условия головоломки можно интерпретировать как массив (диски — это числа), а задача сводится к тому, что данный массив требуется отсортировать.

    Остальные условия мы (почти) не меняем:

    • У нас два вспомогательных шеста (пара пустых массивов).
    • Можем переносить диски по одному.
    • Класть только меньшие на бо́льшие (раз мы разрешили одинаковые размеры дисков, то также можно класть перемещаемый диск сверху на другие такого же размера).
    • Имеем право сравнивать переносимый диск только с самым верхними дисками (то бишь, все 3 массива являются стеками).

    Наша задача: взять классический рекурсивный алгоритм головоломки…

    def hanoi(n, source, helper, target):
    	if n > 0:
    		hanoi(n - 1, source, target, helper)
    		if source:
    			target.append(source.pop())
    		hanoi(n - 1, helper, source, target)

    … и превратить его в сортировку!

    На самом деле, от того что мы разрешили начальную неупорядоченность и повторы для размеров дисков — от этого принципиально не поменялось ничего. По большому счёту задача решаема всё тем же классическим рекурсивным способом. Самое главное что нужно понять — все перемещения дисков разделяются на несколько этапов, каждый из которых — это классичекий «ханой» в миниатюре.

    То есть мы на каждом этапе рассматриваем не все имеющиеся диски, а только совокупность тех из них, которые удовлетворяют старым условиям. Отсортировав по классике это небольшое множество мы приблизим общее состояние системы к классической головоломке. Тогда мы опять берём то множество дисков, которое соответствует классике и применяем известный алгоритм вновь. И это множество будет бо́льшим, чем на предыдущем шаге! И так повторяем до тех пор, пока все диски на всех шестах вдруг не станут отличаться от классического состояния.

    Нарушенная изначально система (тем, что на входе диски не упорядочены) самовосстанавливается с каждой итерацией.

    Что касается разрешения повторов, то это вообще не имеет никакого значения. Потому что подряд идущие одинаковые диски мы просто перемещаем как один диск.

    Алгоритм


    Назовем столбики A, B, C (A в начале непустой).

    Введем операции:

    А->С() — переложить один диск с А на С.
    top(A), top(С) — размер верхнего диска А или С. Если столбик пуст, то этот размер = MaxInt.
    В->С(К) — переложить с В на С все диски, размер которых меньше К (мы это можем сделать, если верхние диски А и С не меньше К).
    swap() — переставить столбики В и С (или переименовать их — нам ведь все равно, где окажутся диски).
    while(A) — цикл пока А не пуст.

    Тогда работает такой алгоритм:

    //С каждым перемещённым диском с шеста A всё более восстанавливаем "ханойскую" систему
    while(A) {
    	K = top(A);
    	//Мини-"ханой" для группы дисков
    	while(top(C) < K){
    		B->C(top(C));
    		swap();
    	}
    	A->C();
    }
    
    //Система восстановлена. Завершение - классический "ханой"
    while(C) {
    	B->C(top(C));
    	swap();
    }

    © Mrrl

    Сложность


    В худшем случае сортировка стремится к сложности по времени для классического алгоритма, который хоть прост и изящен, но при этом максимально неэффективен. Насчёт лучшей и средней предположить затрудняюсь.

    Насчёт памяти можно сказать, что если используется рекурсия, то и затраты будут соответствующие.

    Реализация


    Свой вариант на Python пока не написал (сделаю это позже). Однако ниже в разделе «Ссылки» накидал несколько ссылок, по которым можно посмотреть имплементации на различных языках. Особенно интересен вариант на Джаве. Автор не стал брать за основу общеизвестный рекурсивный способ для решения головоломки, а построил кратчайшее дерево путей. Предположительно, это является самым эффективным решением, если писать сортировку в стиле «Ханойской башни».

    Характеристики алгоритма

    Название Сортировка «Ханойская башня», Tower of Hanoi sort
    Автор идеи Эдуард Люка́
    Класс Сортировки вставками
    Сравнения Есть
    Временна́я сложность лучшая ?
    средняя ?
    худшая O(2n)

    Ссылки


    Tower of Hanoi / Ханойская башня

    Реализации: Си, Java vs C++ vs Python, Python.

    Статьи серии:



    В приложении AlgoLab данная сортировка теперь доступна. Хотя она относится к классу сортировок вставками, по причине экстравагантности алгоритма она помещена в раздел «Прочие сортировки». Ещё там есть ограничение — числа в сортируемом массиве могут быть только от 1 до 5 (связано с непростой прорисовкой дисков). Другие числа всё равно будут заменены на эти.

    Также есть ограничение на размер массива — не более 20-ти элементов. Сделано это сугубо в Ваших же интересах — если возьмёте слишком большой массив, то, может так статься, что сортировать его придётся до глубокой старости.

    EDISON Software - web-development
    Статья написана при поддержке компании EDISON Software, которая профессионально разрабатывает умное городское освещение и поддерживает сайты на питоне
    • +23
    • 6,6k
    • 1

    Edison

    307,00

    Изобретаем успех: софт и стартапы

    Поделиться публикацией

    Похожие публикации

    Комментарии 1
      +1
      Он мне тоже объяснял некоторые вещи. Светлая помять

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое