Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
REC(3S)X.DOC
Скачиваний:
5
Добавлен:
14.08.2019
Размер:
167.42 Кб
Скачать

Задача: фишку можно продвинуть за один шаг на одну или на две клетки вперед (по линии), сколько существует способов продвижения фишки на n клеток вперед?

Fib(1)=1, Fib(2)=2,

Fib(n)= Fib(n-2){если последний шаг был на две клетки}

+ Fib(n-1){если он был на одну клетку} для n>2.

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

FUNCTION Fib(n:INTEGER):INTEGER; BEGIN

IF n<3 THEN Fib:=n ELSE Fib:=Fib(n-2)+Fib(n-1) END;

Ясно, что этот алгоритм не может быть хорошим. Он многократно теряет время на повторные перевычисления чисел Фибоначчи (в соответствии в деревом подзадач).

Если немного обобщить средства определения функций:

<Fib(1),Fib(2)>= <1,2>,

<Fib(n-1),Fib(n)>= <Fib(n-1),Fib(n-2)+Fib(n-1)> для n>2,

то получим почти то же самое, что известно как индуктивный алгоритм вычисления чисел Фибоначчи. На ObjectPascal такую функцию можно описать так:

TYPE TPair= RECORD p,c:INTEGER END;

FUNCTION FibPair(m:INTEGER):TPair; VAR r:TPair;

BEGIN IF m<3 THEN BEGIN FibPair.p:=1; FibPair.c:=2 END

ELSE BEGIN r:=FibPair(m-1);

FibPair.p:=r.c; FibPair.c:=r.p+r.c END END

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

FUNCTION Fib2(m,FibP,FibC:INTEGER):INTEGER;

BEGIN IF m<2 THEN Fib2:=FibP ELSE

IF m=2 THEN Fib2:=FibC

ELSE Fib2:=Fib2(m-1,FibC,FibP+FibC) END;

Все три приведенные реализации вычисления чисел Фибоначчи заметно по-разному организуют процесс вычисления (*):

  • в первой – многократно выполняются повторные вычисления, во второй и третьей – нет повторных вычислений;

  • в первой и второй – основные вычисления выполняются на этапе реализации плана вычислений (на подъеме), в третьей – в процессе построения плана вычислений (на спуске).

  • Задача «Обратить список» (без обращения его элементов – тоже возможно списков).

Такую задачу мы уже решали, но на файлах. Схема тогда использованного алгоритма: «Взять первый»; «Обратить остаток»; «Добавить первый в конец».

FUNCTION Обратить(L:TSpisok):TSpisok;

BEGIN IF EQ(L,NIL) THEN Обратить:=NIL ELSE

Обратить:=Добавить(Обратить(CDR(L)),CAR(L))END;

FUNCTION Добавить(L:TSpisok;

y:(TSpisokTAtom)):TSpisok;

BEGIN IF EQ(L,NIL) THEN Добавить:=CONS(y,NIL) ELSE

Добавить:=CONS(CAR(L),Добавить(CDR(L),y)) END;

Оценим, сложность этого алгоритм. Видимо приемлемо точная и естественная оценка времени работы алгоритма соответствует размеру дерева подзадач (как функции от n – длины входного списка). В итоге получим оценку ~n*n.

Алгоритм решения этой задачи с такой оценкой времени видимо не является достаточно хорошим.

  • «Непроизводительные» затраты времени этого алгоритма связаны с функцией «Добавить».

  • Необходимость использовать функцию «Добавить» возникла в связи с тем, что базовая функция CONS позволяет добавить элемент в начало списка, а нам потребовалось добавление в конец. Доступ к компонентам списка – стековый. А доступ к компонентам файла как у очереди: просмотр – начиная с одного конца, а добавление – с другого.

  • Добавление элемента в конец списка фактически реализовано полным перестроением списка.

  • Интуитивно ясно, что сконструировать обращенный список можно, используя ~n операций CONS. Такой алгоритм можно получить, используя прием с накапливающими параметрами.

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