
8.4. Формирование перестановок и сочетаний
Пусть множество S, содержащее n элементов, линейно упорядочено, например, как целые числа или буквы алфавита, и требуется найти все перестановки данного множества. Это означает, что нас интересует множество строк, которые включают каждый элемент множества S, причем, только один раз. Для начала введем понятие лексикографического порядка. Лексикографическим порядок называется потому, что он лежит в основе упорядочения слов в словаре. Например, слово able в английском словаре стоит перед словом gray, так как в алфавите буква a стоит перед буквой g. Слово goose стоит перед словом gray, потому что, хотя первые буквы совпадают, вторая буква слова goose стоит в алфавите перед второй буквой слова gray. Слово seconded предшествует слову seconds, потому что, хотя первые шесть букв совпадают, седьмая буква слова seconded стоит в алфавите перед седьмой буквой слова seconds. Точно так же число 12346 предшествует числу 12349.
ОПРЕДЕЛЕНИЕ 8.46. Для заданного линейно упорядоченного множества S лексикографический порядок ≤ на строках элементов множества S определяется следующим образом: а ≤ b, если а = а1a2аз.. .аm, b = b1b2b3...bn, и выполнено одно из условий:
а) а1 < b1
б) аi = bi для всех 1 ≤ i ≤ k и ак+1 < bk+1
в) аi = bi для всех 1 < i < m и n < m.
Теперь будем формировать множество всех перестановок элементов линейно упорядоченного множества. Для простоты рассмотрим только перестановки первых п целых чисел, отметив, что метод справедлив и для прочих случаев. Пусть строка a1a2a3...an обозначает перестановку
Таким образом, 51342 представляет перестановку
Для
нахождения всех перестановок необходимо
иметь возможность сформировать
перестановку, следующую за данной
согласно лексикографическому порядку.
Поскольку нас интересуют лишь строки
одной длины, пункт (в) определения
лексикографического порядка можно
опустить. При лексикографическом
порядке увеличение второй цифры
изменит расположение строки в большей
мере, чем изменение в пятой цифре.
Аналогично, увеличение первой цифры
изменит расположение строки в большей
степени, чем изменения в седьмой цифре.
Например, если начать со слова dare,
то
слово read
находится
от него дальше, чем слово dear.
Поэтому
для формирования следующей строки
необходимо менять те символы, которые
ближе всего к концу. Предположим, что
аi
> ai+1
для
m
< аi
< n.
Если
бы это условие выполнялось для всех
неотрицательных m,
то мы имели бы лексикографически
наибольшую строку, что означало бы
завершение процесса. Поэтому допустим,
что существует такое m,
что аi
> ai+1
для
m
< i
<
n,
но аm
< am+1
- Если
переупорядочить все ai
,
где
i
>
m,
то мы создадим строку, которая будет
лексикографически меньше. Например,
любая перестановка последних четырех
цифр в строке 12348765 создаст лексикографически
меньшее число. Таким образом, следует
увеличивать цифру аi,
не меняя при этом никакое аm,
где i
<
m,
т.е. нужно увеличивать am,
оставляя
без изменения все аi,
где i
<
m.
Поэтому
выберем наименьшее число аi,
такое, что i
>
m
и am
<
аi.
Меняем местами аm
и аi.
Затем переставляем в возрастающем
порядке все аi,
следующие за новым am.
Так
мы получим наименьшее из чисел, больших
исходного числа.
Например, рассмотрим число 12348765. Для получения следующего числа меняем местами 4 и 5, получая 12358764. Затем переставляем 8, 7, 6 и 4 в возрастающем порядке и получаем 12354678.
Пусть задано число 1437652. Для нахождения следующего числа меняем местами 3 и 5, получая 1457632. Теперь переставляем 7, 6, 3, 2 в возрастающем порядке, получая 1452367. Обратите внимание, что перестановка последних цифр свелась к записи их в обратном порядке, поскольку до этого они находились в убывающем порядке. Таким образом, приходим к следующей процедуре:
Процедура Формирование перестановки (ai,..., an)
Найти т такое, что аi > ai+1 для m < аi < n, но am < am+1;
Выбрать наименьшую цифру ai такую что i > m и аm < аi;
Поменять местами аm и аi;
Переупорядочить все ai стоящие после нового аm, в возрастающем порядке;
Конец процедуры.
Теперь рассмотрим метод формирования всевозможных сочетаний из n элементов множества S. Мы не будем использовать лексикографический порядок элементов множества S, а воспользуемся лексикографическим порядком на строках бинарных чисел. Напомним, что любому сочетанию элементов из n элементов соответствует бинарная строка длины n. Если множество S задано в виде
{a1,а2,аз,...,an), то единица на месте k-го бита в бинарной строке показывает, что ak присутствует в выбранном подмножестве, в то время как 0 в том же месте бинарной строки показывает, что ак не включено в выбранное подмножество. Например, если множество S представляет собой {a1.a2,a3,a4,a5}. то 10110 соответствует выбору подмножества {a1,a3,a4}, а 10001 — выбору подмножества {a1,a5}. Таким образом, для формирования всех сочетаний из множества n элементов достаточно сформировать все бинарные строки длины n. С этой целью проще всего сосчитать от 0 до 2n - 1, используя бинарные строки длины n.
Эквивалентный метод состоит в том, чтобы показать, как по данной бинарной строке длины п построить следующую бинарную строку такой же длины. Для этого достаточно найти в строке первый 0 справа, заменить его на 1, а все элементы справа от новой единицы заменить на 0. Как говорилось ранее, формирование сочетаний не является лексикографическим упорядочением объектов множества, а представляет собой лексикографическое упорядочение бинарных строк.
ПРИМЕР 8.47. Пусть S = {a, b, с, d, е}. Найдем сочетание, следующее за {a,d,е}. Поскольку сочетанию {a, d, е} соответствует бинарная строка 10011, следующей строкой будет 10100. Поэтому следующим сочетанием будет {a,с}.
Рассмотрим, наконец, как сформировать все сочетания по r элементов, выбранных из элементов линейно упорядоченного множества S. Снова воспользуемся лексикографическим упорядочением элементов множества S. Как и ранее, не ограничивая общности, можем положить S = {1,2,3,...n}. Будем предполагать, что сочетания перечислены в возрастающем порядке. Как и для перестановок, покажем, как по данному сочетанию найти следующее согласно лексикографическому порядку. Предположим, что n = 5 и r = 3. Если можно увеличить последнюю цифру, то так и будем делать. Поэтому, если имеем число 123, то его можно заменить на 124. Если имеем 125, то последнюю цифру увеличивать нельзя. Отсюда переходим к следующей цифре и смотрим, можно ли ее увеличить. В данном случае это можно сделать, поменяв 2 на 3. Однако мы стремимся построить наименьшее число из тех, которые больше 125. Поэтому увеличиваем последнюю цифру на 1. Имеем первые две две цифры 1 и 3, значит, следующее число — 134. Предположим, что имеется число 145. Последнюю и предпоследнюю цифры нельзя увеличивать. Однако. 1-ю цифру увеличить можно, поэтому увеличиваем до 2. Чтобы сделать число минимальным, в качестве последних цифр возьмем 3 и 4. В результате получаем число 234.
Если начинать справа, то значение последней цифры будет наибольшим возможным, если оно равно n = n - r + r. Если последняя цифра — наибольшая возможная, то предыдущая цифра будет наибольшей возможной, если она равна n - r + (r - 1) или n - r + i, где r = r - 1 — позиция предыдущей цифры. В общем случае, значение каждой i-ой цифры будет наибольшим возможным, если цифры справа — наибольшие возможные, и это значение равно n - r + i. Для формирования максимальной перестановки идем справа налево и проверяем, равно ли значение i-ro элемента величине n - r + i. Первое значение, которое не удовлетворяет этому условию, можно увеличить. Например, это значение т на j-ом месте. Увеличиваем m на 1 и затем увеличиваем значение каждого элемента. стоящего после j-ro, на 1 значения предыдущего элемента. В результате
приходим к следующей процедуре:
Процедура r-сочетание (п, г)
Начиная справа, найти первую цифру со значением аi, таким что ai ≠ n - r + i;
Прибавить единицу к этому значению ai;
Начиная с ai+1, увеличиваем его значение на 1;
Конец процедуры.