Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Teoretichesky_obzor_po_teme_Rekursia.pdf
Скачиваний:
12
Добавлен:
30.03.2015
Размер:
306.77 Кб
Скачать

Использование рекурсии является красивым приёмом программирования. Рекурсивные версии программ, как правило, гораздо короче и нагляднее. В то же время в большинстве практических задач этот приём неэффективен с точки зрения расходования таких ресурсов ЭВМ, как память и время исполнения программы. Использование рекурсии увеличивает время исполнения программы и зачастую требует значительного объёма памяти для хранения копий подпрограммы на рекурсивном спуске. Поэтому на практике разумно заменять рекурсивные алгоритмы на итеративные. А любой рекурсивный алгоритм можно преобразовать в эквивалентный итеративный (то есть использующий циклические конструкции).

Метод перебора с возвратом

Рассмотрим алгоритм перебора с возвратом (backtracking) на примере задачи о прохождении лабиринта.

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

Для получения программы решения этой задачи нужно решить две проблемы:

как организовать данные;

как построить алгоритм.

Информацию о форме лабиринта будем хранить в квадратной матрице LAB символьного типа размером N x N, где N — нечетное число (чтобы была центральная точка). На профиль лабиринта накладывается сетка так, что в каждой ее ячейке находится либо стена, либо проход.

12

Матрица отражает заполнение сетки: элементы, соответствующие проходу, равны пробелу, а стене — какому-нибудь символу (например, букве М).

Путь движения по лабиринту будет отмечаться символами +.

Например, приведенный выше рисунок (в середине) соответствует следующему заполнению матрицы LAB:

М М М М М М М М М М М

М

 

+

+

+

 

 

М

 

 

 

М

М

+

М

 

М

 

М

 

 

М

М

 

+

М

+

М

М

М

 

 

М

М

М

+

М

+

М

 

 

 

 

М

М

М

+

М

+

+

 

М

М

М

М

М

М

+

М

М М М

М

М М

М

М

М

+

+

+

 

 

 

 

 

М

М

 

 

М

+

М

М

М

 

М

М

М

М

М

М

+

+

+

+

+

 

М

М

 

 

 

М

М

М

М

+

М

М

Исходные данные — профиль лабиринта (исходная матрица LAB без крестиков); результат — все возможные траектории выхода из центральной точки лабиринта (для каждого пути выводится матрица LAB с траекторией, отмеченной крестиками).

Алгоритм перебора с возвратом еще называют методом проб. Суть метода:

1.Из каждой очередной точки траектории просматриваются возможные направления движения в одной и той же последовательности (например, вверх- вниз-вправо-влево); шаг производится в первую же обнаруженную свободную соседнюю клетку; клетка, в которую сделан шаг, отмечается крестиком.

2.Если из очередной клетки дальше пути нет (тупик), то следует возврат на один шаг назад и просматриваются еще не испробованные пути движения из этой точки; при возвращении назад покинутая клетка отмечается пробелом.

3.Если очередная клетка, в которую сделан шаг, оказалась на краю лабиринта (на выходе), то на печать выводится найденный путь.

Программа строится методом последовательной детализации.

Первый этап детализации:

Program Labirint;

13

Const NN=30; {максимальный размер лабиринта NNxNN клеток}

Type Field=Array[1..NN,1..NN] Of Char; Var LAB:Field;

N,M:Byte; {заданный размер лабиринта}

...

Procedure GO(X,Y:Integer); Begin

{Поиск путей из центра лабиринта до края – каждый найденный путь печатается}

End;

Begin

{Ввод лабиринта}

GO(N Div 2, M Div 2) {начинаем с середины} End.

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

Запишем сначала общую схему процедуры без детализации:

Procedure GO(X,Y:Integer); Begin

If {клетка (x,y) свободна} Begin

{шаг на клетку (x,y)}

If {дошли до края лабиринта} Then {печатается найденный путь}

Else {попытка сделать шаг в соседние клетки в условленной последовательности}

{возвращение на один шаг назад} End

End.

Для вывода найденных траекторий составляется процедура PRINTLAB.

В окончательном виде программа будет выглядеть так: 14

Program Labirint;

Const NN = 30; {Максимально возможный размер лабиринта} Type Field = Array[1..NN,1..NN] Of Char;

Var Lab : Field; {лабиринт}

N,M : Byte; {заданный размер лабиринта} I,J : Byte;

S : String;

Procedure PrintLab; {Вывод найденных траекторий} Var I,J : Byte;

Begin

writeln (‘-------------- <Enter>‘); For I:=1 To N Do Begin

For J:=1 To M Do Write(LaB[I,J]);

WriteLn; End; WriteLn; ReadLn

End;

Procedure Go(X,Y: Byte);

Begin

 

If Lab[X,Y]=' ' Then

{если клетка свободна}

Begin

 

Lab[X,Y]:='+';

{делается шаг}

If (X=1) Or (X=N) Or (Y=1) Or (Y=M) {край}

Then PrintLab

{печатается путь}

Else Begin

{поиск следующего шага}

Go(X+1,Y);

 

Go(X-1,Y);

 

Go(X,Y+1);

 

Go(X,Y-1);

 

End;

 

Lab[X,Y]:=' '

{возвращение назад}

End;

 

End;

 

15

Begin

{Ввод лабиринта}

Write('Введите размерность лабиринта по вертикали '); ReadLn(N);

Write('Введите размерность лабиринта по горизонтали '); ReadLn(M);

WriteLn('Введите лабиринт по строкам, стены символом М’, проходы - пробелом');

For I:=1 To N Do Begin ReadLn(S);

For J:=1 To M Do

Lab[I,J]:=S[J] End;

Go(N div 2,M div 2) End.

Схема алгоритма данной программы типична для метода перебора с возвратом. По аналогичным алгоритмам решаются, например, популярные задачи об обходе шахматной доски фигурами или о расстановке фигур на доске так, чтобы они «не били» друг друга; множество задач оптимального выбора (задачи о коммивояжере, о рюкзаке, об оптимальном строительстве дорог и т.п.).

Пример 2.1.. Заданы N различных натуральных чисел. Выбрать из этих чисел такие, чтобы их сумма равнялась заданному числу Z. Вывести все возможные решения.

Например, N=6: 1, ,9, 3, 2, 5, 6. При Z=10:

19

13 6

3 2 5

Const NN = 30;

Type Mass = array[1..NN] Of Word; DopMass = Array[1..NN] Of Char;

Var A : Mass;

D : DopMass;

16

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]