Смекни!
smekni.com

Перебор с возвратом (стр. 1 из 3)

Министерство образования Республики Беларусь

Учреждение образования

«Гомельский государственный университет им. Ф. Скорины»

Математический факультет

Кафедра МПУ

Курсовая работа

Перебор с возвратом

Исполнитель:

Студентка группы М-32

Ловренчук А.Ю.

Научный руководитель:

Канд. физ-мат. наук, доцент

Звягина Т.Е.

Гомель 2007


Введение

Даны N упорядоченных множеств U1, U2, ..., UN (N - известно), и требуется построить вектор A=(a1, a2, ..., aN), где a1ÎU1, a2ÎU2, ..., aNÎUN, удовлетворяющий заданному множеству условий и ограничений.

В алгоритме перебора вектор А строится покомпонентно слева направо. Предположим, что уже найдены значения первых (k-1) компонент, А=(а1, а2, ..., а(k-1), ?, ..., ?), тогда заданное множество условий ограничивает выбор следующей компоненты аk некоторым множеством SkÌUk. Если Sk<>[ ] (пустое), мы вправе выбрать в качестве аk наименьший элемент Sk и перейти к выбору (k+1) компоненты и так далее. Однако если условия таковы, что Sk оказалось пустым, то мы возвращаемся к выбору (k-1) компоненты, отбрасываем а(k-1) и выбираем в качестве нового а(k-1) тот элемент S(k-1), который непосредственно следует за только что отброшенным. Может оказаться, что для нового а(k-1) условия задачи допускают непустое Sk, и тогда мы пытаемся снова выбрать элемент аk. Если невозможно выбрать а(k-1), мы возвращаемся еще на шаг назад и выбираем новый элемент а(k-2) и так далее.

Графическое изображение - дерево поиска. Корень дерева (0 уровень) есть пустой вектор. Его сыновья суть множество кандидатов для выбора а1, и, в общем случае, узлы k-го уровня являются кандидатами на выбор аk при условии, что а1, а2, ..., а(k-1) выбраны так, как указывают предки этих узлов. Вопрос о том, имеет ли задача решение, равносилен вопросу, являются ли какие-нибудь узлы дерева решениями. Разыскивая все решения, мы хотим получить все такие узлы.

Рекурсивная схема реализации алгоритма.

procedure Backtrack(вектор,i);

begin

if <вектор является решением> then <записать его>

else begin <вычислить Si>;

for aÎSi do Backtrack(векторêêa,i+1);

{êê- добавление к вектору компоненты}

end;

end;

Оценка временной сложности алгоритма. Данная схема реализации перебора приводит к экспоненциальным алгоритмам. Действительно, пусть все решения имеют длину N, тогда исследовать требуется порядка çS1ç*çS2ç*...*çSNç узлов дерева. Если значение Si ограничено некоторой константой С, то получаем порядка СN узлов.

1. Задача о расстановке ферзей

На шахматной доске N*N требуется расставить N ферзей таким образом, чтобы ни один ферзь не атаковал другого.

Отметим следующее. Все возможные способы расстановки ферзей - СNN^2 (около 4,4*109 для N=8). Каждый столбец содержит самое большее одного ферзя, что дает только NN расстановок (1,7*107 для N=8). Никакие два ферзя нельзя поставить в одну строку, а поэтому, для того чтобы вектор (а1, а2, ..., aN) был решением, он должен быть перестановкой элементов (1, 2, ..., N), что дает только N! (4,0*104 для N=8) возможностей. Никакие два ферзя не могут находиться на одной диагонали, это сокращает число возможностей еще больше (для N=8 в дереве остается 2056 узлов). Итак, с помощью ряда наблюдений мы исключили из рассмотрения большое число возможных расстановок N ферзей на доске размером N*N. Использование подобного анализа для сокращения процесса перебора называется поиском с ограничениями или отсечением ветвей в связи с тем, что при этом удаляются поддеревья из дерева. Второе. Другим усовершенствованием является слияние, или склеивание, ветвей. Идея состоит в том, чтобы избежать выполнения дважды одной и той же работы: если два или больше поддеревьев данного дерева изоморфны, мы хотим исследовать только одно из них. В задаче о ферзях мы можем использовать склеивание, заметив, что если а1>éN/2ù, то найденное решение можно отразить и получить решение, для которого а1£éN/2ù. Следовательно, деревья, соответствующие, например, случаям а1=2 и а1=N-1, изоморфны.

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


Структуры данных.

Up:array[2..16] of boolean;{признак занятости диагоналей первого типа}

Down:array[-7..7] of boolean;{признак занятости диагоналей второго типа}

Vr:array[1..8] of boolean;{признак занятости вертикали}

X:array[1..8] of integer;{номер вертикали, на которой стоит ферзь на каждой горизонтали}

Далее идет объяснение “кирпичиков”, из которых “складывается” решение (технология “снизу вверх”).

procedure Hod(i,j:integer); {сделатьход}

begin

X[i]:=j;Vr[j]:=false;Up[i+j]:=false;Down[i-j]:=false;

end;

procedure O_hod(i,j:integer);{отменитьход}

begin

Vr[j]:=true;Up[i+j]:=true;Down[i-j]:=true;

end;

function D_hod(i,j:integer);

{проверка допустимости хода в позицию (i,j)}

begin

D_hod:=Vr[j]andUp[i+j]andDown[i-j];

end;

Основная процедура поиска одного варианта расстановки ферзей имеет вид:

procedure Solve(i:integer;var q:boolean);

var j:integer;

begin

j:=0;

repeat

inc(j);q:=false;{циклповертикали}

if D_hod(i,j) then begin Hod(i,j);

if i<8 then begin Solve(i+1,q);

if not q then O_hod(i,j);

end else q:=true;{решениенайдено}

end;

until q or (j=8);

end;

Возможные модификации.

Поиск всех решений. Длядоски 8*8 ответ 92.

Procedure Solve(i:integer);

var j:integer;

begin

if i<=N then begin

for j:=1 to N do if D_hod(i,j) then begin

Hod(i,j);

Solve(i+1);

O_hod(i,j);

end;

end

elsebegin

Inc(S);{счетчик числа решений, глобальная переменная}

Print;{выводрешения}

end;

end;

Поиск только не симметричных решений. Для доски 8*8 ответ 12.

Эта модификация требует предварительных разъяснений. Из каждого решения задачи о ферзях можно получить ряд других при помощи вращений доски на 90о, 180о и 270о и зеркальных отражений относительно линий , разделяющих доску пополам (система координат фиксирована). Доказано, что в общем случае для доски N*N (N>1) для любой допустимой расстановки N ферзей возможны три ситуации:

· при одном отражении доски возникает новая расстановка ферзей, а при поворотах и других отражениях новых решений не получается;

· новое решение при повороте на 90о и ее отражения дают еще две расстановки;

· три поворота и четыре отражения дают новые расстановки.

Для отсечения симметричных решений на всем множестве решений требуется определить некоторое отношение порядка. Представим решение в виде вектора длиной N, координатами которого являются числа от 1 до N. Для ферзя, стоящего в i-й строке, координатой его столбца является i-я координата вектора. Для того, чтобы не учитывать симметричные решения, будем определять минимальный вектор среди всех векторов, получаемых в результате симметрий. Процедуры Sim1, Sim2, Sim3 выполняют зеркальные отображения вектора решения относительно горизонтальной, вертикальной и одной из диагональных осей. Известно (из геометрии), что композиция этих симметрий дает все определенные выше симметрии шахматной доски, причем не принципиально, в какой последовательности выполняются эти процедуры для каждой конкретной композиции. Проверка «на минимальность» решения производится функцией Cmp, которая возвращает значение true в том случае, когда одно из симметричных решений строго меньше текущего.

{не лучший вариант реализации - отсечение на выводе решений}

type Tarray=array[1..N] of integer;

procedure Sim1(var X:Tarray);

var i:integer;

begin

for i:=1 to N do X[i]:=N-X[i]+1;

end;

procedure Sim2(var X:Tarray);

var i,r:integer;

begin

for i:=1 to N do begin

r:=X[i]; X[i]:=X[N-i+1];X[N-i+1]:=r;

end;

end;

procedure Sim3(var X:Tarray);

var Y:Tarray;

i:integer;

begin

for i:=1 to N do Y[X[i]]:=i;

X:=Y;

end;

function Cmp(X,Y:Tarray):boolean;

var i:integer;

begin

i:=1;

while (i<=N) and (Y[i]=X[i]) do Inc(i);

if i>N then Cmp:=false

else if Y[i]<X[i] then Cmp:=true else Cmp:=false;

end;

Procedure Solve(i:integer);

var j:integer;f:boolean;

Y:Tarray;

begin

if i<=N then begin

for j:=1 to N do if D_hod(i,j) then begin

Hod(i,j);

Solve(i+1);

O_hod(i,j);

end;

end

else begin

f:=true;

for j:=0 to 7 do begin

Y:=X;

if j and 1 =0 then Sim1(Y);

if j and 2 =0 then Sim2(Y);

if j and 4 =0 then Sim3(Y);

if Cmp(Y,X) then f:=false;

end;

iffthenbegin

Inc(S);{счетчик числа решений, глобальная переменная}

Print;{выводрешения}

end;

end;

end;

2. Задача о шахматном коне

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

Разбор задачи начинается с оценки числа 64! - таково общее число способов разметки доски 8*8. Каждую разметку следует оценивать на предмет того, является ли она способом обхода конем доски(решение в “лоб”). Затем оцениваем порядок сокращения перебора исходя из условия - логика выбора очередного хода коня. Будем считать, что поля для хода выбираются по часовой стрелке. Объяснение иллюстрируется следующими рисунками.


Структуры данных.

Const N= ; M= ;

Dx:array[1..8] of integer=(-2,-1,1,2,2,1,-1-2);

Dy:array[1..8] of integer=(1,2,2,1,-1,-2,-2,-1);

Var A:array[-1..N+2,-1..M+2] of integer;

Основной фрагмент реализации - процедура Solve.

procedure Solve(x,y,l:integer);

var z,i,j:integer;

begin

A[x,y]:=l;

if l=N*M then Inc(t)

else for z:=1 to 8 do begin i:=x+Dx[z];j:=y+Dy[z];