
- •Утверждаю Зам.Директора по учебной работе
- •Основы Алгоритмизации и программирования учебно-методическое пособие
- •220301 Автоматизированные системы обработки информации и правления
- •Введение
- •Основные этапы решения задач на эвм
- •Глава 1 способы записи алгоритма
- •1.1 Алгоритм и его свойства
- •Схемы алгоритма
- •1.2 Структуры алгоритмов
- •1.2.1 Алгоритм линейной структуры
- •1.2.2 Алгоритм разветвляющейся структуры
- •1.2.3 Алгоритм циклической структуры
- •1.2.4 Алгоритм со структурой итерационных циклов
- •1.2.5 Алгоритм со структурой вложенных циклов
- •Глава 2 программа на языке высокого уровня
- •2.1 Системы программирования
- •2.2 Характеристика языка программирования Паскаль
- •2.3 Алфавит и структура программы на Паскале Алфавит программы
- •Структура программы
- •Глава 3 Стандартные типы данных
- •3.1 Данные. Типы
- •3.2 Вещественные типы
- •3.3 Целочисленные типы
- •3.4 Символьный тип
- •3.5 Логический тип
- •4 Представление основных структур программирования: итерация, ветвление, повторение
- •4.1 Линейная структура (следование)
- •Var X,y,f: real;
- •4.2 Разветвляющая структура (ветвление)
- •4.3 Циклическая структура (повторение)
- •4.3.1 Оператор цикла с параметром
- •I : Integrer; {номер числа }
- •4.3.2 Оператор цикла с постусловием
- •I,n: integer;
- •4.3.3 Оператор цикла с предусловием
- •4.3.4 Итерационные циклы
- •Var r,a:real;
- •Приближенное вычисление функций
- •Решение уравнений приближенными методами
- •Метод деления отрезка пополам
- •Xsl, Xpr, a, b, e, y1, y2, Lev, Prav, y: Real;
- •Метод Ньютона
- •Метод прохождения отрезка с переменным шагом
- •Вычисление определенных интегралов
- •1. Метод прямоугольников
- •X: Real;
- •2. Метод трапеций
- •X: Real;
- •Глава 5 Типы данных, определяемые пользователем
- •5.1 Пользовательский тип данных
- •5.1.1 Типизированные константы
- •5.1.2 Перечисляемый тип
- •I:1..6; loto: num;
- •5.2 Массивы
- •I : integer;
- •5.2.1. Работа с одномерными массивами
- •I,sum : integer;
- •Var a: array [1..N] of real;
- •Var I,s,r: integer;
- •I : list;
- •I : integer;
- •X : mass;
- •I, j, p, n, m, k:integer;
- •I, j, k, nd : integer;
- •Xmin : real;
- •X : mass;
- •Var I, j, nd : integer;
- •X : mass;
- •5.2.2 Работа с двумерными массивами( матицы)
- •Var I,j,n : integer;
- •I,j,n,m : integer;
- •5.2.3 Сортировка массивов
- •Сортировка методом "пузырька"
- •X : Array [1..Nmax] Of Real;
- •X : Array [1..Nmax] Of Real;
- •Сортировка выбором
- •Обменная сортировка
- •Var m:array[1..1000] of integer;
- •I,z,n:integer; Key:byte;
- •Сортировка слиянием
- •Var { Описание массивов и переменных}
- •X, y: array[1..1000] of integer;
- •5.3 Строковые типы
- •Var s: string[10];
- •5.3.1 Операции над строками
- •5.3.2 Стандартные процедуры и функции для строк
- •Функция Length
- •Функция Upcase
- •Функция Copy
- •Функция Роs
- •I, n, p: integer;
- •I: integer;
- •I: integer;
- •Insert (word2, text, I);
- •Insert (chr (k-32), t, I);
- •Insert (chr (k-80), t, I);
- •Insert (‘е’, t, I);
- •Глава 6 Процедуры и функции
- •6.1 Процедуры
- •I : Integer;
- •I, n: integer;
- •Input _ mas (k, n);
- •I,n : Integer;
- •I,k : Integer;
- •6.2 Функции
- •I:Integer;
- •2) Массивы;
- •I,n : Integer;
- •I : Integer;
- •I,tn,tk:Real;
- •Глава 7 Программирование рекурсивных алгоритмов
- •7.1 Понятие рекурсии
- •7.2 Техника построения рекурсивных алгоритмов
- •7.3 Формы рекурсий
- •If Prim(I) then
- •7.4 Рекурсия и итерация
- •7.5 Программирование с использованием рекурсии
- •Var p: Integer;
- •Var X, y: Integer; begin
- •Var z: Real; begin
- •Var I:integer; j:real;
- •Глава 8 Файлы
- •8.1 Текстовые файлы
- •I,n : Integer; {Вспомогательные переменные}
- •8.2 Типизированные файлы
- •X,m,s : Real;
- •8.3 Нетипизированные файлы
- •Глава 9 Записи
- •9.1 Описание записи
- •I: integer;
- •9.2 Оператор присоединения
- •I, j, k, m : integer;
- •X: real;
- •9.3 Вложенные записи
- •9.4 Записи с вариантами
- •Information: record
- •I, k, n : integer;
- •Vedom : Array [1..Nmax] Of Stud;
- •I,j : Integer;
- •Vedom : File Of Stud;
- •Vedom : File Of Stud;
- •I,j,kdv,k2 : Integer;
- •If Not Eof (Ftel) Then
- •If Not Eof(Ftel) then
- •If Not Eof(FilComp) then
- •Глава 10 Динамические структуры данных
- •10.1 Распределение памяти при выполнении программ
- •Верхняя граница памяти ms-dos
- •10.2 Ссылочные переменные
- •10.3 Процедуры управления кучей
- •10.4 Использование переменных ссылочного типа
- •I: Integer;
- •I, k : Integer;
- •Val(b, k, code);{Превратили второй символ в ч исло}
- •10.5 Списки
- •Var Ch : Char;
- •Var Ch : Char;
- •10.6 Деревья
- •10.7 Константы ссылочного типа
- •Глава 11. Язык Паскаль. Графический модуль Graph Список используемой литературы Основная
- •Дополнительная
7.2 Техника построения рекурсивных алгоритмов
В общем случае для правильной организации рекурсивных алгоритмов необходимо выполнение двух условий:
должно быть найдено представление общей задачи в терминах «более простой» задачи того же класса, которое определит последовательность рекурсивных вызовов;
рекурсивные вычисления не должны создавать бесконечную цепочку вызовов; для этого, во-первых, алгоритм должен включать хотя бы одно предписание, в котором при определенных условиях вычисление производится непосредственно, без рекурсивного вызова {терминальную ситуацию), а во-вторых, рекурсивные построения в конце концов должны сводиться к этим простым терминальным случаям.
В общем виде рекурсивное описание подпрограммы должно иметь одну из следующих структур или некоторую эквивалентную форму:
if <условие> then
<терминальная ситуация> else
<рекурсивные вызовы>
или
while <условие> do
begin
<рекурсивные вызовы>
end; <терминальная ситуация>
Существует два разных стиля построения рекурсивных алгоритмов, называемые восходящей и нисходящей рекурсией. Нисходящая рекурсия последовательно разбивает данную задачу на более простые, пока не доходит до терминальной ситуации. Только после этого она начинает строить ответ, а промежуточные результаты передаются обратно вызывающим функциям.
Пример 7.1 Вычисление факториала.
{Нисходящая рекурсия} Program Factorial;
Var
n : byte;
Function Fact(n : byte): longint;
Begin {Fact}
if n = 0 then
fact := 1 {Терминальная ветвь }
else
fact := n*fact(n-1){Рекурсивная ветвь }
End; {Fact}
Begin {Factorial}
Writeln('Введите n');
ReadLn(n);
Writeln('Факториал', n:2, '=', fact(n))
End. {Factorial}
Вызов, например, fact(5) означает, что функция fact вызывает себя раз $а разом: fact (4), fact(3),... до тех пор, пока не будет достигнута терминальная ситуация. При каждом вызове текущие вычисления «откладываются», локальные переменные и адрес возврата сохраняются в стеке. Tерминальная ситуация fact := 1 достигается при п = 0. По достижении терминальной ситуации рекурсивный спуск заканчивается, начинается рекурсивный возврат изо всех вызванных на данный момент копий функции: начинает строиться ответ: n*fact(n-1), сохраненные локальные параметры выбираются из стека в обратной последовательности, а получаемые промежуточные результаты: 1*1, 2*1, 3*2*1, 4*3*2*1, 5*4*3*2*1 -передаются вызывающим функциям. Латинское recurrere означает «возвращение назад».
В восходящей рекурсии ответ строится на каждой стадии рекурсивного вызова, получаемые промежуточные результаты вычисляются перед рекурсивным вызовом и передаются в виде дополнительного рабочего параметра подпрограммы до тех пор, пока не будет достигнута терминальная ситуация. К этому моменту ответ уже готов и нужно только передать его вызывающей функции верхнего уровня.
Пример 7.2 Вычисление факториала.
{Восходящая рекурсия} Program Factorial;
Var
N : byte;
Function Fact (n : byte, w: longint): longint;
Begin {Fact}
if n = 0 then
fact := w {Терминальная ветвь }
else
fact := fact(n-1, n*w) {Рекурсивная ветвь }
End; {Fact}
Begin {Factorial}
Writeln('Введите N');
ReadLn(N):
WriteLn(‘Фaктopиaл', N:2, '=', fact(N, 1))
End. {Factorial}
Здесь w - рабочий параметр, применяемый для формирования результата. При первом вызове функции этот параметр надо инициализировать (придать ему начальное значение - 1), далее при каждом рекурсивном вызове, например при вычислении 5!, он принимает последовательно значения: 5*1,4*5*1, 3*4*5*1, 2*3*4*5*1, 1*2*3*4*5*1.
Сравнивая нисходящий и восходящий варианты рекурсивного определения факториала, видим: результат вычисляется в разном порядке. Поскольку умножение коммутативно, это не влияет на окончательный ответ. Однако есть классы задач, при решении которых программисту требуется сознательно управлять ходом работы рекурсивных процедур и функций. Такими, в частности, являются задачи, использующие списковые и древовидные структуры данных. Например, при разработке трансляторов применяются так называемые атрибутированные деревья разбора, работа с которыми требует от программиста умения направлять ход рекурсии: одни действия можно выполнить только на спуске, другие -только на возврате. Поэтому понимание рекурсивного механизма и умение управлять им - это необходимые качества квалифицированного программиста.
В следующем примере показана рекурсивная процедура с выполнением действий как до, так и после рекурсивного вызова (с выполнением действий как на рекурсивном спуске, так и на рекурсивном возврате).
Пример 7.3 Счет от и до 1 на рекурсивном спуске и от 1 до и на рекурсивном возврате. При этом видно, как заполняется и освобождается стек.
{Выполнения рекурсивных действий до и после рекурсивного вызова}
Program Stack; Var
n : integer;
Procedure Rekursion (i: integer);
Begin {Rekursion}
WriteLn(i:30); {Вывод на рекурсивном спуске }
If i>1
then
Rekursion(i-1);
WriteLn(i:3); {Вывод на рекурсивном возврате}
End; {Rekursion}
Begin {Stack}
WriteLn ('Введите n:');
ReadLn(n);
WriteLn;
WriteLn ('Рекурсия:');
Rekursion(n);
End. {Stack}
В процедуре Rekursion операция WriteLn(i:30) выполняется перед рекурсивным вызовом, после чего WriteLn(*:3) освобождает стек. Поскольку рекурсия выполняется от и до 1, вывод по WriteLn(i:30) выполняется в обратной последовательности: п, п-1, ..., 1, а вывод по writeln(i:3) -в прямой: 1, 2,..., п (согласно принципу LIFO - «последним пришел, первым обслужен»).
Возможная глубина рекурсивных вычислений определяется размером используемого стека. Насколько велик стек, можно установить с помощью бесконечной рекурсии. Причем использование директивы {$S+} при переполнении стека приведет к прерыванию программы с выдачей сообщения «Error 202: stack overflow error» («Ошибка 202: переполнение стека»).
Пример 7.4 Определение размера стека.
{Программа проверки размера стека}
Program Stack_test;
{$S+} {Включить контроль переполнения стека}
Procedure proc(i: integer);
Begin {proc}
if i mod 1024 = 0 then
WriteLn(i:6);
proc(i+l);
End; {proc}
Begin {Stackjest}
proc(l);
End. {Stackjest}
Стек связан с другой структурой памяти - с динамической облает С помощью директивы {$М} можно управлять размером стека.