10 януари 2017 г.

AlgorithmO #8 - Генериране на пермутации (намиране на следваща пермутация в лексикографски ред)

Продължаваме с пермутациите! Днес ще се опитам да обясня един алгоритъм за генериране на пермутации на ръка. В началото ми се стори доста объркващ, но всъщност е извънредно прост за разбиране.

---



ОПИСАНИЕ:

Както обяснихме в поста за кодиране на пермутации, те представляват комбинации, при които редът е от значение. Иначе казано, ако имаме множество от 3 елемента и тези елементи са 1, 2 и 3 - пермутациите на това множество ще са 123, 132, 213, 231, 312, 321. Във всяка пермутация трябва да участват всички елементи на множеството.

Когато елементите са малко, лесно можем и сами да се досетим какви ще са пермутациите, но този метод спира да бъде ефективен ако елементите станат повечко.

С този алгоритъм можем да генерираме всички съществуващи пермутации - от първата до последната. Или пък ако имаме декодирана пермутацията с номер 345, да намерим тази с номер 346, 347 и т.н, без да се налага да декодираме всяка поотделно.

АЛГОРИТЪМ:

(взето от лекциите ми по Проектиране и анализ на алгоритми :))

Разглеждаме една пермутация на N обекта, означени с числата от 1 до N. Следващата в лексикографското подреждане пермутация се генерира по следния начин:

1. Преглеждаме пермутацията отдясно наляво докато елементите растат (или са равни, в случай, че има повторения). Спираме на първия елемент, който е по-малък от следващия го. Нека той има номер К.

2. Измежду елементите, които са отдясно на елемент K, намираме най-малкия, но по-голям от елемента с номер К. Разменяме го с К-тия елемент.
3. Обръщаме обратно частта от пермутацията с номера К+1...N.
ПРИМЕР:

Нека имаме следната пермутация:

3 2 5 1 7 6 4

Сега искаме да намерим следващата пермутация в лексикографски ред. Как да стане?

Виждаме, че пермутацията се състои от 7 елемента (N = 7). Първото, което трябва да направим, е да номерираме елементите, за да е по-прегледно:

3 2 5 1 7 6 4
1 2 3 4 5 6 7

Сега започваме да разглеждаме пермутацията отдясно наляво. Трябва ни първият елемент, който е по-малък от този, който стои до него отдясно.

Виждаме, че това е елементът с номер 4 (и стойност 1), понеже директно след него стои по-голям елемент със стойност 7 (за елементите с номера 5, 6 и 7 това не е вярно - до всеки от тях стой по-малък елемент, а до последния елемент няма никакъв друг). В случая ни интересува номерът, а не стойността на елемента.

Стойността на К става номера на елемента, който току що избрахме, т. е K = 4. Нека го означим с червен цвят:

3 2 5 1 7 6 4
1 2 3 4 5 6 7

Сега ни трябва най-малкият елемент, който стои отдясно на К-тия елемент и е по-голям от него. Сравнително лесно виждаме, че това е елементът с номер 7 (и стойност 4). Елементите със стойност 7 и 6 също са по-големи от K-тия елемент, но не са минимални.

Нека маркираме този нов избран елемент със зелен цвят:

3 2 5 1 7 6 4
1 2 3 4 5 6 7

Следващата стъпка е да разменим местата на тези два елемента (разбира се, номерата им се запазват, разменяме само стойностите):

3 2 5 4 7 6 1
1 2 3 4 5 6 7

Последната стъпка е да обърнем наобратно елементите с номера от K+1 до N включително.

Тъй като определихме, че K = 4 и N = 7, това значи, че ще обърнем елементите с номера от 5 до 7 наобратно (т.е. от 7, 6, 1, подредбата им ще стане на 1, 6, 7). Сега пермутацията ще изглежда така:

3 2 5 4 1 6 7

Това е следващата пермутация в лексикографски ред. По същия начин можем да намерим и следващите.

---

Това е за днес! Ако забележите някоя неточност, споделете! 😉

2 коментара:

  1. Може ли да запишеш и всички останали пермутации, след последната направена?

    ОтговорИзтриване
    Отговори
    1. Ще трябва да си го припомня, защото не съм се занимавал с този алгоритъм от много време и вече ми стои мъгливо в главата... 😂 Но може да пробваш със същия алгоритъм от поста и след това да сравниш с резултатите от някоя имплементация в интернет.

      Изтриване