ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Государственное образовательное учреждение высшего профессионального образования «ВОРОНЕЖСКИЙ ГОСУДАРСТВЕННЫЙ ПЕДАГОГИЧЕСКИЙ УНИВЕРСИТЕТ» Кафедра информатики и методики преподавания математики Учебно-методический комплект дисциплины: Методы разработки программ Специальность 030100 Информатика УТВЕРЖДАЮ ЗАВЕДУЮЩИЙ КАФЕДРОЙ ПОТАПОВ А.С. «___» _____________ 2009 Г. МЕТОДИЧЕСКИЕ УКАЗАНИЯ К ЛАБОРАТОРНЫМ РАБОТАМ Для студентов заочного отделения Ведущий лектор: Чулюков Владимир Алексеевич, профессор, к.ф.-м.н., доцент ОДОБРЕН НА ЗАСЕДАНИИ КАФЕДРЫ «___» _________ 200 __ Г. ПРОТОКОЛ № _________ Воронеж 2009 1 СТРУКТУРА лабораторных работ по дисциплине «Методы разработки программ» Часть лабораторных работ проводится во время сессионных занятий. Другая часть выносится на самостоятельную работу. Лабораторная работа № 1 ТЕМА: Простые методы сортировки массивов ПРОДОЛЖИТЕЛЬНОСТЬ: 2 часа аудиторных занятий и 8 час. самостоятельной работы. Задание 1. (Выполняется во время аудитороных занятий). Наберите и отладьте программу, реализующую сортировку простыми включениями: program s1; uses crt; const n=10; type item=record key:integer; end; index=0..n; var a:array[0..n] of item; procedure vvod_mass; var i:index; begin randomize; for i:=1 to n do a[i].key:=random(100); end;{of vvod_mass} procedure straightinsertion; var i,j:index; x:item; begin for i:=2 to n do begin x:=a[i]; a[0]:=x;j:=i-1; while x.key<a[j].key do begin a[j+1]:=a[j];j:=j-1; end; a[j+1]:=x; end; end; procedure vivod_mass; var i:index; begin for i:=1 to n do write(a[i].key:4); writeln; 2 end; begin {of main} clrscr; vvod_mass; vivod_mass; writeln('nachat sortirovku? (enter)'); readln; straightinsertion; vivod_mass; writeln('konec sort'); readln end. Вопросы для самопроверки: Сколько чисел сортирует эта программа и откуда это следует? Зачем используется предлолжение uses crt? Объясните роль оператора randomize. Объясните роль оператора random. В каком диапазоне в программе используются случайные целые числа? 5. Объясните роль опреатора clrscr. 6. Объясните роль опреатора readln. 1. 2. 3. 4. Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Выполните программу и засеките время выполнения сортировки (начало – нажатие enter в ответ на вопрос ‘nachat sortirovku?’, конец – появление сообщения ‘konec sort’. Содержание отчета: 1. Текст программы. 2. Время выполнения сортировки для 10000 чисел в секундах. 3. Краткие ответы на вопросы для самопроверки. Задание 2. (Выполняется во время самостоятельной работы). Используя известные процедуры ввода и вывода массива и опираясь на самостоятельное изучение раздела лекций «Сортировка бинарными включениями», наберите и отладьте программу, реализующую эту сортировку. Вопросы для самопроверки: 1. Каким опреатором происходит определение индекса посредине массива? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Выполните программу и засеките время выполнения сортировки 3 Содержание отчета: 1. Текст программы. 2. Время выполнения сортировки для 10000 чисел в секундах. 3. Краткие ответы на вопросы для самопроверки. Задание 3. (Выполняется во время аудитороных занятий). Используя известные процедуры ввода и вывода массива и опираясь на самостоятельное изучение раздела лекций «Сортировка простым выбором», наберите и отладьте программу, реализующую эту сортировку. Вопросы для самопроверки: 1. Какими операторами происходит определение наименьшего элемента оставшейся последовательности? 2. Какими операторами меняются местами наименьший и первый элементы оставшейся последовательности? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Выполните программу и засеките время выполнения сортировки Содержание отчета: 1. Текст программы. 2. Время выполнения сортировки для 10000 чисел в секундах. 3. Краткие ответы на вопросы для самопроверки. Задание 4. (Выполняется во время аудитороных занятий). Используя известные процедуры ввода и вывода массива и опираясь на самостоятельное изучение раздела лекций «Сортировка простым обменом», наберите и отладьте программу, реализующую эту сортировку. Вопросы для самопроверки: 1. Какими операторами происходит сравнение двух соседних элементов массива? 2. Какими операторами меняются местами два соседних элемента массива? 3. Что означает слово «downto»? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Выполните программу и засеките время выполнения сортировки Содержание отчета: 1. Текст программы. 2. Время выполнения сортировки для 10000 чисел в секундах. 3. Краткие ответы на вопросы для самопроверки. 4 Задание 5. (Выполняется во время самостоятельной работы). Используя известные процедуры ввода и вывода массива и опираясь на самостоятельное изучение раздела лекций «Шейкер-сортировка», наберите и отладьте программу, реализующую эту сортировку. Вопросы для самопроверки: 1. Какой участок программы обрабатывает массив слева направо? 2. Какой участок программы обрабатывает массив справа налево? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Выполните программу и засеките время выполнения сортировки Содержание отчета: 1. Текст программы. 2. Время выполнения сортировки для 10000 чисел в секундах. 3. Краткие ответы на вопросы для самопроверки. 5 Лабораторная работа № 2 ТЕМА: Усовершенствованные методы сортировки массивов ПРОДОЛЖИТЕЛЬНОСТЬ: 4 часа аудиторных занятий и 24 час. самостоятельной работы. Задание 1. (Выполняется во время аудитороных занятий). Наберите и отладьте программу, реализующую сортировку включениями с убывающими приращениями (сортировку Шелла): program s2; uses crt; const n=10; type item=record key:integer; end; index=-9..n; var a:array[-9..n] of item; procedure vvod_mass; var i:index; begin randomize; for i:=1 to n do a[i].key:=random(100); end;{of vvod_mass} procedure ShellSort; const t = 4; var i,j,k,s:index; x:item; m:1..t; h: array[1..t] of integer; BEGIN h[1]:=9; h[2]:=5; h[3]:=3; h[4]:=1; FOR m:=1 TO t DO BEGIN k:=h[m]; s:= -k; FOR i:= k+1 TO n DO BEGIN x:=a[i] ; j:=i-k; IF s=0 THEN s:= -k; s:=s+1; a[s]:=x; WHILE x.key<a[j].key DO BEGIN a [j +k]:=a[j]; j:=j-k END; a[j +k]:=x END END END; procedure vivod_mass; var i:index; begin for i:=1 to n do write(a[i].key:4); 6 writeln; end; begin {of main} clrscr; vvod_mass; vivod_mass; writeln('nachat sortirovku? (enter)'); readln; shellsort; vivod_mass; writeln('konec sort'); readln end. Вопросы для самопроверки: 1. Какая последовательность приращений используется в данной программе? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Выполните программу и засеките время выполнения сортировки. 3. Убедившись, что засечь время выполнения сортировки с помощью часов не удается – измените программу так, чтобы время сортировки вычислялось программой с помощью процедуры GetTime модуля Dos: program s3; uses dos, crt; const n=10000; type item=record key:integer; end; index=-9..n; var a:array[-9..n] of item; h, m, s, hund : Word; function LeadingZero(w : Word) : String; var s : String; begin Str(w:0,s); if Length(s) = 1 then s := '0' + s; LeadingZero := s; end; procedure vvod_mass; var i:index; begin randomize; for i:=1 to n do a[i].key:=random(100); end;{of vvod_mass} 7 procedure ShellSort; const t = 4; var i,j,k,s:index; x:item; m:1..t; h: array[1..t] of integer; BEGIN h[1]:=9; h[2]:=5; h[3]:=3; h[4]:=1; FOR m:=1 TO t DO BEGIN k:=h[m]; s:= -k; FOR i:= k+1 TO n DO BEGIN x:=a[i] ; j:=i-k; IF s=0 THEN s:= -k; s:=s+1; a[s]:=x; WHILE x.key<a[j].key DO BEGIN a [j +k]:=a[j]; j:=j-k END; a[j +k]:=x END END END; procedure vivod_mass; var i:index; begin for i:=1 to n do write(a[i].key:4); writeln; end; begin {of main} clrscr; vvod_mass; vivod_mass; writeln('nachat sortirovku? (enter)'); readln; GetTime(h,m,s,hund); Writeln('It is now ',LeadingZero(h),':', LeadingZero(m),':',LeadingZero(s), '.',LeadingZero(hund)); shellsort; GetTime(h,m,s,hund); Writeln('It is now ',LeadingZero(h),':', LeadingZero(m),':',LeadingZero(s), '.',LeadingZero(hund)); writeln('konec sort'); readln end. Заметьте, что из основной программы удален вывод отсортированного массива для удобства наблюдения времени окончания сортировки. Запишите длительность выполнения сортировки 10000 целых чисел. 4. Исследуйте длительность сортировки для 12000 чисел. Запишите результат. 8 5. Измените программу так, чтобы использовалась следующая последовательность приращений: 31, 15, 7, 3, 1. Отладьте и выполните ее для 12000 целых чисел. Запишите длительность выполнения сортировки. Содержание отчета: 1. Текст исходной программы s2. 2. Текст программы для приращений 31, 15, 7, 3, 1 и с использованием процедуры GetTime. 3. Время выполнения сортировки для 10000 чисел c приращениями 9, 5, 3, 1. 4. Время выполнения сортировки для 12000 чисел c приращениями 9, 5, 3, 1. 5. Время выполнения сортировки для 12000 чисел c приращениями 31, 15, 7, 3, 1. 6. Краткие ответы на вопросы для самопроверки. Задание 2. (Выполняется во время аудитороных занятий). 1. Наберите и отладьте программу, реализующую пирамидальную сортировку: {$R-} program s2; uses crt; const n=10; type item=record key:integer; end; index=1..n; var a:array[1..n] of item; procedure vvod_mass; var i:index; begin randomize; for i:=1 to n do a[i].key:=random(100); end;{of vvod_mass} procedure HeapSort; var l,r:index; x:item; procedure sift; label 13; var i,j: index; begin i:=l; j:=2*i; x:=a[i]; while j<=r do begin if j<r then if a[j].key<a[j+1].key then j:=j+1; if x.key>=a[j].key then goto 13; a[i]:=a[j]; i:=j; j:=2*i end; 9 13: a[i]:=x end; begin l:=(n div 2)+1; r:=n; while l>1 do begin l:=l-1; sift end; while r>1 do begin x:=a[1]; a[1]:=a[r]; a[r]:=x; r:=r-1; sift end end; procedure vivod_mass; var i:index; begin for i:=1 to n do write(a[i].key:4); writeln; end; begin {of main} clrscr; vvod_mass; vivod_mass; writeln('nachat sortirovku? (enter)'); readln; Heapsort; vivod_mass; writeln('konec sort'); readln end. Вопросы для самопроверки: 1. Какие две функции выполняет процедура просеивания sift? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Измените программу так, чтобы время сортировки вычислялось программой с помощью процедуры GetTime модуля Dos. 3. Запишите время сортировки 10000 чисел. 4. Измерьте и запишите время выполнения сортировки 12000 чисел. Содержание отчета: Текст исходной программы s2. Текст программы с использованием процедуры GetTime. Время выполнения сортировки для 10000 чисел. Время выполнения сортировки для 12000 чисел. Выводы об эффективности пирамидальной сортировки по сравнению с сортировкой Шелла. 6. Краткие ответы на вопросы для самопроверки. 1. 2. 3. 4. 5. 10 Задание 3. (Выполняется во время аудитороных занятий). 1. Наберите и отладьте программу, реализующую быструю сортировку. Вопросы для самопроверки: 1. Покажите часть кода, где выполняется разделение массива? 2. Какой элемент выбирается в качестве элемента x? 3. Покажите часть кода, где выполняется рекурсия? Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Измените программу так, чтобы время сортировки вычислялось программой с помощью процедуры GetTime модуля Dos. 3. Запишите время сортировки 10000 чисел. 4. Измерьте и запишите время выполнения сортировки 12000 чисел. 5. Составьте сравнительную таблицу: № п.п. Название сортировки … … Время сортировки массива из 10000 целых случайных чисел … 6. Проанализируйте результаты, следующие из таблицы. Содержание отчета: 1. 2. 3. 4. 5. 6. Текст исходной программы. Текст программы с использованием процедуры GetTime. Время выполнения сортировки для 10000 чисел. Время выполнения сортировки для 12000 чисел. Сравнительная таблица. Анализ полученных результатов. 11 Лабораторная работа № 3 ТЕМА: Сортировка последовательных файлов ПРОДОЛЖИТЕЛЬНОСТЬ: 2 часа аудиторных занятий и 10 час. самостоятельной работы. Задание 1. (Выполняется во время аудитороных занятий). Наберите и отладьте программу, реализующую сортировку простыми слияниями. Самостоятельная работа: 1. Внесите в программу изменения, чтобы сортировались 10000 случайных чисел. 2. Измените программу так, чтобы время сортировки вычислялось программой с помощью процедуры GetTime модуля Dos. 3. Запишите время сортировки 10000 чисел. 4. Измерьте и запишите время выполнения сортировки 12000 чисел. 5. Дополните сравнительную таблицу из Лабораторной работы №2 данными по времени сортировки простым слиянием. 6. Сделайте выводы – как соотносятся между собой по быстродействию усовершенствованные методы сортировки массивов и метод простого слияния. Содержание отчета: 1. 2. 3. 4. 5. 6. Текст исходной программы. Текст программы с использованием процедуры GetTime. Время выполнения сортировки для 10000 чисел. Время выполнения сортировки для 12000 чисел. Сравнительная таблица. Анализ полученных результатов. 12 Лабораторная работа № 4 ТЕМА: Введение в рекурсию ПРОДОЛЖИТЕЛЬНОСТЬ: 4 часа аудиторных занятий и 36 час. самостоятельной работы. Задание 1. (Выполняется во время аудитороных занятий). Составьте рекурсивные программы для решения следующих задач. Задача 1. Написать рекурсивную программу нахождения цифрового корня целого числа. Цифровой корень находится суммой через сумму цифр числа до тех пор, пока эта сумма не станет цифрой. Например, для числа 9999999 цифровой корень находится так: 9+9+9+9+9+9+9=63 6+3=9 цифровой корень 9999999 равен девяти. Методические указания Для решения задачи: Формируем тело программы и описываем переменные; Создаем описание функций NUM и ROOT; Вводим целое число N; Вызываем рекурсивную функцию ROOT и определяем цифровой корень числа N; Завершаем работу программы. Переменные: В функции NUM: N- ЦЕЛ О Е ЧИ С Л О ( Г Л О БА Л Ь Н А Я П ЕР ЕМ Е НН А Я ); S-вспомогательная переменная (локальная переменная); В функции ROOT: N- ЦЕЛ О Е ЧИ С Л О ( Г Л О БА Л Ь Н А Я П ЕР ЕМ Е НН А Я ); В основной программе: N - целое число (глобальная переменная). Задача2. Дан прямоугольник со сторонами A и B, где A и B- натуральные числа. Начинаем отсекать от него квадраты. Сколько таких квадратов можно отсечь, если каждый раз отсекается самый большой квадрат? 13 Методические указания Для решения этой задачи нам нужны функции MAX и MIN. Введем: Вспомогательные переменные X и Y (Y>=X), соответствующие уменьшающимся сторонам прямоугольника; Вспомогательную переменную D, которая определяет уменьшение размеров прямоугольника после очередного отсечения наибольшего квадрата, сторона которого находится как X: =MIN(D,X). Организуем цикл, в котором сторона Y уменьшается каждый раз на MIN(D,X) до тех пор, пока не останется последний квадрат или Y не станет меньше X.В последнем случае переименовываем стороны оставшегося прямоугольника как Y: = MAX(D,X) и X: =MIN(D,X) и продолжаем цикл. Для решения задачи: Формируем тело программы и описываем переменные; Создаем описание функций MIN и MAX и F; Вводим два натуральных числа A и B; Присваиваем начальные значения вспомогательным переменным; Вызываем рекурсивную функцию F,которая определяет количество квадратов; Завершаем работу программы. Переменные: В функции MIN: I, J-два целых числа (формальные параметры); В функции MAX: I, J-два целых числа (формальные параметры); В функции F: D, X, Y-вспомогательные переменные (глобальные переменные); В основной программе: D, X, Y-вспомогательные переменные (глобальные переменные); A, B-два натуральных числа. 14 РЕЗУЛЬТАТЫ РАБОТЫ: Введите два натуральных числа 73 искомое число квадратов: 5 введите два натуральных числа 7 13 искомое число квадратов: 8 Задача 3. Дан прямоугольный бильярдный стол со сторонами A и B, где A,B-натуральные числа ( бильярд Льюиса Кэрролла).Из угловой лузы вылетает шар под углом 45 градусов к боковым стенкам, ударяется о борт, отскакивает, ударяется еще раз и т.д., пока не вылетит в одну из угловых луз. Рассчитать количество отрезков в ломаной траектории шара. Считать угол падения равным углу отражения. Методические указания Для решения задачи: формируем тело программы и описываем переменные; создаем описание рекурсивной функции BILL; вводим два натуральных числа А и В; вызываем функцию BILL для определения количества от резков; завершаем работу программы. Переменные: в функции BILL: X, Y - два натуральных числа (формальные параметры); К - вспомогательная переменная (локальная переменная); А - длинная сторона стола (глобальная переменная); 15 в основной программе: А, В - два натуральных числа (глобальные переменные). РЕЗУЛЬТАТЫ РАБОТЫ: Введите два натуральных числа A>B 73 количество отрезков в траектории: 9 введите два натуральных числа A>B 13 7 количество отрезков в траектории: 37 Самостоятельная работа: 1. Составьте итеративные варианты решения задач. Содержание отчета: 1. 2. 3. 4. Листинги рекурсивного решения задач. Результаты решения задач в соответствии с контрольными примерами. Листинги итеративного решения задач. Результаты решения задач в соответствии с контрольными примерами. 16 Лабораторная работа № 5 ТЕМА: Алгоритмы с возвратом ПРОДОЛЖИТЕЛЬНОСТЬ: 4 часа аудиторных занятий и 48 час. самостоятельной работы. Задание 1. (Выполняется во время аудитороных занятий). 1. Наберите и отладьте программу задачи о ходе коня. 2. Проверьте правильность работы программы в соответствии с контрольными применами. 3. Найдите пример начальной позиции коня, когда решения не существует Самостоятельная работа: 1. Наберите и отладьте программу задачи о восьми ферзях, выдающую одно решение задачи. 2. Наберите и отладьте программу задачи о восьми ферзях, выдающую все решения задачи. 3. Измените последнюю программу так, чтобы она выдавала и количество проверок безопасности позиции для всех решений задачи о восьми ферзях. Содержание отчета: Листинг программы о ходе коня. Начальная позиция коня и решение в соответствии с контрольными примерами. Начальная позиция коня, когда решения не существует. Листинг программы задачи о восьми ферзях, выдающую одно решение задачи. Результат решения задачи. Листинг программы задачи о восьми ферзях, выдающую все решения задачи. Первые 12 результатов решения задачи. Листинг программы задачи о восьми ферзях, выдающую все решения задачи и количество проверок безопасности позиций. 9. Первые 12 результатов решения задачи с колонкой количества проверок безопасности позиций. 1. 2. 3. 4. 5. 6. 7. 8. 17 Два примера рекурсивных программ, где рекурсия полностью оправданна. Симпатичный узор на рис. 5 состоит из суперпозиции пяти кривых. Эти кривые строятся на основе некоторого регулярного образца, и предполагается, что их можно нарисовать с помощью графопостроителя, управляемого вычислительной машиной. Задача — найти рекурсивную схему, по которой можно написать программу, управляющую графопостроителем. Рассматривая рисунок, мы обнаруживаем, что три наложенные друг на друга кривые имеют форму, показанную на рис. 3. Рисунок 3. Кривые Гильберта порядка 1, 2 и 3. Мы обозначаем их через H1, Н2 и Н3. На рисунках видно, что Hi+1 получается соединением соответствующим образом четырех вдвое Hi повернутых и меньшего связанных размера, вместе тремя соединительными линиями. Отметим, что можно считать, что H1 состоит из четырех пустых Н0, связанных тремя прямыми линиями. Кривая Нi называется кривой Гильберта i-го порядка в честь его первооткрывателя Д. Гильберта (1891). Предположим., что у нас имеются следующие основные средства для построения графов: две координаты — переменные х и у, процедура setplot (устанавливающая перо в точку с координатами х и у) и процедура plot (передвигающая перо, которое при этом чертит прямую из текущей точки в точку, обозначенную х и у). Поскольку каждая кривая Нi состоит из четырех вдвое меньших копий Hi-1, то естественно построить процедуру, рисующую Нi в виде композиции четырех 18 частей, каждая из которых рисует Hi-1 соответствующего размера и с нужным поворотом. Если мы обозначим эти четыре части А, В, С и D, а подпрограммы, рисующие соединительные линии, в виде стрелок, указывающих соответствующее направление, то получим следующую рекурсивную схему (см. рис.3): A:DAAB B:CBBA (19) С: ВCCD D: ADDC Если длину соединительной линии обозначить через h, то процедуру, соответствующую схеме А, можно легко выразить с помощью рекурсивных обращений к описанным аналогичным образом процедурам В и D и самой процедуры А: Procedure A(i: integer); begin if i>0 then begin D(i-1); x:=x - h; plot; A(i-l); y:=y - h; plot; A(i-l); x:=x + h; plot; B( I - L ) END END Эта процедура инициируется один раз основной программой для каждой кривой Гильберта, которые накладываются одна на другую, образуя данный рисунок. Основная программа задает исходную точку для кривой, т. е. начальные значения х и у, и единичное приращение h. Величина h0 19 соответствует ширине всей страницы и должна удовлетворять равенству h0 =2k для некоторого k≥ n (см. рис. 4). Программа рисует всего n кривых Гильберта. Program Hilbert(pf,output); {изображение кривых Гильберта порядка от 1 до n} Const n=4;h0=512; Var i,h,x,y ,x0,у 0: integer; Pf: file of integer; {plot file} Procedure A(i: integer); Begin if i >0 then begin D(i-l);x:=x-h; plot; A(i-l);y:=y-h ; plot; A(i-1) ;x:=x+h; plot; B(i-l); end; end; Procedure В (i: integer); begin if i>0 then Begin C(i-l)y:=y+h; plot; B(i-l);x:=x+h; plot; B(i-l);y:=y-h; plot; A(i-l); end end; 20 Procedure C(i: integer); Begin if i>0 then begin B(i-l);x:=x+h; plot; C(i-l);y:=y+h; plot; C(i-l);x:=x-h; plot; D(i-l); end; end; Procedure D(i: integer); BEGIN IF I >0 THEN begin A(i-l);y:=y-h; plot; D(i-l);x:=x-h; plot; D(i-l);y:=y+h; plot; C(i-l); end; end; begin startplot; i:=0;h:=h0;x0:=h div 2;y0:=x0; repeat {изображение кривой Гильберта порядка i} i:=i+l; h:=h div 2; x0:=x0+(h div 2);y0:=y0+(h div 2); x:=x0;y:=y0;setplot; A(i) until i=n; 21 E ND P L O T end. Рисунок 4. Рамка для кривых. Рисунок 5. Кривые Гильберта порядка H1,...., Нn Похожий, но несколько более сложный и эстетически утонченный рисунок приведен на рис. 7. Он также получен с помощью наложения нескольких кривых; две такие кривые показаны на рис 6. Кривая Si, называется кривой Серпинского i-го порядка. Какова рекурсивная схема для такой кривой? Попробуем в качестве основного строительного блока выделить лист S1, 22 возможно, без одного ребра. Но это не приводит нас к нужному решению. Принципиальное различие между кривыми Серпинского и Гильберта заключается в том, что кривые Серпинского являются замкнутыми (без соединительных линий). Это означает, что основная рекурсивная схема должна давать разомкнутую кривую, а четыре части соединяются линиями, не принадлежащими самому рекурсивному узору. Действительно, эти связи представляют собой четыре прямые в четырех внешних «углах» изображенных на рис. 6 жирными линиями. Рисунок 6. Кривые Серпинского порядка 1 и 2. Можно считать, что они принадлежат к непустой начальной кривой S0, представляющей собой квадрат, стоящий на одном угле. Теперь легко построить рекурсивную схему. Четыре составляющие фигуры вновь обозначаются А, В, С и D, а соединительные линии рисуются явно. Заметим, что четыре рекурсивные кривые действительно одинаковы с точностью до поворота на 90°. Основной образ кривых Серпинского следующий: S:AВСD (21) а объединенные рекурсивные фигуры строятся по таким схемам: 23 А: А->В=>D->А В: В->С=>А->В С:С->В=>В->С D: D->А=>С->D (22) (Двойные стрелки обозначают линии двойной длинны.) Используя те же примитивы для операций построения, что и в случае кривых Гильберта, приведенную выше рекурсивную схему легко преобразовать в (прямо и косвенно) рекурсивный алгоритм: procedure A(i: integer); begin if i >0 then begin A(i-l); x:=x+h; y:=y-h; plot; B(i-l); (23) x:=x+2*h; plot; D(I-1); x:=x+h; y:=y+h; plot; A(i-l) end endЭта процедура соответствует первой строке рекурсивной схемы (22). Процедуры, соответствующие фигурам В, С и D, строятся аналогично. Основная программа строится по схеме (21). Она должна установить начальные значения для координат рисунка и задать длину единичной линии h в зависимости от формата бумаги, как показано в программе. Результат работы этой программы при n= 4 показан на рис. 7. Заметим, что S0 не рисуется. 24 Рисунок 7. Кривые Серпинского Si program Serp; {изображение кривых Серпинского порядка от 1 до n} const n=4;h0=512; var i,h,x,y,x0,y0:integer; pf: file of integer; procedure A(i: integer); begin if i >0 then begin A(i-l); x:=x+h; y:=y-h; plot; 25 B(i-l); x:=x+2h; plot; D(i-l);x:=x+h; y:=y+h; plot; A(i-l) end end; procedure B(i: integer); begin if i >0 then begin B(i-l); x:=x-h; y:=y-h; plot; C(i-l); x:=x-2*h; plot; A(i-l);x:=x+h; y:=y-h; plot; B(i-l) end end; procedure C(i: integer); begin if i >0 then begin c(i-l); x:=x-h; y:=y+h; plot; D(i-l); x:=x-2*h; plot; B(i-l); x:=x-h; y:=y-h; plot; C(i-l) end end; procedure D (i: integer); begin if i >0 then begin D(i-l); x:=x+h; y:=y+h; plot; A(i-l); x:=x+2*h; plot; 26 C(i-l); x:=x-h; y:=y+h; plot; D(i-l) end end; Begin startplot; I:=0; h:=h0 div 4; x0:=2*h; y0:=3*h; repeat i:=i+l; x0:=x0-h; h:=h div 2; y0:=y0+h; x:=x0 ; y:=y0; setplot; A(i); x:=x+h; y:=y-h; plot; B(i); x:=x-h; y:=y-h; plot; C(i); x:=x-h; y:=y+h; plot; D(i); x:=x+h; y:=y+h; plot; until i = n; endplot end. Как можно убедиться, в этих примерах рекурсия используется весьма элегантно. Правильность программ легко следует из их структуры и схем построения. Кроме того, использование явного параметра уровня i в соответствии со схемой 5 гарантирует окончание программ, так как глубина рекурсии не может быть больше n. По сравнению с этой рекурсивной формулировкой эквивалентные программы, которые избегают явного использования рекурсии, чрезвычайно сложны и трудны для понимания. 27