- •Проектирование полнопереборных алгоритмов
- •Содержание
- •1.1. Основные понятия и определения
- •1100, 1010, 1001, 0110, 0101, 0011.
- •1.2. Общие подходы к порождению комбинаторных объектов
- •1.3. Алгоритмы порождения подмножеств
- •1.4. Алгоритмы порождения сочетаний
- •1.5. Алгоритмы порождения перестановок
- •1.6. Алгоритм порождения размещений
- •1.7. Алгоритмы порождения композиций
- •1.8. Алгоритм порождения разбиений
- •2. Проектирование алгоритмов, основанных на полном переборе траекторий задачи выбора
- •2.1. Понятие задачи выбора
- •2.2. Комбинаторный поиск
- •Поиск с возвращением
- •Метод решета
- •2.3. Использование алгоритмов порождения элементарных комбинаторных объектов при проектировании полнопереборных алгоритмов решения задач выбора
- •3. Некоторые вопросы теории сложности
- •4. Задания для самостоятельной работы
- •4.1. Порождение подмножеств
- •4.2. Порождение перестановок
- •4.3. Порождение сочетаний и размещений
- •4.4. Порождение композиций и разбиений
- •4.5. Решение комбинаторных задач
- •4.6. Проектирование полно переборных алгоритмов
- •Список литературы
- •Генерация случайных чисел
- •Приложение 2 функции времени языка Turbo Pascal
- •Текст программы на языке Turbo Pascal реализующей точный алгоритм решения задачи о рюкзаке
- •Проектирование полнопереборных алгоритмов
- •308012, Белгород, ул. Костюкова, 46.
1.2. Общие подходы к порождению комбинаторных объектов
При разработке алгоритмов часто возникает задача порождения комбинаторных объектов.
При порождении комбинаторных объектов существуют две возможные цели - систематическое порождение всех возможных конфигурация и порождение равномерно распределенных случайных конфигураций.
А
лгоритм
систематического порождения обычно
строится по следующей схеме (1-я схема):
Замечание 1. При такой схеме необходимо учитывать, что алгоритм предпринимает еще одно преобразование после того, как выведен последний объект.
На практике иногда желательно, чтобы изменения текущего объекта при переходе к следующему были, возможно, малыми. Такие алгоритмы называются алгоритмами минимального изменения.
Случайное порождение комбинаторных объектов часто затруднено тем, что требуется равновероятность всех возможных конфигураций. В этом случае можно задать определенное взаимно однозначное соответствием между целыми числами 0,1,…,N-1 и N объектами, тогда случайное порождение осуществляется случайным порождением целого числа от 0 до N-1 и обращением его в объект. Такой же подход пригоден и для систематического порождения комбинаторных объектов. Систематическое порождение сводится к перечислению целых чисел от 0 до N-1 и обращением каждого числа в объект (2-я схема).
Замечание 2. Процесс обращения может быть долгим, тогда его следует избегать, если это возможно.
1.3. Алгоритмы порождения подмножеств
Как было сказано выше между n-разрядными двоичными наборами (целыми числами от 0 до 2n-1) и подмножествами n-элементного множества существует взаимно однозначное соответствие. Т.е. порождение подмножеств множества S={s1,s2,...,sn} эквивалентно порождению n-разрядных двоичных наборов.
Наиболее прямым способом порождения всех двоичных наборов длины n является счет в системе счисления с основанием 2, что реализует алгоритм 1. Здесь и далее при записи алгоритмов используются следующие обозначения:
- оператор присваивания.
- оператор транспозиции, т.е. выполнение оператора ab при a=3 и b=7 приводит к результату a=7, b=3.
-
составной оператор.
for i=начальное значение to конечное значение do оператор – оператор цикла с фиксированным числом шагов, значение счетчика i увеличивается на единицу.
for i=начальное значение downto конечное значение do оператор – оператор цикла с фиксированным числом шагов, значение счетчика i уменьшается на единицу.
while условие do оператор – оператор цикла с предусловием.
вывести(список вывода) – оператор вывода.
{} – комментарий.
В алгоритме 1 двоичные наборы длины n формируются в ячейках
(bn-1,bn-2,...,b0), ячейка bn принимает значение 1, когда все 2n наборов выданы. Т.е. замечание 1 учтено тем, что при выборе начальной конфигурации инициализируется нулем n+1 ячеек памяти.

Задача порождения случайного подмножества сводится к задаче порождения случайного двоичного набора длины n. Она решается путем генерации случайного числа от 0 до 2n-1 и обращением его двоичного представления в набор (bn-1,bn-2,...,b0). Обращение осуществляется с помощью битовых операций сдвига и не является трудоемким, следовательно, замечание 2 в данном случае не имеет силы.
Использовать данное обращение можно и при порождении всех двоичных наборов, как в алгоритме 2.

Другим комбинаторным объектам также соответствуют целые числа, но процесс обращения для них уже будет достаточно долгим. Поэтому, далее алгоритмы, основанные на использовании данного обращения, не рассматриваются. Как учитывается замечание 1, оставляется на самостоятельную проработку4.
Рассмотрим алгоритм порождения подмножеств в порядке минимального изменения. Минимальным возможным изменением при переходе от одного подмножества к другому является добавление или удаление одного элемента множества. В терминах двоичных наборов это означает, что последовательные наборы должны различаться в одном разряде. Такие последовательности двоичных наборов называются кодами Грея; более точно, n-разрядный код Грея есть упорядоченная циклическая последовательность 2n n-разрядных наборов (кодовых слов), такая, что последовательные слова отличаются в одном разряде.
Пример 3-разрядного кода Грея приведен в табл. 2.
Коды Грея удобно задавать начальным словом и последовательностью переходов, т.е. упорядоченным списком номеров разрядов (пронумерованных справа налево), которые меняются при переходе от одного кодового слова к другому. Так для приведенного в табл. 2 кода G(3) начальное слово (000), а последовательность переходов будет иметь вид Т3=1,2,1,3,1,2,1.
Код Грея используется при построении различных преобразователей. Аналоговая величина - код, где он позволяет свести к единица младшего разряда ошибку неоднозначности при считывании информации.
Таблица 2
|
№ |
КОД | |
|
Двоичный В(3) |
Грея G(3) | |
|
1 |
000 |
000 |
|
2 |
001 |
001 |
|
3 |
010 |
011 |
|
4 |
011 |
010 |
|
5 |
100 |
110 |
|
6 |
101 |
111 |
|
7 |
110 |
101 |
|
8 |
111 |
100 |
Существует много n-разрядных кодов Грея. Рассмотрим так называемый двоично-отраженный код Грея.
Пусть

есть n-разрядный код Грея, записанный в форме двоичной 2nn матрицы так, что 1-я строка матрицы является 1-м кодовым словом - Gi. Дадим рекурсивное определение кода

Пусть
есть последовательность переходов для
n-разрядного
кода, тогда можно дать рекурсивное
определение последовательности
переходов.
1) Т1=1,
2)
Tn+1=Tn,n+1,
.
Следует
отметить, что последовательности
переходов Tn
и
одинаковы (доказывается по индуктивности).
Поэтому данное рекурсивное определение
упрощается:
1) T1=1,
2) Tn+1=Tn,n+1,Tn.
Итак, для порождения кода Грея достаточно уметь порождать последовательность его переходов. Последовательность переходов можно порождать итеративно, используя стек. Вначале стек содержит элементы n,n-1,...,1 (с 1 в вершине). Затем верхний элемент стека – i выталкивается и помещается в последовательность переходов, после этого в стек добавляются элементы i-1,i-2,...,1. Процесс повторяется пока стек не пуст (см. алгоритм 3).
Для организации стека S можно использовать массив и переменную t, следящую за вершиной стека. Пусть для S отведены ячейки S(1), S(2),...,S(m), тогда пустой стек соответствует случаю t=0, и операции включения x в стек S (обозначение Sх) и исключения x из стека S (обозначение хS) будут следующими:
Sx tt+1
If t>m then overflow
else S(t)x
xS if t=0 then underflow
else![]()
Здесь процедура overflow подразумевает действия, которые необходимо выполнить при переполнении стека, а underflow - при попытке извлечь элемент из пустого стека.

