Jump to content

Алгоритм Хипа

Карта 24 перестановок и 23 свопов, используемых в алгоритме Хипа, переставляющих четыре буквы A (янтарный), B (синий), C (голубой) и D (темно-красный).
Колесная диаграмма всех перестановок длины генерируется алгоритмом Хипа, где каждая перестановка имеет цветовую кодировку (1 = синий, 2 = зеленый, 3 = желтый, 4 = красный).

Хипа Алгоритм генерирует все возможные перестановки из n объектов. Впервые он был предложен BR Heap в 1963 году. [ 1 ] Алгоритм минимизирует движение: он генерирует каждую перестановку из предыдущей путем замены одной пары элементов; остальные n -2 элемента не нарушены. В обзоре алгоритмов генерации перестановок 1977 года Роберт Седжвик пришел к выводу, что на тот момент это был наиболее эффективный алгоритм генерации перестановок с помощью компьютера. [ 2 ]

Последовательность объектов , перестановок n сгенерированная алгоритмом Хипа, является началом последовательности перестановок n +1 объектов. Итак, существует одна бесконечная последовательность перестановок, сгенерированная алгоритмом Хипа (последовательность A280318 в OEIS ).

Подробности алгоритма

[ редактировать ]

Для коллекции содержащий n различных элементов, Хип нашел систематический метод выбора на каждом шаге пары элементов для переключения, чтобы произвести каждую возможную перестановку этих элементов ровно один раз.

Описанный рекурсивно как метод уменьшения и завоевания , алгоритм Хипа работает на каждом этапе начальные элементы коллекции. Изначально и после этого . Каждый шаг генерирует перестановки, которые заканчиваются одним и тем же конечные элементы. Он делает это, вызывая себя один раз с помощью элемент без изменений, а затем раз с ( ) элемент, замененный на каждый из начальных элементы. Рекурсивные вызовы изменяют исходный элементы и нужно правило на каждой итерации выбирать, какие будут обмениваться с последними. Метод Хипа говорит, что этот выбор может быть сделан по четности количества элементов, обрабатываемых на этом этапе. Если четен, то последний элемент итеративно заменяется индексом каждого элемента. Если нечетно, последний элемент всегда заменяется первым.

procedure generate(k : integer, A : array of any):
    if k = 1 then
        output(A)
    else
        // Generate permutations with k-th unaltered
        // Initially k = length(A)
        generate(k - 1, A)

        // Generate permutations for k-th swapped with each k-1 initial
        for i := 0; i < k-1; i += 1 do
            // Swap choice dependent on parity of k (even or odd)
            if k is even then
                swap(A[i], A[k-1]) // zero-indexed, the k-th is at k-1
            else
                swap(A[0], A[k-1])
            end if
            generate(k - 1, A)
        end for
    end if

Можно также написать алгоритм в нерекурсивном формате. [ 3 ]

procedure generate(n : integer, A : array of any):
    // c is an encoding of the stack state. c[k] encodes the for-loop counter for when generate(k - 1, A) is called
    c : array of int

    for i := 0; i < n; i += 1 do
        c[i] := 0
    end for

    output(A)
    
    // i acts similarly to a stack pointer
    i := 1;
    while i < n do
        if  c[i] < i then
            if i is even then
                swap(A[0], A[i])
            else
                swap(A[c[i]], A[i])
            end if
            output(A)
            // Swap has occurred ending the for-loop. Simulate the increment of the for-loop counter
            c[i] += 1
            // Simulate recursive call reaching the base case by bringing the pointer to the base case analog in the array
            i := 1
        else
            // Calling generate(i+1, A) has ended as the for-loop terminated. Reset the state and simulate popping the stack by incrementing the pointer.
            c[i] := 0
            i += 1
        end if
    end while

Доказательство

[ редактировать ]

В этом доказательстве мы будем использовать приведенную ниже реализацию в качестве алгоритма кучи. Хотя это и не оптимально (оно не минимизирует ходы, что описано в разделе ниже), реализация, тем не менее, по-прежнему правильна и будет производить все перестановки. Причина использования приведенной ниже реализации заключается в том, что анализ проще и некоторые закономерности можно легко проиллюстрировать.

procedure generate(k : integer, A : array of any):
    if k = 1 then
        output(A)
    else
        for i := 0; i < k; i += 1 do
            generate(k - 1, A)
            if k is even then
                swap(A[i], A[k-1])
            else
                swap(A[0], A[k-1])
            end if

        end for
    end if

Утверждение: если массив A имеет длину n , то выполнение алгоритма Heap либо приведет к тому, что A будет «повернуто» вправо на 1 (т. е. каждый элемент сдвинут вправо, причем последний элемент займет первую позицию), либо приведет к тому, что A станет неизменен, в зависимости от того, является ли n четным или нечетным соответственно.

Основание: Приведенное выше утверждение тривиально справедливо для поскольку алгоритм Хипа просто вернет A без изменений по порядку.

Индукция: предположим, что утверждение верно для некоторых . Затем нам нужно будет обработать два случая для : четное или нечетное.

Если А для четно, то подмножество первых i элементов останется неизменным после применения алгоритма Хипа к подмассиву, как предполагается по гипотезе индукции. Выполняя алгоритм кучи на подмассиве, а затем выполняя операцию замены на k -й итерации цикла for, где , k -й элемент в A будет заменен на последнюю позицию A , которую можно рассматривать как своего рода «буфер». Поменяв местами 1-й и последний элементы, а затем поменяв местами 2-й и последний элементы, вплоть до тех пор, пока не будут заменены n -й и последний элементы, массив, наконец, испытает вращение. Чтобы проиллюстрировать вышесказанное, посмотрите ниже случай

1,2,3,4 ... Original Array
1,2,3,4 ... 1st iteration (Permute subset)
4,2,3,1 ... 1st iteration (Swap 1st element into "buffer")
4,2,3,1 ... 2nd iteration (Permute subset)
4,1,3,2 ... 2nd iteration (Swap 2nd element into "buffer")
4,1,3,2 ... 3rd iteration (Permute subset)
4,1,2,3 ... 3rd iteration (Swap 3rd element into "buffer")
4,1,2,3 ... 4th iteration (Permute subset)
4,1,2,3 ... 4th iteration (Swap 4th element into "buffer") ... The altered array is a rotated version of the original

Если А для нечетно, то подмножество первых i элементов будет повернуто после выполнения алгоритма кучи для первых i элементов. Обратите внимание, что после 1 итерации цикла for при выполнении алгоритма Хипа на поворачивается вправо A A на 1. По гипотезе индукции предполагается, что первые i элементов будут вращаться. После этого вращения первый элемент A будет заменен в буфер, который в сочетании с предыдущей операцией вращения, по сути, выполнит вращение массива. Выполните эту операцию поворота n раз, и массив вернется в исходное состояние. Это показано ниже для случая .

1,2,3,4,5 ... Original Array
4,1,2,3,5 ... 1st iteration (Permute subset/Rotate subset)
5,1,2,3,4 ... 1st iteration (Swap)
3,5,1,2,4 ... 2nd iteration (Permute subset/Rotate subset)
4,5,1,2,3 ... 2nd iteration (Swap)
2,4,5,1,3 ... 3rd iteration (Permute subset/Rotate subset)
3,4,5,1,2 ... 3rd iteration (Swap)
1,3,4,5,2 ... 4th iteration (Permute subset/Rotate subset)
2,3,4,5,1 ... 4th iteration (Swap)
5,2,3,4,1 ... 5th iteration (Permute subset/Rotate subset)
1,2,3,4,5 ... 5th iteration (Swap) ... The final state of the array is in the same order as the original

Индукционное доказательство утверждения теперь завершено, что теперь приведет к тому, почему алгоритм Хипа создает все перестановки массива A . Еще раз по индукции докажем корректность алгоритма Хипа.

Основа: Алгоритм Хипа тривиально переставляет массив A размера 1, поскольку вывод A является единственной перестановкой A .

Индукция: предположим, что алгоритм кучи переставляет массив размера i . Используя результаты предыдущего доказательства, каждый элемент A окажется в «буфере» один раз, когда будут переставлены первые i элементов. Поскольку перестановки массива могут быть сделаны путем изменения некоторого массива A путем удаления элемента x из A , а затем добавления x к каждой перестановке измененного массива, из этого следует, что алгоритм кучи переставляет массив размером , поскольку «буфер», по сути, содержит удаленный элемент, прикрепленный к перестановкам подмассива размера i . Поскольку каждая итерация алгоритма Хипа имеет другой элемент A , занимающий буфер при перестановке подмассива, каждая перестановка генерируется, поскольку каждый элемент A имеет шанс быть прикрепленным к перестановкам массива A без элемента буфера.

Частые неправильные реализации

[ редактировать ]

Соблазнительно упростить приведенную выше рекурсивную версию, уменьшив количество случаев рекурсивных вызовов. Например, как:

procedure generate(k : integer, A : array of any):
    if k = 1 then
        output(A)
    else

        // Recursively call once for each k
        for i := 0; i < k; i += 1 do
            generate(k - 1, A)
            // swap choice dependent on parity of k (even or odd)
            if k is even then
                // no-op when i == k-1
                swap(A[i], A[k-1])
            else
                // XXX incorrect additional swap when i==k-1
                swap(A[0], A[k-1]) 
            end if

        end for
    end if

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

свопы дополнительно = свопы
1 0 0 0
2 1 1 0
3 5 6 1
4 23 27 4
5 119 140 21
6 719 845 126
7 5039 5922 883
8 40319 47383 7064
9 362879 426456 63577

Эти дополнительные замены существенно меняют порядок префиксные элементы.

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

procedure generate(k : integer, A : array of any):
    if k = 1 then
        output(A)
    else

        // Recursively call once for each k
        for i := 0; i < k; i += 1 do
            generate(k - 1, A)
            // avoid swap when i==k-1
            if (i < k - 1)
                // swap choice dependent on parity of k
                if k is even then
                    swap(A[i], A[k-1])
                else
                    swap(A[0], A[k-1])
                end if
            end if
        end for
    end if

Выбор в первую очередь эстетический, но последний приводит к проверке ценности в два раза чаще.

См. также

[ редактировать ]
  1. ^ Хип, БР (1963). «Перестановки путем обмена» . Компьютерный журнал . 6 (3): 293–4. дои : 10.1093/comjnl/6.3.293 .
  2. ^ Седжвик, Р. (1977). «Методы генерации перестановок» . Обзоры вычислительной техники ACM . 9 (2): 137–164. дои : 10.1145/356689.356692 . S2CID   12139332 .
  3. ^ Седжвик, Роберт (4 июня 2020 г.). «Доклад об алгоритмах генерации перестановок» (PDF) .
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 1af7941584cef04132808cc9310ba8ce__1704065100
URL1:https://arc.ask3.ru/arc/aa/1a/ce/1af7941584cef04132808cc9310ba8ce.html
Заголовок, (Title) документа по адресу, URL1:
Heap's algorithm - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)