pascal 8 Класс - Окулов

424
С. ОКУЛОВ о с н н в ш Москва Лаборатория Базовых|Эй6РЬМ" 2 002

Upload: timeidea-corp

Post on 25-Mar-2016

250 views

Category:

Documents


8 download

DESCRIPTION

Год: 2002 Автор: Окулов С. М. Жанр: компьютерная литература Издательство: Бином. Лаборатория знаний

TRANSCRIPT

Page 1: Pascal 8 Класс - Окулов

С. ОКУЛОВ

о с н н в ш

М о с к в а Л а б о р а т о р и я Базовых|Эй6РЬМ"

2 0 0 2

Page 2: Pascal 8 Класс - Окулов

УДК 519.85(023) Б Б К 22.18

О 52

Окулов С. М. О 52 Основы программирования. — М.: ЮНИМЕДИАСТАИЛ,

2002. — 424 е.: ил. ISBN 5-94774-003-6

В учебнике рассмотрены основные управляющие конструкции сис-темы программирования Турбо Паскаль, процедуры н функции, стро-ковый, вещественный и файловый типы данных. Приводится матери-ал для изучения массивов, методов сортировки и поиска, а также по динамическим структурам данных Рассмотрены следующие структу-ры данных: списки, стеки, очереди, двоичные деревья, АВЛ-деревья и Б деревья. В материалах для чтения обсуждаются практически все во-просы, входящие в школьный минимум знаний по информатике

Книга является достаточно полным учебником по программиро-ванию, реализующим сложную задачу — формирование у читателя структурного стиля мышления Учебным материалом является сис-тема программирования Турбо Паскаль, а также большое число за-дач, включая задачи иа алгоритмы сортировки и поиска

Достаточно подробно рассмотрена работа с динамическими струк-турами данных.

Книга рассчитана на широкий круг читателей от школьника и студента до специалиста, решающего с помощью программирования прикладные задачи.

УДК 519.85(023) ББК 22.18

Серия «Технический университет» Учебное издание

Окулов Станислав Михайлович Основы программирования Художник Н Лозинская

Компьютерная верстка Л Катуркиной Лицензия на издательскую деятельность №066140 от 12 октября 1998 г.

Подписано в печать 30.01.02. Формат бОхЭО1/,,. Гарнитура Школьная. Бумага офсетная. Печать офсетная. Усл. печ.л. 26,5.

Тираж 5000 экз. Заказ 452 ООО «Издательство Лаборатория Базовых Знаний», 2002 г. Адрес для пере-

писки: 103473, Москва, а/я 9. Телефон (095)955-0398. E-mail: [email protected] Гигиеническое заключение 77.99.1.953.П.1815.4.99 от 12.04.1999 г.

Отпечатано с готовых диапозитивов в полиграфической фирме «Полиграфист». 160001, г. Вологда, ул. Челюскинцев, 3.

ISBN 5-94774-003-6 © Окулов С. М., 2002 © ЮНИМЕДИАСТАЙЛ, 2002

Page 3: Pascal 8 Класс - Окулов

Содержание

Содержание 3 Предисловие 5

Часть первая. О с н о в н ы е у п р а в л я ю щ и е к о н с т р у к ц и и 10 Занятие № 1. Первая программа 10 Занятие № 2. Целый тип данных 20 Занятие № 3. Команды редактора для работы с блоками, работа с окнами 28 Занятие № 4. Логический тип данных, операции сдвига. . . . 37 Занятие № 5. Составной оператор и оператор If 46 Занятие № 6. Оператор For 56 Занятие № 7. Оператор While 66 Занятие № 8. Оператор Repeat-Until 73 Занятие № 9. Вложенные циклы 84

Часть вторая. П р о ц е д у р ы и ф у н к ц и и — э л е м е н т ы с т р у к т у р и з а ц и и п р о г р а м 99 Занятие № 10. Одномерные массивы. Работа с элементами 99 Занятие № 11. Процедуры 115 Занятие № 12. Функции 133 Занятие № 13. Рекурсия 149 Занятие № 14. Символьный и строковый типы данных . . . . 167 Занятие № 15. Вещественный тип данных 186 Занятие № 16. Текстовые файлы 202

Часть третья. М а с с и в — ф у н д а м е н т а л ь н а я структура д а н н ы х 2 2 0 Занятие №17. Методы работы с элементами одномерного массива 220 Занятие № 18. Множественный тип данных 234 Занятие № 19. Методы сортировки 243 Занятие № 20. Методы быстрой сортировки 252 Занятие № 21. Поиск данных 266 Занятие № 22. Двумерные массивы. Работа с элементами 288 Занятие № 23. Двумерные массивы. Вставка и удаление 302

Page 4: Pascal 8 Класс - Окулов

Осповы программирования

Занятие № 24. Несколько задач на технику работы с двумерными массивами 311 Занятие № 25. Комбинированный тип данных (записи). . . . 323

Часть четвертая. Д и н а м и ч е с к и е с т р у к т у р ы д а н н ы х 3 3 5 Занятие № 26. Динамические структуры данных 335 Занятие № 27. Стек 350 Занятие № 28. Очередь 360 Занятие № 29 Поиск в графе 373 Занятие № 30. Двоичные деревья 384 Занятие № 31. Сбалансированные деревья 399 Заключение 421

Page 5: Pascal 8 Класс - Окулов

Предисловие

Моему отцу — Окулову Михаилу Митрофановичу, рано ушедшему из этой жизни, посвящаю.

Из истории возникновения учебника. В 1988 году перед ав-тором возникла проблема: «чему учить в информатике и как учить». Исходным «багажом» при принятии решения было об-разование в классическом университете по специальности при-кладная математика и более чем 15-летний опыт разработки программного обеспечения специализированных вычислитель-ных комплексов в промышленности. Попытки обучения по су-ществующим в то время учебникам, а их было не так много, как в настоящее время, не принесли ни удовлетворения, ни ре-зультатов. Информатика воспринималась как обычный пред-мет. Дидактические возможности компьютера «оставались за бортом». Может быть, причиной явилось отсутствие у автора соответствующего опыта и педагогического образования. Огра-ничимся констатацией факта, оставим критику этого результа-та, так же как и обсуждение достоинств и недостатков сущест-вующих учебников, доброжелателям.

Тезисно обозначим исходные положения при принятии ре-шения. Первой посылкой было убеждение в том, что програм-мирование является «стержнем информатики». В нем синтезиро-вано все, что десятилетиями нарабатывалось в Computer Science. Это и результаты работы специалистов, работающих на «сты-ке» математики и информатики, это и достижения в вычисли-тельной технике, это и, наконец, огромный опыт формализации и решения сложнейших проблем в самом программировании, связанный с созданием больших программных комплексов.

Второй исходной посылкой являлось убеждение в том, что занятия по информатике в корне должны отличаться от тра-диционных занятий по любому другому предмету. Во-первых, на занятиях по информатике должна поощряться ошибка, ибо только через ошибку можно прийти к результату, при изуче-нии же любого другого предмета ошибка карается двойкой. Во-вторых, постоянная обратная связь с обучаемым через компьютер, объективная и лишенная эмоций, — это инстру-ментарий индивидуального и развивающего обучения. В-тре-тьих, стиль мышления у программистов свой, отличающийся от стиля мышления как математика, так и любого другого специалиста. Он настроен, если так можно выразиться, на бо-

Page 6: Pascal 8 Класс - Окулов

Осповы программирования

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

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

Основной методический принцип учебника — все познается через труд, через преодоление ошибок (собственных), через процесс решения задач. Этот принцип определяет структуру за-нятий. Вводная часть - обсуждение нового материала, экспери-менты с заготовками решений задач, самостоятельное решение задач.

Данный учебник нельзя считать продуктом творчества од-ной личности. Коллеги по работе в Вятском государственном педагогическом университете и физико-математическом лицее г. Кирова сделали много для того, чтобы работа была заверше-на. Влияние моих учеников на осмысление того, как учить и чему учить, просто огромно. Я благодарен им за совместную ра-боту.

В первой части учебника изучаются основные управляющие конструкции языка программирования Турбо Паскаль, но не только. Целевой установкой этих занятий было конструирова-ние решения задач из минимального числа инструкций. В про-цессе занятий необходимо достичь такого уровня их понима-ния, чтобы работа программы школьником воспринималась в динамике. Ему необходимо «видеть» эту работу, динамику из-менения значений переменных в процессе работы программы. С этой целью в начале применяется «ручная» прокрутка про-граммы, а затем — средства отладчика системы программиро-вания. В конечном итоге, использование этих инструкций уча-щимися должно осуществляться не по заученным схемам, а так же естественно, как применение русского языка. Учащемуся нужно как бы забыть об их существовании, писать их автома-тически. Таким образом закладывается первый «кирпичик» в фундамент структурного стиля мышления.

Page 7: Pascal 8 Класс - Окулов

Предисловие 7

Вторая часть учебника посвящена механизму использования процедур и функций , создания «блоков» логики с одной точкой входа и одной точкой выхода. При этом взаимодействие по дан-ным должно осуществляться по заданным правилам — в соот-ветствии с механизмом передачи параметров. Пересечение «бло-ков» к а к по управлению, так и по данным должно быть минимальным.

Материал, так же к а к и в первой части учебника, разбит на занятия . Что под этим понимать? Это материал не для одного урока. Автор не дает рекомендаций по этому поводу. Все зави-сит от уровня подготовки школьников . Приведу пример. Одну из программ четвертого занятия ему приходилось объяснять и 5 минут и целое занятие (да и того не хватало):

Program Му4_3; Begin

WriteLn (1365 And 2730) ; HriteLn (1365 Or 2730) ; WriteLn (1365 ^or 2730) ; UriteLn (1365 And $FF) ; WriteLn (1365 And $FF0) ; WriteLn (1365 And $FF00) ; ReadLn;

End.

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

Третья часть учебника посвящена, в основном, фундамента-льному понятию информатики — массиву. При этом весь мате-риал является очередным витком в освоении и закреплении це-левых установок первых двух частей. Разумеется, задачной основой этой части являются в основном алгоритмы сортиров-к и и поиска, ибо для достижения нашей цели — изучения мас-сива как структуры данных, трудно найти что-либо лучшее. Занятие по комбинированному типу данных стоит несколько особняком. Оно не согласуется с целью этой части учебника, но является необходимым для работы с четвертой частью — динами-ческими структурами данных (последним «кирпичиком» фунда-мента нашего здания).

В четвертой части учебника рассмотрены динамические струк-туры данных: списки, стеки, очереди, двоичные деревья, ABJI-деревья и Б-деревья. Обычно этот материал не затрагивается при работе со школьниками. Однако без него невозможен пере-ход на следующий «виток» обучения программированию. В объектно-ориентированных, визуальных технологиях и т. д. он

Page 8: Pascal 8 Класс - Окулов

Осповы программирования

является одним из стержневых, без которого понимание техно-логий остается «любительским». Это один аспект. Второй, и ве-сьма немаловажный, заключается в том, что на этом материале «шлифуется» структурный стиль мышления. В задачах этой час-ти учебника без автоматизма в структуризации решений (испо-льзования принципа «разделяй и властвуй») обойтись — очень трудно. Еще одна особенность — отладка программ является достаточно сложным мероприятием. Предвидеть и рассмотреть все варианты, случаи работы программы при различных исход-ных данных очень не просто. Оксфордский словарь английского языка трактует эвристику как искусство нахождения истины. Мы уточним — искусство в убеждении себя самого и окружаю-щих в правильности работы программы. Владение этим искусст-вом при решении задач четвертой части потребуется полное.

Итак, подведем итоги. Главная цель учебника — развитие мышления ученика: от алгоритмического к структурному, а за-тем к эвристическому мышлению. Речь не идет о том, что вна-чале мы обучаем одному, затем другому и, наконец, третьему. Элементы обучения синтезированы в нечто единое, и те, и дру-гие, и третьи аспекты проблемы развиваются одновременно.

Обозначим на уровне общих положений каждый стиль мыш-ления. Умение написать алгоритм назовем алгоритмическим стилем мышления. Алгоритм — план будущей деятельности (модель деятельности), записанный в заранее выбранной фор-мальной системе обозначений с ограниченными возможностя-ми.

Умение «расчленять» задачу, «разделять и властвовать» на-зовем структурным стилем мышления. Когда мы говорим о структуре, то обязаны сказать о том, из каких элементов она состоит, и как элементы связаны между собой. Характерные черты этого стиля: простота и ясность; использование только базовых (основополагающих) конструкций; отсутствие многоце-левых функциональных блоков и т. д.

Умение находить истину, доказывать факт правильности ре-шения задачи (работы программы) назовем эвристическим сти-лем мышления. Факторами успешной эвристической деятель-ности являются умение оценивать, рациональность действий, принцип экономии и т. д.

Отметим, что компьютер, система программирования не яв-ляются целью обучения, они — инструмент реализации целей, хотя при этом, разумеется, познается, в определенном объеме, и сам инструмент.

Page 9: Pascal 8 Класс - Окулов

Предисловие 9

В заключении хотелось бы еще раз сказать слова благодар-ности (огромной!) всем моим коллегам и ученикам. Сергею Львовичу Островскому, главному редактору газеты «Информа-тика», хотелось бы выразить особую признательность. Его ум-ное и ироничное молчание значило для меня иногда больше, чем все жаркие дискуссии по поводу ...

Моей дружной семье, все члены которой мирились с ежеве-черней работой, ибо это единственное время, которое можно было выделить для написания учебника, низкий поклон. Ви-деть постоянно чем-то озабоченного мужа и отца, наверное, со-мнительное удовольствие. Но они терпели и терпят, и я благо-дарен им, и не только за это.

Все замеченные ошибки и опечатки автор с благодарностью примет ([email protected]) .

Page 10: Pascal 8 Класс - Окулов

Часть первая

Основные управляющие конструкции

З а н я т и е № 1. П е р в а я п р о г р а м м а

План занятия • загрузка Турбо Паскаля ; • структура диалога; • первая программа — нахождение произведения двух це-

лых чисел; • компиляция , сохранение и запуск программы; • разбор программы. Опишем основные моменты проведения занятия . Структура диалога. После загрузки системы на экране по-

является три окна:

— окно 1 — главное меню

— окно 2 — основное, или рабочее, окно

— окно 3 — окно помощи, в нем указывается назначение основных функциональных клавиш

Переход из первого окна во второе и наоборот осуществляет-ся при помощи н а ж а т и я к л а в и ш и F10.

Набор программы

Program Myl_l; Var a,Ь,rez: Integer; Begin

WriteLn ('Введите два числа через пробел'); ReadLn(a,b) ; rez:=а*b; WriteLn (' Их произведение равно ' , rez) ; WriteLn('Нажмите <Enter>'); ReadLn;

End.

Page 11: Pascal 8 Класс - Окулов

Основные управляющие конструкции 11

Краткий разбор примера. Структура программы. Програм-ма начинается с заголовка, имеющего следующий вид: Program <имя программы>; за ним идет раздел описаний, в котором должны быть описаны все идентификаторы (константы, пере-менные, типы, процедуры, функции, метки), которые будут ис-пользованы в программе. После раздела описаний идет раздел операторов, который начинается со служебного слова Begin и заканчивается служебным словом End. В этом разделе задают-ся действия над объектами программы, введенными в разделе описаний. Операторы в этом разделе отделяются друг от друга точкой с запятой. После последнего слова End ставится точка.

Имя этой программы Му1_1 (заметим, что в имени програм-мы не должно быть пробелов, оно должно начинаться с буквы или знака подчеркивания, состоять только из латинских букв, цифр и знаков подчеркивания, не допускается использование символов точки и запятой). Из разделов описаний имеется лишь один — раздел переменных. Он начинается со служебно-го слова Var, после которого идет последовательность объявле-ния переменных, разделенных точкой с запятой. В каждом объявлении перечисляются через запятую имена переменных (идентификаторы) одного типа, после чего ставится двоеточие и указывается тип переменных. В нашем примере описаны три пе-ременные: все они (a, b и rez) имеют целый тип (Integer), то есть значениями переменных этого типа являются целые числа.

Понятие переменной — центральное в любом языке программи-рования. Для описания переменной (величины, которая может изменяться в процессе работы программы) следует указать имя переменной, ее тип и значение. Следует соблюдать следующий принцип: «Использовать переменную можно лишь тогда, когда ей присвоено некоторое значение». Это позволит Вам избежать многочисленных ошибок в работе программ.

После описательной части идет раздел операторов, начинаю-щийся со служебного слова Begin, после которого записывают-ся операторы языка. Первый встречающийся оператор — это WnteLn('<meKcm>')\ — записать (вывести) на экран текст, за-ключенный между апострофами, Ln добавляется после Write для того, чтобы курсор автоматически переходил на следую-щую строку при выводе на экран текстов или результатов вы-полнения программы.

Следующий оператор — это ReadLn(a.b); — читать данные с клавиатуры. В данном случае необходимо ввести два целых

Page 12: Pascal 8 Класс - Окулов

12 Часть первая

числа через пробел, тогда переменной а присваивается значе-ние, равное первому введенному числу, а переменной b присва-ивается значение, равное второму введенному числу. Напри-мер, вы ввели числа 12 и 45, тогда а=12, а Ь=45. После Write также можно ставить Ln.

После этих двух операторов стоит оператор присваивания: rez:=a*b; (:= — это знак оператора присваивания в я зыке Пас-каль).

Значение выражения из правой части оператора присваивания заменяет текущее значение переменной из левой части. Тип значение выражения обычно должен совпадать с типом пере-менной.

При выполнении оператора переменная rez получит значе-ние, равное произведению числа а на число b (см. рис.). Так как в результате умножения двух целых чисел получается це-лое число, то переменная rez описана типом Integer (значения-ми которого могут быть лишь целые числа).

readln(a,b) rez =a*b

Следующий оператор — это снова оператор WriteLn ( <текст>', rez) — он выведет на экран текст, заключенный между апострофами, а за ним значение переменной rez. Затем следующий оператор WriteLn выведет на экран сообщение: На-жмите <Enter> , а оператор ReadLn будет ожидать этого нажа-тия в окне выполнения. В конце раздела операторов стоит слу-жебное слово End с завершающей точкой.

Запуск программы. Для того, чтобы запустить программу, выходим в главное меню (нажатием F10) — первое окно, выби-раем режим Run и дважды нажимаем <Enter>. На экране по-является сообщение:

Page 13: Pascal 8 Класс - Окулов

Основные управляющие конструкции 13

Введите два целых числа через пробел Курсор мигает в следующей строке, вводим два целых числа

через пробел и н а ж и м а е м <Enter>, после этого появляется со-общение:

произведение равно... нажмите <Enter>

Вместо точек будет написано значение переменной гег, то есть число, равное произведению первого введенного числа на второе. Это сообщение будет на экране до тех пор, пока не на-жата к л а в и ш а <Enter>.

Сохранение программы.. Д л я того, чтобы сохранить про-грамму на внешнем носителе, необходимо:

• в ы й т и в главное меню и выбрать р е ж и м File; • н а ж а т ь <Enter>, из появившегося окна выбрать р е ж и м

Save as... и н а ж а т ь к л а в и ш у <Enter>; • в п о я в и в ш е м с я окне набрать и м я файла и н а ж а т ь клави-

ш у <Enter>.. Н а п р и м е р , a:\priml_l.pas; здесь а:\ — это логическое и м я диска , на котором будем сохранять файл , priml_l — и м я файла (оно м о ж е т содержать не более 8 символов) , p a s — расширение , сообщающее о том, что файл содержит программу, написанную на я з ы к е Пас-к а л ь .

П р и м е ч а н и е Для быстрого сохранения файла можно воспользоваться команда-ми Save или Save all меню File. Выход из системы программирования Турбо-Паскаль.

Д л я того, чтобы закончить работу, необходимо: • выйти в главное меню и выбрать р е ж и м File; • н а ж а т ь <Enter> и из появившегося окна выбрать р е ж и м

Quit, после чего н а ж а т ь либо <Enter>, либо комбинацию <Alt>-<X>.

Экспериментальный раздел работы

1. Введите числа при работе программы, например 4567 и 789. Убедитесь, что у Вас получается неправдоподобный резуль-тат — отрицательное число ( -1117) . Найдите эксперимента-льным путем тот интервал значений переменных а и Ь, когда результат умножения правильный.

2. Вместо ввода числа введите какой-нибудь символ. Убедитесь, что выполнение программы не произошло, компьютер выдал сообщение об ошибке Error 106: Invalid numeric format.

Page 14: Pascal 8 Класс - Окулов

3. Добавьте лишний знак апострофа в операторе WriteLn. Убе-дитесь, что компиляция программы не произошла, а компь-ютер Вас порадовал ошибкой Error 8: String constant exceeds line.

4. Измените в программе Myl_l оператор гег~а*Ъ на rez:=a-(a Div b)*b. Выясните действие операции Div для переменных целого типа. Измените текстовую часть следу-ющего за оператором присвоения оператора WriteLn, отра-зив в ней результат Вашего исследования.

5. Добавьте к программе предыдущего примера переменную с именем ost, оператор присвоения ost:=a Mod b и оператор вы-вода WriteLnf'????????' ,ost);. Выясните, что за действие вы-полняется с помощью оператора Mod. Замените знаки вопро-са Вашими пояснениями его работы.

6. Наберите следующую программу.

Program Myl_2; Var a: Integer; Begin

WriteLn('Введите целое числос); ReadLn (a); WriteLn('???????? Abs (a)); WriteLn('Нажмите <Enter>'); ReadLn;

End. Вашей задачей является замена знаков вопроса в операторе

WriteLn на текст, поясняющий работу операции Abs. Выполни-те аналогичное исследование для операций Sqr(a), Ord(a), Succ(a), Pred(a).

П р и м е ч а н и е Рекомендуется следующий порядок работы. Текст программы Му1_1 сохраняется под новым именем, например Pnml_2.pas, а затем из-меняется и вновь сохраняется. Это позволит сократить время на на-бор программы.

Задания для самостоятельной работы

1. Изменить программу для нахождения суммы двух чисел. 2. Изменить программу для нахождения суммы четырех чисел. 3. Найти значение выражения: (a+(d-12)*3)*(c-5*k), где значе-

ния переменных a,d,c и к вводятся с клавиатуры.

Page 15: Pascal 8 Класс - Окулов

Основные управляющие конструкции 15

4. Н а р и с о в а т ь р и с у н о к по т и п у р и с у н к а , приведенного в тек-сте з а н я т и я , о т р а ж а ю щ е г о д е й с т в и я из следующего фраг-м е н т а п р о г р а м м ы : X = 1 3 ; У =2 5 ; t = х ; X = у ; Y = t ;

5. И з м е н и т ь рисунок из предыдущего примера для о т р а ж е н и я действий фрагмента программы при замене последних трех операторов на : х : = х - у ; у : = х + у ; X : = у - х ;

6. Н а п и с а т ь п р о г р а м м у вывода на э к р а н н е с к о л ь к и х чисел в виде : 13

14 15

16 или

101 102

103

Материал для чтения

1. И з истории. Турбо П а с к а л ь появился на р ы н к е програм-мных продуктов в 1983 году и совершил революцию в програм-мировании . До этих пор предпочтение отдавалось Бейсику — простому, дешевому и легко усваиваемому. П а с к а л ь же был ап-паратно зависимым, дорогим и сложным в обращении. С появ-лением Турбо П а с к а л я положение меняется . Турбо Паскаль со-стоит из я з ы к а программирования и среды программирования , которая создает удобства в работе.

Изучение Паскаля как я з ы к а программирования идет вместе с изучением всей системы Турбо Паскаль. Язык программирования Паскаль был разработан Н. Виртом в 1968-1970 годах и получил широкое распространение благодаря наглядности программ и легкости при изучении. Он послужил основой для разработки других языков программирования (например, Ада, Модула-2).

Page 16: Pascal 8 Класс - Окулов

16 Часть первая

Первая версия Турбо Паскаля использовалась не очень дол-го _ появилась в 1983 году, а уже в 1984 году ее заменила вто-рая версия, которая получила широкое распространение. К осе-ни 1985 года появляется третья версия, более удобная в работе (быстрее работают компилятор и редактор, возможен вызов MS-DOS из программы).

Четвертая версия (1988 год), представила Турбо Паскаль в новом виде (появление новой среды, компилятор стал встроен-ным). Осенью этого же года разработана пятая версия, у кото-рой еще больше развита среда и появился встроенный отлад-чик. А в 1989 году появилась версия 5.5, позволившая перейти к объектно-ориентированному программированию.

Шестая версия уже обеспечивала многооконный и много-файловый режим работы, использование «мыши», применение объектно-ориентированного программирования, обладала встро-енным ассемблером и имела другие возможности.

В 1992 году фирма Borland International выпустила два па-кета программирования на языке Паскаль — это Borland Pas-cal 7.0 и Turbo Pascal 7.0.

Пакет Turbo Pascal 7.0 использует новейшие достижения в программировании. Язык этой версии обладает широкими воз-можностями, имеет большую библиотеку модулей. Среда про-граммирования позволяет создавать тексты программ, компи-лировать их, находить и исправлять ошибки, компоновать программы из отдельных частей, использовать модули, отла-живать и выполнять отлаженную программу.

2. Некоторые команды редактора. Режим работы Edit (глав-ное меню) — режим редактирования текста программы.

давления движением курсора — перемещение курсора на символ вправо

— перемещение курсора на символ влево

— перемещение курсора на строку вверх

— перемещение курсора на строку вниз

— перемещение курсора в начало текущей строки

— перемещение курсора в конец текущей строки

— перемещение курсора на страницу вверх.

— перемещение курсора на страницу вниз.

Команды упр

Page 17: Pascal 8 Класс - Окулов

Основные управляющие конструкции 17

П р и м е ч а н и е Страница — это число строк текста, составляющих один экран .

+ Ноте — перемещение курсора в левый верхний угол

Ctrl + End I —перемещение >урсора в левый нижний угол

К о м а н д ы в с т а в к и и у д а л е н и я т е к с т а

Insert — включение и выключение режима вставки,

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

Delete — удаление символа, стоящего в позиции курсора

Backspase I — удаление символа, стоящего перед курсором.

П р и м е ч а н и е Иногда на этой к л а в и ш е написано BS, а иногда это левосторонняя стрелка , р а с п о л о ж е н н а я над к л а в и ш е й ввода (ENTER).

— вставка пустой строки над строкой, где находится курсор

— удаление строки, где находится курсор.

3 . Д а н н ы е — о б щ е е п о н я т и е в с е г о т о г о , с ч е м р а б о т а е т к о м п ь ю т е р . В а п п а р а т у р е в с е д а н н ы е прей*и»р2гатготсятрв»-я®-~ с л е д о в а т е л ь н о с т и д в о и ч н ы х ц и ф р ( р а з р я ! ч в ) . Л я з ы к а з с в н о в " к о г о у р о в н я , а к н и м о т н о с и т с я Т у р б о П а | ; к а Л Е , riW*rpAl«ilpiy*l® с я от д е т а л е й п р е д с т а в л е н и я д а н н ы х в П а м я т и к Л и д а т е р а .

I Ctrl 1

Е П + п л 1Ч + ГН

Page 18: Pascal 8 Класс - Окулов

18 Часть первая

Любой тип данных определяет множество значений, которые может принимать величина этого типа, и те операции, кото-рые можно применять к величинам этого типа . В Турбо Пас-кале работают с пятью типами данных: простыми, строковы-ми, составными, ссылочными и процедурными. К простым типам данных относятся целые, вещественный, логический , символьный, перечислимый и ограниченный (два последних определяются пользователем). На простых типах данных , кро-ме вещественного, определено отношение порядка. Что это та-кое? Все множество значений типа рассматривается к а к упо-рядоченное множество, и каждое значение связано с некоторым целым числом, которое есть его порядковый номер. В любом порядковом типе для каждого значения , кроме первого, суще-ствует предшествующее значение, и для каждого значения , за исключением последнего, существует последующее значение . Определены следующие стандартные ф у н к ц и и д л я работы с порядковыми типами:

• Ord — возвращает порядковый номер значения любого по-рядкового типа.

• Pred — возвращает предшествующее значение для задан-ного значения порядкового типа (а если заданное значе-ние первое?).

• Succ — возвращает следующее значение для заданного значения порядкового типа (а если заданное значение по-следнее?).

В Турбо Паскале есть возможность конструировать (созда-вать) свои типы данных из имеющихся типов, причем весьма сложные. Итак , абстрагирование и конструирование суть кон-цепции типа данных.

4. К а ж д а я программа взаимодействует с окружающей сре-дой с помощью операторов ввода, вывода. Если трактовать тер-мин программа очень вольно, то можно считать, что любая про-грамма что-то откуда-то берет, что-то делает с введенными данными (преобразует) и затем выводит куда-то полученные ре-зультаты. В Турбо Паскале связь программы с внешними устрой-ствами осуществляется через имена файлов. Самый простой случай, когда эти имена связаны с клавиатурой (для ввода дан-ных) и с экраном дисплея (для вывода).

Д л я ввода с клавиатуры используется оператор Read или Re adLn.

Page 19: Pascal 8 Класс - Окулов

Основные управляющие конструкции 19

Вызов: Read(rj,r2,...,rn) Параметры: r j , r 2 , . . . , r n имеют тип Integer или Real или Char или String. Действие: если имеет тип

• Integer или Real, то считывается одно число и значение его присваивается переменной При этом знаки пробела и перевода строки перед числом игнорируются;

• Char, то считывается один символ и присваивается пере-менной

• String, то считывается максимум q символов при длине q строковой переменной г^

Оператор ReadLn действует так же, как и Read. Отличие в том, что после ввода осуществляется переход на начало следующей строки.

Вывод на экран осуществляется с помощью операторов Wri-te или WriteLn.

Вызов: Write(r1,r2,...,rn) Параметры: r j , r 2 , . . . , r n имеют тип Integer или Real или Boolean или Char или String. Действие: на экран выводится значение ^ в стандартном фор-мате. Работа WriteLn отличается тем, что после вывода осуществля-ется переход на начало следующей строки.

Page 20: Pascal 8 Класс - Окулов

20 Часть первая

З а н я т и е № 2 . Ц е л ы й т и п д а н н ы х

План занятия • обсуждение целого типа данных; • разбор программы выделения цифр из десятичного числа;^ • эксперименты с программой: перевод числа из десятичной

системы счисления в двоичную систему счисления, выяс-нение сути арифметических операций с переменными це-лого типа, преобразование переменных целого типа;

• выполнение самостоятельной работы.

Целый тип данных. Существует пять целых типов: Shor-tlnt, Integer, Longlnt, Byte, Word. Они отличаются диапазоном значений, а значит, и размером памяти, отводимой для их представления.

Тип Диапазон значений Объем памяти !

Shortlnt -128 . 127 1 байт, со знаком !i

Integer -32768 . . 32767 2 байта, со знаком

Longlnt -2147483648. .2147483647 4 байта, со знаком !;

Byte 0 . 255 1 байт, без знака j

Word 0 ... 65535 2 байта, без знака >

Операции с величинами целого типа: сложение (+), вычита-ние ( - ) , умножение (*), нахождение целой части деления (Dw), нахождение остатка от деления (Mod). Так как целый тип дан-ных относится к типам, на которых определено отношение по-рядка, то работают стандартные функции Ord, Succ и Pred.

П р и м е ч а н и е Переменной целого типа присваивать значение результата обычной операции деления «/» нельзя. Убедитесь в этом с помощью про-стой модификации программы первого занятия. Попробуйте най ти объяснение этому факту. Возникают, по крайней мере, два, достаточно сложных на

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

Разбору программы предшествует обсуждение выполнения операций Div и Mod.

Page 21: Pascal 8 Класс - Окулов

Основные управляющие конструкции 21

В п р о г р а м м е о п р е д е л я ю т с я ц и ф р ы трехзначного числа . Мож-но ее и с п о л ь з о в а т ь и д л я о п р е д е л е н и я ц и ф р д в у з н а ч н о г о ч и с л а , просто ц и ф р а сотен в этом случае р а в н а н у л ю , и это д е л а е т с я проще .

Program Му2_1; Var a, one, dec, hun, rez:Integer;

WriteLn('Введите число'); ReadLn (a); one:=a Mod 10; WriteLn('Цифра единиц числа - ',one); dec: = (a Div 10) Hod 10; WriteLn('Цифра деечтков числа - ',dec); hun:=a Div 1 00; WriteLn('Цифра сотен числа - ',nun); Rez:=hun*100+dec*l0+one; WriteLn ('А это тоже число - ' , r e z ) ; Write('Enter ' ) ; ReadLn;

End.

137|10 "i30 13 [ i o _

7 10 j 3

Н а п р и м е р , если В ы введете число 137, то зна-ч е н и е п е р е м е н н о й one будет равно 7, dec — 3 и hun — 1. Вспомните д е л е н и е чисел столбиком. П р и м е ч а н и е Не забудьте сохранить программу под именем prim2_l.pas.

Экспериментальный раздел работы

1. И з м е н и т е п р о г р а м м у Му2_1 д л я н а х о ж д е н и я ц и ф р двузнач-ного ч и с л а . С о х р а н и т е ее под и м е н е м prim2_2.pas.

2. И з м е н и т е п р о г р а м м у Му2_1 д л я н а х о ж д е н и я ц и ф р четырех-значного ч и с л а . Сохраните ее под и м е н е м Prim2_2.pas.

3. Деление на 10 и н а х о ж д е н и е остатков от деления м ы рассмот-рели в ы ш е . Р а с с м о т р и т е п р и м е р д е л е н и я с т о л б и к о м на 2 . О с т а т к и от д е л е н и я и л и 0, и л и 1.

Page 22: Pascal 8 Класс - Окулов

22 Часть первая

1IZ.L2 1 68. [2

О 2112 О 17 Ц

1 812. О 4 12.

О 2 Ц . О 1

Наберите следующую программу, отладьте ее и попробуйте дать объяснение полученному результату. Измените программу так , чтобы она правильно работала, например , с числом 115.

Program Му2_2; Var r e z : I n t e g e r ; Begin

WriteLn('137'); WriteLn('10001001'); Rez:=1 * 128+0*64 + 0*32+0*16+1*8 + 0*4 + 0*2 + 1 *1; WriteLn(rez); ReadLn;

End.

4. Наберите следующую программу:

Program My2_3; Uses Crt; Var a:Integer; b:Word; rl:Integer; r2:Longlnt; Begin

ClrScr;(Очистка экрана, процедура модуля Crt} а:=32000;b:=64 000; rl;=a+b; WriteLn(rl); r2:=a+b; WriteLn(r2); ReadLn;

End. После запуска Вы увидите, что значение переменной rl рав-

но 30464, а значение переменной г2 — 96000 . Б е л и изменить тип переменной rl на Word , то результат не изменится . Испо-льзуя информацию из таблицы, приведенной в начале з а н я т и я , измените программу так , чтобы проделать аналогичные экспе-рименты с другими ц е л ы м и типами . Попробуйте н а й т и логику получения результата компьютером.

5. Добавьте в программу Му2_3 перед оператором ReadLn сле-дующие два оператора:

Wri teLn (Longln t (1 00 *а)) ; Wri teLn (1 00*LongIn t (a)) ;

Page 23: Pascal 8 Класс - Окулов

Основные управляющие конструкции 23

Функция Longlnt преобразует переменную типа Integer в тип Longlnt. В первом случае преобразование осуществлялось после умножения, а во втором — перед умножением. В первом случае получен результат, далекий от истины, — отрицатель-ное число -11264, во втором правильный — 3200000.

Приведем основные правила, по которым в Турбо Паскале осуществляются операции с переменными целых типов.

Перед выполнением операций (бинарных) над двумя операнда-ми оба операнда преобразуются к общему для них типу. Им яв-ляется тип с наименьшим диапазоном, включающим все воз-можные значения обоих типов. Например, общим типом для Integer и Byte будет Integer, для Integer и Word, — Longlnt. Ре-зультат будет общего типа.

Выражение в правой части оператора присваивания вычисля-ется независимо от размера или типа переменной в левой части! Перед выполнением любой арифметической операции любой операнд длиной в 1 байт преобразуется в промежуточный опе-ранд длиной в 2 байта, который является совместимым как с Integer, так и с Word.

Задания для самостоятельной работы

1. Чему равны значения переменных а и b после выполнения по-следовательности действий: • а— 15 Div (16 Mod 7); b:= 34 Mod a* 5 — 29 Mod 5 + 2; • a:= 4 * 5 Dw 3 Mod 2; b~ 4 * 5 Div ( 3 Mod 2);

2. Дано двузначное число. Определить: • сумму и произведение цифр числа; • число, образованное перестановкой цифр исходного чис-

ла. 3. Дано трехзначное число. Определить:

• сумму и произведение цифр числа; • число, образованное перестановкой цифр исходного числа; • число, полученное перестановкой цифр десятков и еди-

ниц; • число, полученное перестановкой цифр сотен и десятков; • четырехзначное число, полученное приписыванием циф-

ры единиц в качестве цифры тысяч (например, из числа 137 необходимо получить число 7137).

П р и м е ч а н и е Сколько различных чисел можно получить из трехзначного числа путем перестановки цифр?

Page 24: Pascal 8 Класс - Окулов

24 Часть первая

4. Р е ш и т ь задачу 3 (кроме последнего пункта) д л я четырехзнач-ных чисел. П р и м е ч а н и е Предложить максимальное количество разумных модификаций рассматриваемой задачи.

5. Арифметическая прогрессия — это последовательность чисел, в которой разность м е ж д у последующим и п р е д ы д у щ и м эле-ментами остается неизменной. Последовательность 12, 15, 18, 21, 24, ... я в л я е т с я арифметической прогрессией, 12 — пер-вый член прогрессии ( а Д разность прогрессии равна 3. Лю-бой член прогрессии вычисляется по формуле a n = a j + d * ( n - l ) , где d — разность прогрессии, п — номер взятого члена . Д а н ы a j и d. Н а й т и п, при котором значение ап выходит за диапа-зон типа In t ege r ( экспериментальным путем) .

6. Сумма первых п членов арифметической прогрессии вычис-ляется по формуле Sn=(aj-(-an)*n/2. Д а н ы a j и d. Н а й т и п, при котором значение S n выходит за диапазон т и п а I n t e g e r (экс-периментальным путем).

Материал для чтения

1. В ы р а ж е н и я . Они состоят из операций и операндов. Разли-чают бинарные операции — в ы п о л н я ю т с я над д в у м я операнда-ми, и унарные (одноместные) — над одним операндом. Б и н а р -ные операции записываются в обычной математической форме, знак унарной операции предшествует операнду. П о р я д о к вы-полнения операций в в ы р а ж е н и и определяется п р а в и л а м и при-оритета. Приведем их .

П р и м е ч а н и е Если некоторые из операций Вам не очень понятны, то не отчаи-вайтесь. Всему свое время.

Операции Приоритет Тип операции | Not, +, - 1-й (высший) унарный

*, /, Div, Mod, And, Shi, Shr 2-й мультипликативный +, -, Or, Xor 3-й аддитивный

=,<>,<,>,<=, >= ,„ 4-й (низший) операции отношения

Сформулируем основные правила приоритета : • операнд, расположенный м е ж д у з н а к а м и двух операций с

р а з н ы м и приоритетами, я в л я е т с я границей операции с бо-лее высоким приоритетом;

Page 25: Pascal 8 Класс - Окулов

Основные управляющие конструкции 25

• операнд , р а с п о л о ж е н н ы й между д в у м я о п е р а ц и я м и с рав-н ы м и п р и о р и т е т а м и , я в л я е т с я границей д л я операции, р а с п о л о ж е н н о й слева;

• в ы р а ж е н и я в скобках в ы ч и с л я ю т с я до того, к а к они будут обрабатываться к а к один операнд.

2. Системы с ч и с л е н и я — способ записи чисел. Системы счис-л е н и я , в к о т о р ы х в к л а д к а ж д о й ц и ф р ы в величину числа зави-сит от ее п о з и ц и и в последовательности цифр , и з о б р а ж а ю щ е й число, н а з ы в а ю т с я п о з и ц и о н н ы м и . Ч и с л о в десятичной систе-ме счисления 3377=3*1000+3*100+7*10+7 =3*10 3 +3*10 2 +7*10 1 + +7*10° — в к л а д к а к ц и ф р 3, т а к и ц и ф р 7 различен . Это же ч и с л о в п я т е р и ч н о й с и с т е м е с ч и с л е н и я з а п и с ы в а е т с я к а к 102002 ( 3 3 7 7 = 1*5 5 + 0 * 5 4 + 2 * 5 3 +0*5 2 + 0 * 5 4 2 * 5 ° ) . П о д с к а ж е м , что 5 5 = 3 1 2 5 , а 5 3 = 1 2 5 . Это же число в двоичной системе счис-л е н и я з а п и с ы в а е т с я к а к 1 1 0 1 0 0 1 1 0 0 0 1 (3377 = 1 * 2 И +1*2 1 0 + + 0 * 2 9 + 1 * 2 8 + 0 * 2 7 +0*2® + 1 * 2 5 + 1 * 2 4 + 0 * 2 3 +0*2 2 + 0 * 2 ' +1*2°). П р и в е д е м з н а ч е н и я степеней двойки : 2 И = 2 0 4 8 , 2 1 0 = Ю 2 4 , 2 9 = = 512, 28 = 2 5 6 и т. д. А л г о р и т м перевода целого числа из деся-тичной системы с ч и с л е н и я в двоичную: делим исходное число на 2 н а ц е л о в десятичной системе счисления и считаем новым значением ч и с л а целую часть частного, остаток от деления , а это 0 и л и 1, з апоминаем; продолжаем процесс деления до тех пор, пока в результате не будет получен 0.

Пример: 3 3 7 7 : 2 = 1 6 8 8 (1) ^ 1 6 8 8 : 2 = 8 4 4 (0) 8 4 4 : 2 = 4 2 2 (0) 4 2 2 : 2 = 2 1 1 (0) 2 1 1 : 2 = 1 0 5 (1) 1 0 5 : 2 = 5 2 (1) 5 2 : 2 = 2 6 (0) 2 6 : 2 = 1 3 ( 0 ) 1 3 : 2 = 6 (1) 6 : 2 = 3 (0) 3 : 2 = 1 (1) 1:2=0 (1) В с к о б к а х у к а з а н о з н а ч е н и е о с т а т к а от д е л е н и я . И т а к ,

3 3 7 7 1 0 = 1 1 0 1 0 0 1 1 0 0 0 1 2 . М е т о д и ч е с к и е р е к о м е н д а ц и и Ученикам следует предложить выполнить порядка 15-20 упражне-ний по переводу целых чисел из десятичной системы счисления в двоичную и обратно.

Page 26: Pascal 8 Класс - Окулов

26 Часть первая

3. Представление целых чисел в памяти компьютера. Компь-ютер работает только с данными, представленными в двоичной системе счисления. Не имеет значения, какие это данные: текст, звук, рисунок, целые числа, они д о л ж н ы быть переведены в двоичное представление (закодированы). Д л я хранения одной двоичной цифры, а это 0 или 1, используется один бит (разряд) памяти компьютера. Запомните, что 8 битов называют 1 бай-том, 1024 байт — 1 килобайтом (1Кбайт), 1024Кбайт — 1 мега-байтом (1Мбайт), 1024Мбайт — 1 гигабайтом (1Гбайт). В 2 би-тах можно хранить 4 различные последовательности из 0 и 1: 00, 01, 10, 11 (22). (В 3 битах — 8: 000, 001, 010, 011, 100, 101, 110, 111 (23). А в 8 битах или 1 байте?) Если перевести эти 8 последовательностей из 0 и 1 из двоичной системы счисления в десятичную, то получим десятичные ц и ф р ы от 0 до 7. Пусть у нас есть 4 бита, а это 16 различных двоичных последовательно-стей, для хранения целых неотрицательных чисел. Итак , м ы можем хранить в этом случае числа из интервала от 0 до 15. Проверьте. А что делать, если необходимо хранить и отрицате-льные числа? У нас по-прежнему 4 бита памяти компьютера. Естественным шагом является выделение одного бита для хра-нения знака числа. Получаем, что, например, - 5 хранится как 1101, а 5 — 0101. И диапазон представления чисел в этом слу-чае от - 7 до 7, т. е. 15 различных значений, а у нас есть воз-можность хранить в 4 битах 16 различных последовательно-стей. Последовательность 1000 не задействована или , что еще более неприятно, она может трактоваться как - 0 , а это уже из разряда необъяснимых фактов, 0 и - 0 . Давайте научимся вы-полнять следующие действия: инверсию и прибавление 1 к дво-ичному числу. Рассмотрим на нашем примере.

Число Двоичное представление

Инверсия Прибавление 1

Отрицательное число I

1 0001 1110 1111 -1 2 0010 1101 1110 -2 3 0011 1100 1101 -3 4 0100 1011 1100 - 4 5 0101 1010 1011 -5 6 0110 1001 1010 -6 7 0111 1000 1001 - 7 8 1000 0111 1000 -8 0 0000 1111 10000 (пятый,

старший разряд «отбрасываем»)

0

Page 27: Pascal 8 Класс - Окулов

Основные управляющие конструкции 27

В предпоследнем столбце мы получаем двоичное представле-ние отрицательных чисел. Все 16 двоичных последовательно-стей задействованы, диапазон представления от - 8 до 7 и пред-ставление 0 однозначно. Представление отрицательных чисел в том виде, который приведен в таблице, называется дополните-льным кодом. Естественно, что представление положительных чисел в дополнительном коде совпадает с их обычным пред-ставлением. Сравните полученный результат с диапазоном зна-чений величин целого типа из таблицы, приведенной в начале занятия .

М е т о д и ч е с к и е р е к о м е н д а ц и и Вручную выполнить аналогичные действия при 5 и 6 разрядах, от-водимых для представления целых чисел.

Page 28: Pascal 8 Класс - Окулов

28 Часть первая

Занятие № 3. К о м а н д ы р е д а к т о р а для работы с блоками , работа с о к н а м и

План занятия • знакомство с командами редактора для работы с блоками; • знакомство с режимом Window системы программирова-

ния; • программа вычисления степеней двойки; • программа перевода чисел из десятичной системы счисле-

ния в двоичную систему счисления; • выполнение самостоятельной работы. Знакомство с командами редактора для работы, с блока-

ми. Блок — любой фрагмент текста программы. Одновременно в тексте может быть только один блок. Отмеченный блок выде-ляется на экране яркостью. Для того, чтобы что-то сделать с блоком, его необходимо выделить, т. е. отметить начало и ко-нец блока. После этого выполняются действия с блоком

Приведем основные команды редактора (режим Edit главного меню) для работы с блоками:

Ctrl

Ctrl

Ctrl

Ctrl

Ctrl

Ctrl

Ctrl

К К К К К К

К

К

К

В Отметить начало блока

К Отметить конец блока

Т Отметить одно слово как блок

с Скопировать блок

У Удалить блок

н Убрать выделение блока

V Переместить блок

я Ввести с диска ранее записанный блок, начиная с позиции курсора

W Записать отмеченный блок на диск

П р и м е ч а н и е 1. Последовательность выполнения команд: • устанавливается курсор в требуемое место текста; • нажимается клавиша Ctrl; • не отпуская клавишу Ctrl, нажать клавишу с буквой К; • отпустив клавишу с буквой К, не отпуская клавишу Ctrl, нажать

клавишу со второй буквой. 2. Знание команд работы с блоками значительно ускоряет набор Ваших программ. Используйте их при работе.

Page 29: Pascal 8 Класс - Окулов

Основные управляющие конструкции 29

Структура задания 1. Набор любого содержательного текста. Например. «На сче-

тах десятичное число кодируется положением костяшек на спицах, а в арифмометре (механическом вычислителе) чис-ло кодируется положением диска с 10 зубцами. С помощью таких связанных между собой дисков можно построить сум-мирующее устройство. Идея такого устройства принадлежит Леонардо да Винчи ( 1 4 5 2 - 1 5 1 9 ) . А впервые такое устройство сделал в 1642 году французский философ и математик Блез Паскаль (1623-1662) . Выдающийся американский математик Джон фон Нейман (1905-1957) при работе в составе группы исследователей над машиной «ЭНИАК» сформулировал принципы, лежащие в основе функционирования современных вычислительных ма-шин. Суть этих принципов: в памяти ЭВМ хранятся не толь-ко данные, но и обрабатывающая их программа; представле-ние в памяти данных и программы совпадает; программа должна выполняться последовательно команда за командой».

2. Выделение его фрагментов как блоков. 3. Копирование фрагментов текста (выделяется блок, курсор

устанавливается в требуемое место, выполняется команда копирования) .

4. Перемещение фрагментов текста (выделяется блок, курсор устанавливается в требуемое место, выполняется команда пе-ремещения).

5. Вставка фрагментов текста через диск (выделяется блок, за-дается команда записи на диск, при этом необходимо опре-делить имя блока, курсор устанавливается в требуемое место, выполняется команда чтения блока с диска).

Работа с окнами. В Турбо Паскале есть возможность рабо-ты с несколькими окнами (режим Window главного меню). Окно — ограниченная область экрана. Его размеры и положе-ние на экране можно изменять. Программа, а она хранится на диске как файл с определенным именем, размещается в опреде-ленном окне. Активным в текущий момент является только одно окно, в нем находится курсор.

Page 30: Pascal 8 Класс - Окулов

30 Часть первая

Команда Функциональная клавиша

Назначение

Tile Последовательное размещение окон

Cascade Каскадное размещение окон

Close All Закрытие всех окон

Size/Move Ctrl F5 Изменение размера, перемещение окна. При выполнении команды изменяется цвет рамки окна Для изменения размера окна используется комбинация клавиш J S/i/ft+ft, У, <=, =>. Для перемещения — ft, |j У, <=, =г Для завершения работы с окном i следует нажать клавишу Enter Цвет ' рамки окна изменится на | первоначальный |1

II Zoom F5 Размеры активного окна устанавливаются 1 равными полному экрану

Next F6 Переход к следующему окну |

Previ os Shift + F6 Переход к предыдущему окну '

II Close Alt + F3 Закрытие активного окна (

| Us t Просмотр списка открытых окон ц

Структура задания 1. Открыть все программы предыдущих занятий. 2. Выполнить перечисленные выше команды работы с окнами.

Экспериментальный раздел работы

1. Выполните обычные действия с программой МуЗ_1 (набор, компиляцию, запись на диск, запуск).

Program МуЗ_1; Uses Crt; Var rO,rl,r2,r3,r4,r5,гб:Integer; Begin

CIrScr; WriteLn ('Вычисляем степени числа 2'); г0:=1;rl:=2;г2:=rl*rl; r3:=r2*rl; г4:=г2*г2; r5:=rl *rl *rl *rl * rl ; r6:=r3*r3; WriteLn('Нулевая степень, ',г0); WriteLn('Первая степень, ' , r l ) ; WriteLn('Вторая степень, ' ,г2) ; WriteLn('Третья степень, ',гЗ); WriteLn('Четвертая степень, ',г4); WriteLn('Пятая степень, ',г5); WriteLn ('Шестая степень, ',г6); ReadLn;

End.

Page 31: Pascal 8 Класс - Окулов

Основные управляющие конструкции 31

П р и м е ч а н и е При наборе программы рекомендуется использовать изученные команды работы с блоками. Набирается r2:=rl*rl, а затем этот фрагмент выделяется как блок, копируется 4 раза и модифициру-ется. Аналогичные действия выполняются с операторами WriteLn. Давайте подсчитаем, за какое количество операций выпол-

няется , например , вычисление 5-й степени двойки в нашей программе — 4 операции. А можно ли вычислить за меньшее количество операций? Оказывается , «да», за 3 операции — г5~г2*г2*г1. Вычисление г2 требует одну операцию умноже-ния и две операции для вычисления результата.

Модифицируйте программу так , чтобы она вычисляла степе-ни двойки до 10-й включительно (за минимальное количество операций).

2. Выполните обычные действия с программой МуЗ_2 (набор, к о м п и л я ц и ю , запись на диск, запуск). П р и м е ч а н и е

При наборе программы рекомендуется использовать изученные команды работы с блоками и окнами. Скопируйте через диск текст программы МуЗ_1, запишите его как текст программы МуЗ_2, а затем измените его, максимально используя команды работы с блоками. Например, набирается s0~b Mod 2; b.=b Dw 2: и копируется столько раз, сколько необходимо. Program МуЗ_2;

Uses Crt; Var rO,rl,r2,r3,г 4,г5,гб:Integer;

s0,sl,s2,s3,s4,s5,s6:Integer; а,Ь,rez:Integer; one,dec,hun:Integer;

Begin ClrScr;{*Очистка экрана*} {*Вычисление степеней двойки*} rO: =1;rl:=2;r2:=rl*rl;гЗ:=r2*rl; r4:=r2*r2; г5:=гЗ*г2; г6:=гЗ*гЗ; W r i t e L n ('Введите число меньше 128 и больше 64') ; ReadLn(а);Ь:=а; (*Перевод числа в двоичную систему счисления*} s0:=Ь Mod 2; Ь : = Ь Div 2; sl:=b Mod 2; Ь:=Ь Div 2; s2:-b Mod 2; Ь : = Ь Div 2; s 3 r = b Mod 2; b:=b Div 2; s4: ~b Mod 2; b:~b Div 2; s5:=b Mod 2; b:=b Div 2; s6:~b Mod 2; b:=b Div 2; b: =a ;

Page 32: Pascal 8 Класс - Окулов

32 Часть первая

( * Выделение десятичных цифр в записи числа*) one:=Ь Mod 10; Ь:=Ь Div 10; dec:=b Mod 10; b:=b Div 10; hun:=b Mod 10; WriteLn ('Мы правильно выделили десятичные цифры') ;1

WriteLn ('Вывод числа в десятичной системе счисления',hun*100+dec*10+one); WriteLn (hun,dec,one) ; WriteLn ('Это же число в двоичной системе счисления'); WriteLn (s6,s5,s4,s3,s2,sl,s0); WriteLn (' Переводим число из двоичной систем счисления в десятичную'); Rez:=s6*r6+s5*r5 + s4*r4 + s3*r3-ts2*r2+sl *rl + s0*rC; (*Перевод*)

WriteLn('Все сделано правильно, числа совпадают'); Wnteln (а, ' ' , rez) ; Readln;

End. Введите числа больше, чем 128. Оцените результат работы

программы. Модифицируйте программу так, чтобы и в этом случае она была работоспособна.

Задания для самостоятельной работы

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

свойствам) структура для работы с данными. Она имеет имя, например А, и элементы из этой структуры выбираются по номеру (она последовательная). Так, запись А[10], говорит о том, что мы работаем с 10-м элементом иа-шей структуры. А задача заключается в том, чтобы, используя эту структуру данных, пред-ложить идею (нет, нет, не программу!) вычис-ления больших степеней двойки за минималь-ное количество операций.

1 При наборе программ в Турбо Паскале (здесь и далее по тексту) перено-сить строковую констапту па следующую строку нельзя.

Page 33: Pascal 8 Класс - Окулов

Основные управляющие конструкции 33

Структура — взаиморасположение и связь составных частей чего-либо (из словаря иностранных слов). Итак, когда мы гово-рим о структуре, то обязаны сказать о том, из каких элементов она состоит и как они (элементы) связаны между собой. Следу-ет также понимать, что структура обладает новыми свойства-ми, качествами по отношению к свойствам элементов, ее состав-ляющих. Если структуре дать какое-то имя, то мы получаем нечто новое. В информатике структура из однородных элемен-тов, расположенных последовательно, называется массивом.

2. Модифицируйте программу МуЗ_1 так, чтобы она вычисля-ла степени тройки.

3. Модифицируйте программу МуЗ_2 так, чтобы она осущест-вляла перевод чисел из определенного интервала в троичную систему счисления и обратно.

4. На предыдущем занятии в материале для чтения мы рассмот-рели представление отрицательных чисел в дополнительном коде. Пусть по-прежнему у нас только 4 разряда для представ-ления чисел. Необходимо найти 7 -3 или 7+(-3). Складыва-ем в двоичной системе счисления 0111+1101=10100. Первое двоичное число это 7, второе - 3 в дополнительном коде. Ре-зультат пятиразрядный, у нас 4 разряда, отбрасываем стар-ший разряд, получаем 0100, а это двоичная запись числа 4. Еще пример. Требуется найти 2-5 , или 2+(-5). В двоичной системе счисления 0010+1011=1101, а это запись - 3 в допол-нительном коде. Пусть Вам дано 6 разрядов для представле-ния целых чисел. Измените предыдущие операции так, чтобы они выполнялись и для этого случая. Разберите еще несколько аналогичных примеров.

В компьютере нет необходимости реализовать операцию вычи-тания целых чисел! Достаточно уметь складывать числа, инвер-тировать двоичное представление числа и прибавлять единицу к младшему разряду. А сейчас представим себе, что любое сло-жение можно реализовать путем последовательного прибавле-ния единицы к младшему разряду. Что остается? Инверсия и прибавление единицы к младшему разряду!

5. Подсчитайте сумму пятеричных чисел в интервале от 205 до 405, включая границы интервала.

Page 34: Pascal 8 Класс - Окулов

34 Часть первая

6. Восстановите цифры двоичного числа, на месте которых за-писан символ «*». 1**1+0011=1100. Придумайте и выполни-те еще несколько примеров такого типа.

7. Определите, является ли число 43015 четным? Найдите все четные числа в интервале от 43005 до 43405 .

8. В четверичной и восьмеричной системах счисления составить таблицы сложения и умножения.

9. Сравнить числа в различных системах счисления 3124 и 72g. Изменится ли результат, если вычесть из чисел соответствен-но 12„и 12g?

10. Существует ли система счисления с основанием х, в которой выполняются следующие равенства: З х +4 х =7 х , 3х*4х=13х и 39х+29х=70х? Ответ обосновать.

11. Возможно ли в какой-нибудь системе счисления с основанием х выполнение следующего равенства 600 х=21 l x +252 x i -53 x ? Ответ обосновать.

Материал для чтения

Попытаемся ответить на вопрос, что такое программа? Суще-ствуют различные, очень умные, научные трактовки этого тер-мина. Определим очень просто. Программа — это «откуда взять, что сделать, куда положить, а если это не так?» (А. Н. Венц «Профессия программист», 1999.) Правда, в книге таким обра-зом определено понятие алгоритма, но, на наш взгляд, оно бо-лее соответствует тому, что понимается под словом программа. Оставим пока в стороне нюансы терминологии. Пусть оно будет нашим рабочим определением. Вспомним разобранные програм-мы и ответим на вопросы. «Откуда взять» — мы пока берем только из файла, в который вводятся данные с клавиатуры компьютера, но есть и другие места, откуда можно взять исход-ные данные. «Что сделать» — например, в нашей первой про-грамме один оператор присваивания. «Куда положить» — от-вет на вопрос очевиден, пока этим местом является то, что связано с монитором нашего компьютера. И наконец, четвер-тый и, наверное, самый главный вопрос — «а если это не так». Мы видим, что даже в первой программе ответ не очевиден. Она работает не при всех исходных данных. Если Вы откомпи-лировали и один или два раза запустили Вашу программу и по-лучили правильные результаты, то это еще не значит, что у Вас есть работающая программа. Она пока лишь первое приближе-ние к программе. Итак, сформулируем один из основных прин-ципов нашей с Вами работы: «Все подвергай сомнению, все

Page 35: Pascal 8 Класс - Окулов

Основные управляющие конструкции 35

проверяй сам, ни одного факта на веру». Это относится не толь-ко к программам, но и к тому, о чем говорит учитель, к тому, что пишут в книгах по информатике!

Сформулируем еще один из принципов. Работа по схеме вос-произведения, пусть даже творческого, того, что написано в учебнике, или того, что говорит учитель, не приводит к успеху в информатике. Львиная доля успешности освоения предмета при-ходится на самостоятельную, кропотливую работу. А. Н. Венц в своей книге приводит формулу великого программиста (ВП), выведенную экспериментальным путем:

ВП=50%К+30%Т+10% 0+5% 3+ 5%ТЛ, где К — знать, как это делать, Т - трудолюбие, О — опыт, 3 — знание, ТЛ — та-лант.

На то, что относится к вундеркиндам, только 5%, осталь-ное — труд, ежедневный труд. А сейчас подвергните сомнению эту формулу. Автор, например, не очень понимает разницу между терминами: «знать, как ...» и просто «знание».

Продолжим уточнение термина программа. Языков програм-мирования существует великое множество, но, на каком бы из них Вы ни работали, при создании программы необходимо вы-полнить следующее:

• ввести данные в программу (ввод); • определить представление этих данных в памяти компью-

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

условиях (условия); • часть из них выполняется несколько раз (циклы); • часть из них допускает разбивку на блоки и выполняется

в различных частях программы (подпрограммы). Итак, эти семь элементов ввод, данные, операторы, вывод,

условия, циклы, подпрограммы, являются основными при созда-нии небольших программ на любом языке программирования. Однако следует заметить, что рассмотренное уточнение пони-мания того, что есть программа, относится примерно к 1975 году, да и то с некоторыми оговорками. Нет, например, того, что понимается под термином технологии программирования. Мы не говорим (пока) о том, как из этих элементов «склеивает-ся», собирается программа (об этом позднее), а реальные про-граммы, по оценкам экспертов, намного сложнее автомобилей. И не только об этом. Абсолютно не затронут вопрос о том, как

Page 36: Pascal 8 Класс - Окулов

36 Часть первая

программа должна реагировать на события во внешней среде и многое другое. Таким образом, наша трактовка работы про-граммы сводится к некоему последовательному процессу, а это не всегда соответствует действительности. Однако «нельзя объ-ять необъятное», программирование — сложнейший раздел ин-форматики, сложнейшая отрасль производства, а учебник по-священ «основам программирования». В материалах для чтения мы сделаем обзор технологий программирования для того, что-бы составить общее представление о том, какой «виток» спира-ли развития технологий программирования изучается.

Page 37: Pascal 8 Класс - Окулов

Основные управляющие конструкции 37

З а н я т и е № 4 . Л о г и ч е с к и й т и п д а н н ы х , о п е р а ц и и с д в и г а

План занятия • обсуждение логического типа данных; • обсуждение операций сдвига; • эксперименты с программами вывода таблиц истинности,

выполнения операций сдвига и логических операций; С выполнение самостоятельной работы. Логический тип данных. Переменные логического типа опи-

сываются с помощью идентификатора Boolean. Диапазон значе-ний — два: False (ложь) или True (истина), размер выделяемой памяти — 1 байт (False и True — стандартные константы). Тип янляется перечислимым, поэтому: False<True, Ord(False)=0, Ord(True)=l, Succ(False)=True, Pred(True)=False.

Перечислим четыре логические операции, реализованные н Турбо Паскале: логическое сложение, или дизъюнкция, — Or; логическое умножение, или конъюнкция, — A n d ; отрицание — Not, исключающее «Или» (сложение по модулю два) — Хог. Результаты выполнения операций над переменными логиче-ского типа х и у приведены в таблице.

Значение операнда Значение операции ]

х У Not x Andy x Or у xXory

False False True False False False

False True True False True True

True False False False True True |j

1 True True False True True False j

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

Таблица истинности представляет собой таблицу, устанавлива-ющую соответствие между возможными значениями наборов переменных и значениями операции.

Следует четко понимать, что результатом выполнения опе-раций сравнения (отношения): «<» (меньше), «>» (больше), «<=» (меньше или равно), «>=» (больше или равно), «<>» (не равно), « = » (равно) является величина логического типа. Ее

Page 38: Pascal 8 Класс - Окулов

38 Часть первая

значение равно True, если отношение в ы п о л н я е т с я д л я значе-н и й в х о д я щ и х в него операндов, и False — в противном случае .

В я з ы к е Турбо П а с к а л ь нет в о з м о ж н о с т и ввода л о г и ч е с к и х д а н н ы х с п о м о щ ь ю оператора Read. Однако предусмотрен вы-вод з н а ч е н и й п е р е м е н н ы х логического т и п а с п о м о щ ь ю опера-тора Write.

Операции сдвига. Р е ч ь идет о двух о п е р а ц и я х : Shi — сдвиг влево и Shr — сдвиг вправо . Тип операндов и р е з у л ь т а т а в опе-р а ц и я х сдвига Integer. И т а к , т. Shi п — значение m сдвигается влево на п разрядов ; а п р и т Shr п — значение m сдвигается вправо на п р а з р я д о в . П р и в ы п о л н е н и и о п е р а ц и й р а з р я д ы , вы-ш е д ш и е за пределы области п а м я т и , в ы д е л я е м о й д л я т и п а дан-н ы х , т е р я ю т с я , а с другой стороны д о б а в л я ю т с я н у л и . Н а п р и -мер, если m равно 32, то сдвиг влево на один р а з р я д дает 64, а сдвиг вправо — 16. Операции р а в н о с и л ь н ы у м н о ж е н и ю и деле-нию на два .

Экспериментальный раздел работы

1. В ы п о л н и т е обычные действия с п р о г р а м м о й Му4_1 (набор, к о м п и л я ц и ю , запись на диск , запуск) .

Program Му4_1; Uses Crt; Var a ,b:Boolean; Begin

ClrScr; a:=True;b:=True;WriteLn(a:6,b: 6,a And b: 6) ; a:=True;b:=False;WriteLn(a:6,b:6,a And b: 6) ; a:=False;b:=True;WriteLn(a:6,b:6,a And b: 6) ; a:=False;b:=False;WriteLn(a:6,b:6,a And b:6); ReadLn;

End.

П р и м е ч а н и е При наборе программы не забывайте использовать команды рабо-ты с блоками. Набирается a:=True;b:=True;WriteLn(a.6,b:6,a And b:6); а затем этот фрагмент выделяется как блок, копируется 3 раза и модифицируется. Измените программу для проверки остальных рассмотренных в ы ш е логических операций .

2. Выполните обычные действия с программой Му4_2 (набор, к о м п и л я ц и ю , запись на диск , запуск) .

Page 39: Pascal 8 Класс - Окулов

Основные управляющие конструкции 39

Program Му4_2; Uses Crt; Var т,п:Integer; Begin

ClrScr; WriteLn('Введите число и количество сдвигов'); ReadLn (m,n); WriteLn(' При сдвиге на ' , п , ' разрядов влево числа ',т,' получаем число ',т Shi п), WriteLn ('Введите число и количество сдвигов'); ReadLn (m,n); WriteLn (' При сдвиге на ' ,п,' разрядов вправо числа ' ,т,' получаем число ' ,т Shr п) ; ReadLn;

End. Введите в том и другом случае числа 32 и 1, убедитесь, что

получаются числа 64 и 16. Сдвиги вправо отрицательных чисел приводят к интересным результатам. Например , если Вы введе-те - 1 и 1 д л я того и другого сдвигов, то получите - 2 и 32767 . Если первый результат вполне объясним, то второй требует вспомнить о представлении отрицательных ц е л ы х чисел в до-полнительном коде. Пусть у нас не шестнадцать разрядов для представления чисел (тип Integer), а 4. Представление - 1 в до-полнительном коде есть 1111 2 . Сдвиг вправо на один разряд приводит к числу 0111 2 , а это не что иное, к а к 7 1 0 .

3. Выполните набор программы Му4_3, набрав только первые т р и оператора WriteLn). Откомпилируйте ее, з а п и ш и т е на диск .

Program Му4_3; Uses Crt; Begin

ClrScr; WriteLn(1365 And 2730); WriteLn(1365 Or 2730); WriteLn (1365 Xor 2730); WriteLn (1365 And $FF) ; WriteLn(1365 And $FF0); WriteLn(1365 And $FF00); ReadLn;

End.

Page 40: Pascal 8 Класс - Окулов

Часть первая

М ы видим, что с величинами типа Integer можно выполнять логические операции, они выполняются поразрядно над двоич-ными представлениями чисел. Почему выбраны числа 1365 и 2730? Двоичное представление этих чисел имеет вид: 136510=0101010101012, 2730i0=1010101010102 (рассматрива-ются только 12 младших разрядов). Операция And, дает в резу-льтате число 0, а операции Or и Хог — 4095. Поэксперименти-руйте с этой версией программы. Убедитесь, например, что -256 And 256=0, а -256 Or 256=-1 и -256 Хог 266—1. Попы-тайтесь дать разумное объяснение этому результату.

Добавьте к программе следующие три оператора WriteLn. В шестнадцатеричной системе счисления для обозначения цифр 10, 11, 12, 13, 14, 15 используются соответственно буквы ла-тинского алфавита А, В, С, D, Е, F. Двоичное представление F — 11112. Знак $ означает, что величина (константа) записана в шестнадцатеричной системе счисления. Запустите програм-му. Убедитесь в том, что результат равен 85, 1360, 1280. Его правильность подтверждается выделением соответствующих раз-рядов из числа 0101010101012 и переводом остатка в десяти-чную систему счисления. Исследуйте описанным способом пред-ставление в дополнительном коде отрицательных целых чисел.

Задания для самостоятельной работы

1. В математической логике известна функция следования, или импликация, (х=>у), ее таблица истинности имеет вид

X - Y X^y False False True False True True True False False True True True

Проверьте, что х=>у эквивалентно Not(x) Or у. Составьте программу проверки эквивалентности этих двух логических функций. 2. В математической логике известна функция Шеффера (х I у),

ее таблица истинности имеет вид

X " У xly False False True False True True True False True True True False

Page 41: Pascal 8 Класс - Окулов

Основные управляющие конструкции 41

Проверьте, что х | у эквивалентно Not(x) Or Not(y). Соста-вьте программу проверки эквивалентности этих днух логиче-ских функций.

3. В математической логике известна функция Вебба, или стрел-ка Пирса, (xlly) ее таблица истинности имеет вид

X У XUy

False False True

False True False

True False False

[ True True False

Проверьте, что xlly эквивалентно Not(х) And Not(у). Соста-вьте программу проверки эквивалентности этих двух логиче-ских функций.

4. Дана логическая функция , например, (x=>y)=>z. Построить таблицу истинности данной функции. Схема построения при-ведена в таблице. В первом столбике приведены возможные значения наборов переменных х, у и z (значение True обозна-чено как единица, значение False — как нуль).

II X=>y (x=>y)=>z

0 0 0 1 0

0 0 1 1 1

0 1 0 1 0

0 1 1 1

1 0 0 0 1

1 0 1 0 1

1 1 0 1 0

I 1 1 1 1 1

Преобразуйте эту формулу в эквивалентную ей. Составьте программу проверки эквивалентности этих двух логических формул.

5. Постройте таблицы истинности для следующих функций: • ( x | y ) | z ; • (xlly)Uz; • (х=>у) And z; • Not (x Or Not(y) And z); • x And Not(у Or Not(z)); • Not(Not(x) Or у And z).

Page 42: Pascal 8 Класс - Окулов

42 Часть первая

6. Дана логическая функция (xUy)U(zUv). Построить таблицу ис-тинности. Возможных значений наборов переменных х, у, z и v в данной задаче шестнадцать. Обозначим значение True единицей, а значение False — нулем.

х у ZV хНу zUv (xUy)U(zUv)

0 0 0 0 1 1 0

0 0 0 1 1 0 i 0

0 0 1 0 1 0 0

0 0 1 1 1 j 0 | 0

0 1 0 0 о | 1 ; о

0 1 0 1 0 0 1

0 1 1 0 0 0 1

0 1 1 1 о ! о 1

1 0 0 0 0 I 1 I 0

1 0 0 1 0 I 0

1 0 1 0 0 | 0 1 ll

1 0 1 1 0 о 1 !

1 1 0 0 0 1

1 1 0 1 0 0

1 1 1 0 0 0 1

I 1 1 1 1 0 0 ' 1 ;!

Материал для чтения

1. Слово «логика» употребляется в разных значениях, на-пример логика событий, логика характера и т. д. В этом случае имеется в виду определенная последовательность и взаимозави-симость событий или поступков. Слово «логика» употребляет-ся и в связи с процессами мышления. Когда говорят о логич-ном мышлении, то рассматривают его последовательность, дока-зательность и т. д. Итак, логика — особая наука о мышлении. Основателем ее считается древнегреческий философ Аристо-тель (IV в. до н. э.). Позднее она стала называться формальной логикой, и ее цель на протяжении всей истории развития неиз-менна: исследование того, как из одних утверждений можно выводить другие, при этом считается, что правильность рас-суждения определяется только его логической формой и не за-висит от конкретного содержания входящих в него рассужде-ний. В XIX веке благодаря усилиям английского ученого Джор-джа Буля возникла наука математическая логика. Джордж Буль перенес на логику законы и правила алгебраических дей-ствий, ввел логические операции, предложил способ записи вы-

Page 43: Pascal 8 Класс - Окулов

Основные управляющие конструкции 42

о к а з ы в а н и й в символической форме. Алгебра логики — раздел математической логики , и зучающей строение (форму, структу-ру) с л о ж н ы х в ы с к а з ы в а н и й и способы установления их истин-ности с помощью алгебраических методов. Высказывание — повествовательное предложение , относительно которого можно сказать , истинно оно или ложно. Все в ы с к а з ы в а н и я условно разделяются на простые и сложные, или составные. Составные в ы с к а з ы в а н и я образуются из простых. В ы с к а з ы в а н и я х и у — простые, в ы с к а з ы в а н и е х And у — составное, оно называется к о н ъ ю н к ц и е й и имеет 4 логические возможности, рассмотрен-ные в ы ш е , д л я определения возможности его истинности. Вы-сказывание х Or у тоже составное и называется д и з ъ ю н к ц и е й .

2. Алгебра логики и ее законы. Операции алгебры: конъюнк-ция , д и з ъ ю н к ц и я и отрицание. Эти операции позволяют произ-водить тождественные преобразования логических выражений .

Законы: • закон одинаковости: х Or х = х ; х And х = х ; • закон коммутативности : х Or у=у Or х , xAnd у=у And х; • закон ассоциативности: х Ог(у Or z)=(x Or у) Or z, x And(у

And z)=(x And y) And z; • законы дистрибутивности: x And (y Or z )=x And у Or x

And z — первый; x Or у And z=(x Or у ) A n d ( x Or z) — вто-рой;

• закон двойного отрицания : Not(Not(x))=x; • з а к о н ы де Моргана: Not(x) Or Not(y)=Not(x And у); • з а к о н ы поглощения : х Or х And у=х ; х And (х Or у)=х; • з аконы, определяющие действия с логическими констан-

т а м и False и True: х Or False=x; х And False=False; х Or True=True\ x And True=x~, Not(False)=True; Not (True )= =False; Not(x) Or x=True; Not(x) And x=False.

Дополнительные законы (они выводятся из основных зако-нов):

• з аконы склеивания : х And у Or Not(x) And у=у; (х Or у) And (Not(x) Or y)=y;

• закон Блейка-Порецкого: x Or Not(x) And y = x Or y; • закон свертки логического в ы р а ж е н и я : х And у Or Not(x)

And z Or у And z=x And у Or Not(x) And z. П р и м е ч а н и я 1. Приведем пример вывода для закона Блейка-Порецкого: X Or Not(x) And у= X And True Or Not(x) And y= x And (y Or Not(y)) Or Not(x.) And y= x And у Or x And Nof(y) Or Not(x) And y= x And у Or x Arcd Nof(y) Or Not(x) And у Or x And y= x Or y.

Page 44: Pascal 8 Класс - Окулов

44 Часть первая

2. Тип упражнений для закрепления материала может быть следу-ющим. Дается логическое выражение, например, Not(Not(x) Or Not(у))=? И варианты ответов: Not(x) Or у или х Or у и т. д., необ-ходимо выбрать правильный ответ.

3. Л о г и ч е с к и е ф у н к ц и и м о ж н о преобразовать в две ра злич -ные ф о р м ы :

• д и з ъ ю н к т и в н у ю н о р м а л ь н у ю ф о р м у (ДНФ) ; • к о н ъ ю н к т и в н у ю н о р м а л ь н у ю ф о р м у ( К Н Ф ) . В первом случае л о г и ч е с к а я ф у н к ц и я з а п и с ы в а е т с я в виде

д и з ъ ю н к ц и и к о н ъ ю н к ц и й , о б р а з о в а н н ы х и з п е р е м е н н ы х и и х о т р и ц а н и й . Во втором случае наоборот.

Примеры: Not(x) Or у And z, х And у And z Or Not(y) And Not (z) —

Д Н Ф , a x And (y Or Not(z)) - н е т , ( N o t ( x ) Or y) And z, x And у And (z Or Not(v)) — К Н Ф , x And (y And z Or Not(v)) — нет .

В с я к а я с л о ж н а я л о г и ч е с к а я ф у н к ц и я м о ж е т быть преобра-зована к а к к Д Н Ф , т а к и к К Н Ф . А л г о р и т м п р е о б р а з о в а н и я :

• з аписать ф у н к ц и ю с и с п о л ь з о в а н и е м т о л ь к о о п е р а ц и й Or, And, Not-,

• с п о м о щ ь ю законов де Моргана о п е р а ц и ю о т р и ц а н и я дове-сти до о т д е л ь н ы х п е р е м е н н ы х и убрать в ы р а ж е н и я т и п а Not(Not(x)) по закону двойного о т р и ц а н и я ;

• с п о м о щ ь ю первого закона дистрибутивности убрать все к о н ъ ю н к ц и и д и з ъ ю н к ц и й и провести п о г л о щ е н и е .

В результате п о л у ч и м Д Н Ф п р е д с т а в л е н и я л о г и ч е с к о й фун-к ц и и . Д л я п о л у ч е н и я з аписи в виде К Н Ф следует и з м е н и т ь тре-тий п у н к т а л г о р и т м а :

• с п о м о щ ь ю второго з а к о н а дистрибутивности убрать все д и з ъ ю н к ц и и к о н ъ ю н к ц и й и провести п о г л о щ е н и е .

Если все к о н ъ ю н к ц и и в Д Н Ф содержат все л о г и ч е с к и е пере-менные и л и и х о т р и ц а н и я , то Д Н Ф н а з ы в а е т с я совершенной . Аналогично определяют и совершенную К Н Ф .

(x=>y)=>Not(z)

00 1 0 1 о 0 1 1 1 о о

Page 45: Pascal 8 Класс - Окулов

Основные у п р а в л я ю щ и е конструкции 4 5

Рассмотрим построение совершенных Д Н Ф и К Н Ф на незна-чительно измененном 4-м примере из раздела самостоятельной работы з а н я т и я . Дана логическая ф у н к ц и я (x=>y)=>iVot(z). По-строим таблицу истинности данной ф у н к ц и и .

СДНФ ((х=>у) =>Not (z) ) =Not (х) And Not (у) AndNot(z) Or Not (x) And у And Not (z) Or x And Not (у) And Not (z) Or x And Not (y) And (z) Or x And у And Not (z) .

СКНФ( (x=>y)=>Not (z) ) =Not (Not (x) And Not (y) And z) And Not (Not (x) And у And z) And Not (x And у And z) = (x Or у Or Not (z)) And (x Or Not (y) Or Not (z)) And (Not (x) Or Not (y) Or Not ( z ) ) .

Page 46: Pascal 8 Класс - Окулов

46 Часть первая

З а н я т и е № 5 . С о с т а в н о й о п е р а т о р и о п е р а т о р If

План занятия • обсуждение операторов; О э к с п е р и м е н т ы с п р о г р а м м а м и н а х о ж д е н и я н а и б о л ь ш е г о

из двух и трех чисел; • выполнение самостоятельной работы.

Составной оператор. Он состоит из р я д а операторов, вы-п о л н я е м ы х в той последовательности, в которой они з а п и с а н ы в программе . Его схема:

Begin оператор; оператор; оператор; End; П р и м е ч а н и е Разделитель «;» перед End можно не записывать.

Оператор I f , или, условный оператор. Оператор записыва-ется следующим образом:

Выполнение условного оператора начинается с в ы ч и с л е н и я значения логического в ы р а ж е н и я , записанного в условии . Про-стые условия записываются в виде равенств и л и неравенств . Сложные условия составляют из простых с п о м о щ ь ю логиче-ских операций. Как известно, значением логического в ы р а ж е -ния является и л и True, и л и False. В первом случае выполняется соператор 1>, во втором — соператор 2>. В качестве <опера-тор 1> и л и < оператор 2> может выступать любой оператор я з ы к а программирования Турбо П а с к а л ь , в частности и состав-ной оператор, и условный оператор. В последнем случае полу-чаемая к о н с т р у к ц и я называется в л о ж е н н ы м и условными опе-раторами. Допускается запись неполного условного оператора, без ветви Else. В этом случае при значении False н и к а к и х дей-ствий не производится . В записи условных операторов возника-ет неоднозначность типа :

If <условие 1> Then <оператор 1> If <условие 2> Then соператор 2> Else соператор 3>;

Неясно , к к а к о м у оператору If относится ветвь Else. Она (не-однозначность) разрешается по следующему правилу : «Else от-носится к б л и ж а й ш е м у оператору I f , у которого еще отсутству-ет данная ветвь».

Page 47: Pascal 8 Класс - Окулов

Основные управляющие конструкции 47

Экспериментальный р а з д е л р а б о т ы

1. Разбор оператора м о ж н о выполнить на следующем простом

примере .

Вывести на экран наибольшее из двух д а н н ы х чисел.

Program МуЬ_1; Var х,у: Integer; Begin

WriteLn ('Введите 2 числа'); ReadLn(х,у); I f х>у Then WriteLn(<)Else WriteLn(у); ReadLn;

End. Поставьте «;» после оператора WriteLn(x). Убедитесь, что по-

явилась ошибка «Error 113: Error in statement». Конструкция (оператор) If — Then — Else неделима, поэтому разделитель «;» недопустим. В случае равенства чисел Ваша программа выводит значение переменной у. Измените программу так , чтобы в этом случае она выводила на экран сообщение «Числа равны».

Попробуем найти наибольшее из трех чисел — значения пе-ременных х , у и z. Предположим, что нет равенств, т. е. все числа различны. Возможны шесть различных случаев, они при-ведены на рисунке .

Программа определения значения наибольшего из трех чи-сел имеет вид

Program Му5_2; Var х,у, z : Integers-Begin

WriteLn ('Введите три числа через пробел'); ReadLn (х,у, z) ;

Page 48: Pascal 8 Класс - Окулов

48 Часть первая

I f (х>у) And (x>z) Then WriteLn (у) Else I f (y>x) And (y>z) Then WriteLn (y)

Else WriteLn (z) ; {* I f x>y Then I f a>z Then WriteLn (a)

Else WriteLn (z) Else I f y>z Then WriteLn (y)

Else WriteLn(z);*} ReadLn;

End. Вторая версия решения заключена в фигурные скобки (ком-

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

Задания для самостоятельной работы

1. Имеется условный оператор: If DolO Then wnteln('ypa.r) Else Writeln( плохо...')-, Можно ли заменить его следующими опе-раторами:

I f D=10 Then Writeln I'ура 1 ' ) Else Writeln ('плохо... ' ) ;

I f Not (D—l 0) Then Writeln Сура ' ' ) Else Writeln('плохо. • . ' ) ;

I f Not (D=l 0) Then Writeln ('плохо. . . ' ) Else Wn teln ('ура ! ' ) ;

I f Not (DO10) Then Writeln ('плохо...') Else Writeln ('ура!').

2. Запишите условный оператор, в котором значение перемен-ной вычисляется по формуле: а+Ъ, если а — нечетное и а*Ь, если а — четное.

3. Вычислить значение функции:

ж2 + 5, при х> 3 х - 8, при х < 3

4. Вывести на экран номер четверти, которой принадлежит точ-ка с координатами (х,у), при условии, что х и у отличны от 0.

5. Дано двузначное число. Написать программу определения: • является ли сумма его цифр двузначным числом;

Page 49: Pascal 8 Класс - Окулов

Основные управляющие конструкции 49

• больше ли числа X сумма его цифр, число X вводится с клавиатуры;

• кратна ли шести сумма его цифр; • больше ли цифра десятков цифры единиц; • оканчивается ли число цифрой 5. Придумать не менее 8 аналогичных задач с трехзначными числами.

6. Написать фрагмент программы, подсчитывающий сумму то-лько положительных из трех данных чисел.

7. Даны три числа. Написать фрагмент программы, подсчиты-вающий количество четных чисел.

8. Дано трехзначное число. Написать программу определения является ли оно палиндромом («перевертышем»), т. е. числом, десятичная запись которого читается одинаково слева напра-во и справа налево.

9. Если целое число М делится нацело на целое число N, то вы-вести на экран частное от деления, в противном случае — со-общение «М на N нацело не делится».

10. Составьте программу, которая уменьшает первое число в пять раз, если оно больше второго по абсолютной величине.

11. Вычислить значение функции:

12. Даны три целых числа, найти среднее из них. Средним на-зовем число, которое больше наименьшего из данных чисел, но меньше наибольшего.

13. Составьте программу нахождения произведения двух наибо-льших из трех введенных с клавиатуры чисел.

14. Найти количество положительных (отрицательных) чисел среди четырех целых чисел А, В, С и D.

15. Дано двузначное (трехзначное) число. Написать программу определения: • входит ли в него цифра 5; • входят ли в него цифры 4 и 7; • входят ли в него цифры 3, 5, 7.

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

х - 12, при ж > 0 5, при х = 0 хг, при х < 0

Page 50: Pascal 8 Класс - Окулов

50 Часть первая

17. Известна т е к у щ а я дата. Пользователь вводит день, месяц и год своего рождения . Написать программу, определения , ис-полнилось или нет пользователю полных 16 лет.

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

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

20. Составьте программу вычисления в ы р а ж е н и я • max(x+y+z , xyz)+3; • min(x 2 +y 2 , y 2 +z 2 ) - 4, если x,y,z вводятся с клавиатуры.

21. Составьте программу, которая из трех введенных с клавиа-туры чисел возводит в квадрат положительные, а отрицате-льные оставляет без изменения .

Page 51: Pascal 8 Класс - Окулов

Основные управляющие конструкции 51

22. Д а н ы два конверта прямоугольной формы с длинами сторон (а,Ь) и (c,d). Определить, можно ли один из конвертов вложить в другой?

23. Составьте программу, которая определяла бы вид треуголь-н и к а по длинам его сторон a, b и с (если данные отрезки по-зволяют его построить) . Н а п о м н и м , что условием того, что треугольник может быть составлен, является одновременное выполнение следующих условий: a+b>c, Ь+с>а, а+с>Ь, а так-ж е то, что треугольники могут быть разными: равносторон-ними, равнобедренными, прямоугольными, равнобедренны-ми п р я м о у г о л ь н ы м и и т.д. Кроме того, следует учесть, что в качестве длин сторон могут быть случайно введены к а к ну-левые, так и отрицательные значения . П р и м е ч а н и е

Эту, хорошо известную задачу, иногда называют тестом Г. Майер-са на профессиональную пригодность (из книги «Искусство тести-рования программ»). Если Вы сможете реализовать в своей про-грамме порядка 10 различных ситуаций, то Вам следует выбрать программирование своей специальностью

Материал для чтения

1. Давайте обсудим то, чем м ы пытаемся заниматься , т. е. программирование . Определение: «Программирование — тео-ретическая и практическая деятельность по обеспечению про-граммного управления обработкой данных, включающая созда-ние программ, а также выбор структуры и кодирования данных». Попробуем определить несколько иначе, более конструктивно, рассматривая то, чем занимается программист. Есть задача или проблема. В первую очередь программист должен определить возможность ее решения , выбирая соответствующий метод. За-тем разработать проект программы, состоящий из алгоритма на каком-либо из языков программирования, доказать правильность работы программы и предусмотреть возможность ее изменения, внесения изменений на этапе сопровождения. Таким образом, в укрупненном виде мы видим три этапа: до программирования, программирование и после программирования . Только часть работы связана с выбором структур данных и кодированием — использованием языков программирования. Программирование есть, если так можно выразиться , инженерная работа по конст-руированию некой целостной системы обработки данных. От-личие программы, например, от некоторой механической сис-темы в том, что число взаимодействующих частей в программе настолько велико, что не поддается никакому разумному объ-

Page 52: Pascal 8 Класс - Окулов

52 Часть первая

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

«Технология — совокупность методов обработки, изготовле-ния, изменения состояния, свойств, форм сырья, материала или полуфабриката в процессе производства или наука о спосо-бах воздействия на сырье, материалы или полуфабрикаты соот-ветствующими орудиями производства» (словарь иностранных слов). Приведем более подходящее определение. «Технология — это способ реализации людьми конкретного сложного процесса путем разделения его на систему последовательных взаимосвя-занных процедур и операций, которые выполняются более или менее однозначно и имеют целью достижение высокой эффек-тивности. Под процедурой понимается набор действий (опера-ций), посредством которых осуществляется тот или иной главный процесс (или его отдельный этап), выражающий суть конкретной технологии, а операция — это непосредственное практическое решение задачи в рамках данной процедуры, т. е. однородная логически неделимая часть конкретного процесса». В нашем случае технология должна поддерживать все три этапа работы программиста, о которых речь шла выше, ибо технология в данном случае есть искусство, мастерство изготовления, конст-руирования систем обработки данных. Обычно, когда говорят о технологиях программирования, то имеют в виду этап разра-ботки программ, первый и третий этапы не рассматриваются. Действительно, можно считать этот этап решающим, качест-венный выход на этом этапе обеспечивает достаточную просто-ту третьего этапа. Его часто трактуют как этап сопровождения, но мы вкладываем более широкий смысл — эволюционное из-менение системы обработки данных.

2. Поговорим о простых и сложных программах. Первые имеют ограниченную область применения, разработаны и со-провождаются одним человеком. Они могут быть профессиона-льно изготовлены, но так или иначе они обозримы, и их слож-ность не превышает интеллектуальные возможности человека. Но не все программы простые. Программы управления желез-нодорожными или воздушными сообщениями, системы управ-ления базами данных, обеспечивающих параллельный доступ многих пользователей и т. д. — это другой класс программ по уровню их сложности. Считается, что сложность программ от-

Page 53: Pascal 8 Класс - Окулов

Основные управляющие конструкции 53

нюдь не случайное свойство, скорее необходимое. Где пролегает граница между простым и сложным? Обычно сложность про-грамм определяется количеством операторов исходного текста программы. Это условная градация, простая количественная мера, которую можно принять в качестве первого приближе-ния. Известны разработки программ с менее 10000 оператора-ми исходного текста, относящиеся по выполняемым функциям к сложным и даже сверхсложным.

Программа Количество операторов исходного текста (до)

Простая 1000

Средней сложности 10 000

Сложная 100 000

Сверхсложная 1 000 000 I

Гиперсложная 10 000 000 и более !

Ограничимся интуитивным пониманием простого и сложно-го. Философский анализ этих понятий применительно к про-граммам — предмет отдельного разговора. Отметим еще одну особенность больших программ. Они имеют тенденцию к эво-люции в процессе их использования. Это часто называют сопро-вождением, но сопровождение есть устранение ошибок, а эволю-ция подразумевает внесение в программу изменений, обуслов-ленных изменяющимися требованиями к ней.

Как «бороться» со сложностью? Принцип известен со времен древних греков — «разделяй и властвуй». Сложную задачу сле-дует разделить на взаимосвязанные подзадачи. Последние в свою очередь опять разделяются на свои подзадачи и т. д., вплоть до самых низших уровней нашего понимания задачи. Что мы имеем? Во-первых, сложная задача (проблема, система) описывается некой иерархической структурой, во-вторых, опре-деляются принципы ее декомпозиции и, в-третьих, так как каж-дый уровень — это определенный уровень абстрагирования, со-здается инструментарий для описания абстракций. Иерархия — это ранжированная , или упорядоченная, система абстракций, расположение частей или элементов целого в порядке от вы-сшего к низшему. В результате деятельности программиста со-здается иерархическая структура из абстракций, а «абстрак-ция — это такие существенные характеристики некоторого объекта, которые отличают его от всех других видов объектов и, таким образом, четко определяют особенности данного объ-екта с точки зрения дальнейшего рассмотрения и анализа». Г. Бруч разделяет абстракцию сущности объекта — объект

Page 54: Pascal 8 Класс - Окулов

54 Часть первая

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

3. В этом и последующих материалах для чтения мы коснем-ся истории развития технологий программирования. Операцио-нальное программирование. Этот этап развития технологий про-граммирования характерен для ЭВМ первого поколения (с 1945 до 1959 года). Быстродействие ЭВМ этого поколения до 50 ты-сяч арифметических операций, объем оперативной памяти в лучшем случае несколько килобайт ячеек. Ресурсы минималь-ны. Если сравнивать эти характеристики ЭВМ с современными компьютерами: быстродействие — миллиарды операций в се-кунду, объемы памяти — мегабайты, то различие поразитель-но. ЭВМ того времени понимала только цифровые команды, и программа состояла из множества строк, состоящих из цифр, интерпретируемых центральным процессором. Напри-мер, 05 825 631 трактовалось как команда сложения двух чи-сел (код 05), записанных в ячейки с номерами 825 и 631. Ми-нимальные ресурсы ЭВМ требовали строжайшей экономии оперативной памяти и эффективных алгоритмов обработки. Программа по взаимосвязи составных частей напоминала «спа-гетти», примерно то, что изображено на рисунке.

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

Page 55: Pascal 8 Класс - Окулов

Основные управляющие конструкции 55

ЭВМ второго поколения . Появились я з ы к и программирования типа ассемблера и автокода. Различие, на примере нашей коман-ды, заключается в том, что команда сложения записывалась с ис-пользованием мнемоники — ADD (английское сложить) PR1, ZET, где ADD — код команды, PR1, ZET — имена ячеек. Перевод программы (трансляция), записанной таким образом, в цифровое представление, а только такое понимает ЭВМ, осуществлялся с помощью специальных программ, называемых ассемблерами. Чем характеризуется этот этап развития технологий? Его мож-но назвать операциональным программированием.

Первый и третий этапы работы программиста не обсуждают-ся. Программа «собирается» из мелких деталей, отдельных операций и имеет достаточно простую структуру, если исклю-чить принцип «спагетти» из управления вычислительным про-цессом. Уровень абстрагирования — отдельное действие, прин-ципы декомпозиции задачи отсутствуют, во всяком случае, о них не говорят. Существует разрыв между требованиями прак-т и к и и возможностями программирования. Круг решаемых с помощью ЭВМ задач достаточно ограничен — в основном рас-четные задачи.

Page 56: Pascal 8 Класс - Окулов

56 Часть первая

Занятие № 6. О п е р а т о р For

План занятия • обсуждение оператора; р разбор программ (с использованием ручной трассировки

логики) определения того, что число является палиндро-мом, в записи четырехзначного числа ровно три одинако-вые цифры;

• выполнение самостоятельной работы. Оператор For. Оператор задает многократное выполнение

некоторого оператора (может быть и составным) с одновремен-ным изменением значения управляющей переменной. Вид опе-ратора For:

For управляющая переменная>.= А Го В Do <оператор>, For управляющая переменная> =А DownTo В Do <оператор>,

где А — начальное значение управляющей переменной, В — конечное значение управляющей переменной.

Начальное (А) и конечное (В) значения управляющей пере-менной могут быть представлены константами, переменными или арифметическими выражениями. Они определяются один раз в начале выполнения оператора For и не изменяются во время выполнения этого оператора. Если окажется, что А>В при использовании слова Го, то оператор после слова Do («тело» цикла) не будет выполнен ни разу и выполнение цикла с пара-метром сразу же закончится (соответственно при DownTo, если А<В). Управляющая переменная, а также А и В должны быть одного типа, обязательно порядкового. Оператор после слова Do выполняется один раз для каждого значения управляющей пе-ременной из диапазона, определяемого значениями А и В. Если в операторе For используется слово То, то значение управляю-щей переменной увеличивается на единицу при каждом повто-рении А, А+1,.. . , В—1, В, при DownTo — уменьшается на еди-ницу.

Рекомендации по использованию. Оператор For применяют тогда, когда известно число повторений одного и того же дейст-вия (оператора). Изменение значения управляющей перемен-ной в теле цикла может привести к ошибкам, считается «дур-ным тоном» в программировании, поэтому договоримся о том, что это действие запрещено законом, т. е. учителем. Управляю-щая переменная после выполнения оператора For имеет неоп-ределенное значение. Запретим использование ее значения для анализа чего-либо после выполнения оператора For, а также

Page 57: Pascal 8 Класс - Окулов

Основные управляющие конструкции 57

«искусственные» выходы из For с помощью операторов GoTo, Exit и т. д. Оператор For должен иметь одну точку входа и одну точку выхода, он оператор!

Экспериментальный раздел работы

1- Дано натуральное число п (п<9999). Определить, является ли оно палиндромом («перевертышем»), сучетомчетырех цифр. Например, палиндромами являются числа: 2222, 6116, 0440.

Начнем не с программы, а с ручной трассировки логики ре-шения. Это важно, очень важно. Мы с Вами с помощью этого приема должны достичь того, чтобы при написании программы у Вас одновременно складывался «зрительный образ» ее рабо-ты, Вы видели ее работу, причем это должна быть не статиче-ская «картинка», а динамическая. Трассировка обычно выпол-няется для конкретных значений входных параметров задачи.

Итак, у нас четырехзначное число, поэтому переменная опе-ратора For изменяется от 1 до 4. В переменной с именем m хра-нится «остаток» числа, в первоначальный момент времени он равен введенному числу. В переменной с именем г формируем значение числа — «перевертыша». Основными операциями яв-ляются: r~10*r + т. Mod. 10 (добавление очередной цифры к числу «перевертышу») и т:=т Div 10 (изменение проверяемого числа). Процедура трассировки приведена в таблице. После ее выполнения написание программы — «техническая работа».

Program Му6_1; Var п,т, г, i:Integer; Begin

WriteLn С Введите целое число, не большее 9999'); ReadLn (п) ; m: =п ; г: = 0 ; For i:=l То 4 Do Begin {так как число четырехзначное)

r := r*10+m Mod 10; m:=m Div 10; End;

Page 58: Pascal 8 Класс - Окулов

58 Часть первая

I f r=n Then WriteLn ('ДА') Else WriteLn('НЕТ'); ReadLn;

End. Измените программу так , чтобы была возможность обраба-

тывать целые числа из диапазона Longlnt. 2. Д а н ы натуральные числа n, к (п, к<9999) . И з чисел от п до к

выбрать те, запись которых содержит ровно три одинаковых ц и ф р ы . Н а п р и м е р , числа 6766 , 5444, 0006 , 0060 содержат ровно три одинаковых ц и ф р ы .

Если данное число содержит ровно три одинаковых ц и ф р ы , то только одна из цифр отличается от остальных, то есть воз-м о ж н ы четыре случая , приведенных в таблице. Пусть в качест-ве п и к введены числа 3732 и 3740. В переменных a l , а2, аЗ, а4 храним значения цифр текущего числа i.

i а1 a2 аЗ a4 Результат сравнения 3732 3 7 3 2 ложь 3733 3 7 3 3 истина 3734 3 7 3 4 ложь 3735 3 7 3 5 ложь 3736 3 7 3 6 ложь 3737 3 7 3 7 ложь 3738 3 7 3 8 ложь 3739 3 7 3 9 ложь 3740 3 7 4 0 ложь

П р и м е ч а н и е Пусть Вас не смущает «элементарность» выполняемых действий. Вспомните одно из качеств «великого программиста». Program Муб 2;

Var п, к,i,al,а2,аЗ,а4,т:Integer; Begin

WriteLn (' Введите два числа, не больших 9999'); ReadLn (п, к); For I:=л То к Do Begin

Page 59: Pascal 8 Класс - Окулов

Основные управляющие конструкции 58

m:=i; {выделение цифр: al - первая, а2 - вторая, аЗ — третья, а4 — четвертая) а 4:=m Mod 10; т:=ю Div 10;аЗ:=т Mod 10; т:=т Div 10; а2:=т Mod 10; al:=m Div 10; I f ( (al=a2) And (al=a3) And (alOal)) Or {первое условие}

((al=a2) And (al=a4) And (al<>a3)) Or {второе условие} ((al=a3) And (al=a4) And (al<>a2)) Or {третье

условие} ((a2=a3) And (a2=a4) And (a2<>al)) {четвертое

условие} Then WriteLn (l: 5) ;

End; ReadLn;

End. Измените программу д л я обработки 4, 5 или 6 - значных чи-

сел. Если Ваше решение будет идейно копировать приведенное, то это хорошо, но не очень. Все ж е оно будет достаточно громоз-дким. Постарайтесь найти другое решение, его не обязательно оформлять в виде программы.

Задания для самостоятельной работы

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

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

2. Составить программу возведения натурального числа в квад-рат, используя следующую закономерность:

I 2 =1 2 2 = 1 + 3 З2 =1 + 3 + 5 4 2 = 1 + 3 + 5 + 7

п2 = 1 + 3 + 5 + 7 + 9 + . . . + 2 п - 1 3. Определить количество трехзначных натуральных чисел, сум-

ма ц и ф р которых равна заданному числу N.

Page 60: Pascal 8 Класс - Окулов

59 Часть первая

4. Составить программу вычисления суммы кубов чисел от 25 до 55.

5. Среди двузначных чиселнайти те, сумма квадратов цифр ко-торых делится на 13.

6. Написать программу поиска двузначных чисел, т а к и х , что если к сумме цифр этого числа прибавить квадрат этой сум-мы, то получится это число.

7. Квадрат трехзначного числа оканчивается тремя цифрами, которые как раз и составляют это число. Написать програм-му поиска таких чисел.

8. Написать программу поиска четырехзначного числа, которое при делении на 133 дает в остатке 125, а при делении на 134 дает в остатке 111.

9. Найти сумму положительных нечетных чисел, меньших 100. 10. Найти сумму целых положительных чисел из промежутка от

А до В, кратных 4 (значения переменных А и В вводятся с клавиатуры).

11. Найти сумму целых положительных чисел, больших 20, ме-ньших 100, кратных 3 и заканчивающихся на 2, 4 или 8.

12. В трехзначном числе зачеркнули первую цифру слева, когда полученное двузначное число умножили на 7, то получили данное число. Найти это число.

13. Сумма цифр трехзначного числа кратна 7, само число также делится на 7. Найти все такие числа.

14. Среди четырехзначных чисел выбрать те, у которых все че-тыре цифры различны.

15. Среди двузначных чисел найти те, сумма цифр которых рав-на п(0<п<18) и число делится без остатка на числа q.

16. Дано четырехзначное число п. Выбросить из записи числа п цифры 0 и 5, оставив прежним порядок остальных цифр. На-пример, из числа 1509 должно получиться 19.

17. Натуральное число из п цифр является числом Армстронга, если сумма его цифр, возведенных в n-ю степень, равна са-мому числу (например, 153=1 3+5 3+3 3 ) . Получить все числа Армстронга, состоящие из трех и четырех цифр.

18. Дана последовательность из 20 целых чисел. Определить ко-личество чисел в наиболее длинной подпоследовательности из подряд идущих нулей.

19. Дано натуральное число. Найти все его делители и их сум-му.

Page 61: Pascal 8 Класс - Окулов

Основные управляющие конструкции 61

Материал для чтения

Из истории программирования. О нисходящем проекти-ровании программ, структурном и модульном программи-ровании. Третье поколение ЭВМ (наиболее известная — IBM/ 360) связано с появлением интегральных схем. Существенной частью ЭВМ становятся операционные системы, на которые возлагаются задачи управления работой компьютера. Операци-онные системы — ядро системного программного обеспечения. Развиваются я з ы к и программирования высокого уровня. Если первые версии FORTRAN I, ALGOL-58 обеспечивали запись ма-тематических формул и не более, то в этом поколении я зыков реализуются новые идеи: подпрограммы и раздельная компи-л я ц и я (FORTRAN II); блочная структура и типы данных (AL-GOL-6O); описание данных и работа с файлами (COBOL); обра-ботка списков и указатели (Lisp). В следующих версиях я зыков продолжается развитие: P L / 1 (FORTRAN+ALGOL+COBOL), AL-GOL-68 (преемник ALGOL-60), Pascal (развитие ALGOL-60), Si-mula (классы, абстрактные данные). Следует особо сказать о я зыке P L / 1 . В истории программирования была, и, наверное, есть, идея создания универсального я з ы к а программирования , позволяющего программировать задачи различных классов, дру-гими словами — всевозможные задачи. Обилие языков програм-мирования, а их зарегистрировано более трех тысяч, говорит о тщетности этих попыток. Возможности языков программирова-ния обеспечивают поддержку (начальные этапы) нисходящей технологии конструирования программ. Суть нисходящего кон-струирования программ в разбивке большой задачи на меньшие подзадачи, которые могут рассматриваться отдельно. Основны-ми правилами д л я успешного применения данной технологии являются :

• формализованное и строгое описание программистом вхо-дов ф у н к ц и й и выходов всех модулей программы и систе-мы;

• согласованная разработка структур данных и алгоритмов; • ограничение на размер модулей. Н и с х о д я щ а я технология разработки программ не есть свод

жестких правил , скорее это основной принцип, допускающий вариации в соответствии с конкретными особенностями решае-мой задачи. В свое время в обширной литературе по этому по-воду говорилось и о восходящей технологии . В этом случае решение (программа) к а к бы «складывалось из отдельных к и р п и ч и к о в » , из известных решений подзадач. Таким обра-зом, данной технологией оговаривается определенный принцип

Page 62: Pascal 8 Класс - Окулов

62 Часть первая

декомпозиции и иерархическая структура программы. Важ-нейшей составляющей этой технологии является структурное программирование ( языки программирования Паскаль, Моду-ла-2). Профессор Э. Дейкстра был первым инициатором струк-турного программирования. В 1965 году он высказал предполо-жение о том, что оператор GO ТО мог бы быть исключен из языков программирования. Разумеется, структурное програм-мирование представляет собой нечто большее, чем один л и ш ь отказ от оператора GO ТО. Структурное программирование — это некоторые принципы написания программ. Теоретически-ми основаниями структурного программирования являются

• формальные системы теории вычислимости (операторные схемы программы А. А. Ляпунова, системы Поста, алго-ритмы Маркова, исчисление Черча);

• анализ программ по нисходящей схеме, декомпозиция, основанная на разбивке задач по уровням 0, 1 к . В классической работе Бома и Джакопини показано, что та-кая структура (иерархическая, разбитая на уровни) может быть реализована в языке , включающем только ограни-ченное число управляющих конструкций.

Для реализации программ требуется три основных составля-ющих блока:

• функциональный блок или конструкция следования; • конструкция обобщенного цикла; • конструкция принятия двоичного или дихотомического

решения. Графическое изображение этих блоков приведено на следую-

щих трех рисунках.

Page 63: Pascal 8 Класс - Окулов

Основные управляющие конструкции 63

Характерные черты структурного стиля программирования: • простота и ясность (программа легко читается и анализи-

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

гических конструкций; • расположение в строке программы не более одного опера-

тора языка программирования; • содержательность имен переменных. Пример неструктурированной логики приведен на рисун-

ке.

При этом процесс нисходящей разработки программы может продолжаться до тех пор, пока не будет достигнут уровень «атомарных» блоков, т. е. базовых конструкций (присвоения, if-then-else, do-while).

Итак, если формулировать суть в сжатом виде, то в струк-турном программировании уточнен принцип декомпозиции за-дачи (в основном ее алгоритмического аспекта, управляющей

Page 64: Pascal 8 Класс - Окулов

64 Часть первая

компоненты, т. е. действий, однако уровень интеграции дейст-вий и данных «на совести» разработчика) и сделана попытка его строгой формализации. К нисходящей технологии следует отнести и то, что называется модульным программированием. Достаточно независимые фрагменты задачи оформляются как модули. Создаются библиотеки модулей, определяется меха-низм включения модулей в разрабатываемую программу. Мо-дуль должен иметь строго определенный интерфейс и скрытую часть, одну точку входа и одну точку выхода. Из фольклора computer science — «модульность в программировании подобна честности в политике: к а ж д ы й утверждает, что она — одно из его достоинств, но кажется , никто не знает, что она собой пред-ставляет, как ее привить, обрести или добиться». Очередной этап развития принципов декомпозиции и абстрагирования. Схематично структура получаемой программы изображена на рисунке.

модули

Следует отметить еще одно важное обстоятельство. В инфор-матике существовало и существует как бы два программирова-ния: теоретическое и практическое. Естественно, без теоретиче-ского программирования не было бы практического, но если строго следовать первому, то любую часть программы следует строить математическими методами, доказывая правильность ее работы. Вопросы взаимного влияния этих подходов — пред-мет отдельного исследования. В данном учебнике речь идет, в основном, о втором программировании. И если теоретическое программирование является уделом математиков — програм-мистов и ему, в принципе, учат, в основном, на факультетах вычислительной математики и кибернетики классических уни-верситетов, то практическое программирование — это сочетание знаний, интеллекта (аналитических способностей ума) и здра-

Page 65: Pascal 8 Класс - Окулов

Основные управляющие конструкции 65

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

Проведем черту под этим этапом развития технологий про-граммирования. Структурная технология предоставляет в рас-поряжение разработчиков строгие формализованные методы описания программ и принимаемых технических решений. Ис-пользуется наглядная графическая техника (схемы, диаграм-мы). Однако труд этот не был автоматизирован, а вручную невозможно разработать и графически представить строгие формальные спецификации программы, проверить их полноту и непротиворечивость и тем более изменить. Программы имеют последовательную структуру (управление вычислительным про-цессом), идеи Э. Дейкстры реализованы в полной мере, что по-зволило сделать скачок в развитии технологий. Но, несмотря на возможность конструирования структур данных различной сложности, данные и действия над данными по-прежнему раз-делены. Разрыв между потребностями практики и возможно-стями разработки (по времени, надежности, возможности вне-сения изменений на стадии эксплуатации) сложных программ в пределах данной технологической схемы сохраняется. Образ-ная формулировка сути этого разрыва заключается в том, что «мы не знаем, как этого достичь, у нас нет инструментария для обеспечения процесса правильной разработки программы, но так примерно должна работать эта программа».

Page 66: Pascal 8 Класс - Окулов

Занятие № 7. Оператор While

План занятия • обсуждение оператора; • эксперименты с программами определения количества цифр

в числе, преобразования натурального числа п в 1; • выполнение самостоятельной работы. Оператор While меет вид:

| While Логическое выражение» Do <оператор (составной оператор)?, |

Оператор While содержит логическое выражение, значение которого (True или False) управляет повторным выполнением оператора (после служебного слова Do), им может быть и со-ставной оператор. Значение выражения вычисляется перед вы-полнением оператора. Если результат равен True, то оператор выполняется, при значении False — нет. Таким образом, если в начале логическое выражение имеет значение False, оператор после Do вообще не выполняется. В операторе (составном опе-раторе) обязательно изменение значений переменных, влияю-щих на значение логического выражения. При невыполнении этого условия получаем пример того, что называется «зацикли-ванием». Простейший пример — While True Do <что-то> — бесконечный цикл.

Экспериментальный раздел работы 1. Дано натуральное число п. Подсчитать количество цифр дан-

ного числа. Количество цифр в числе п неизвестно, поэтому необходимо

использовать оператор While. Использование For потребует или введения дополнительных переменных, или искусственного вы-хода из цикла, а мы договорились, что это плохо — каждый фрагмент нашей логики должен иметь одну точку входа и одну точку выхода. Подсчет количества цифр начнем с последней цифры числа. Увеличим счетчик цифр на единицу (к). Число (т) уменьшим в 10 раз, убирая тем самым из него последнюю цифру (подсчитанную). Далее с получившимся числом проделаем ту же последовательность действий и т. д., пока число не станет равным нулю. Для этого и всех остальных примеров занятия, требуется выполнять ручную «трассировку». Пусть введено число 65387, присвоим это [ значение переменной с именем т , значение

653

Page 67: Pascal 8 Класс - Окулов

Основные управляющие конструкции 67

счетчика числа ц и ф р (к) равно 0. Выполним действия, описан-ные в ы ш е , их результат приведен в таблице. Итак , окончатель-ное значение переменной к равно 5, в числе 5 цифр. После это-го работаем с программой Му7_1 по традиционной схеме: набор, к о м п и л я ц и я , сохранение, запуск и проверка работы на конк-ретных примерах .

Proqra Му"_1 : Var г), г: Longh t;

к: Integer;{счетчик числа цифр) Beg in

Wr-teLn (' Введите целое число неравное С ) ; ReadLn (п) ;гг : =ч ;к: =0; While тОС Do Eegm

In с (к) ; n:=ir Div 10; En d ; WriteLn (' В числе ' , л , ' — ' , к , ' цифо ' ' ) ; ReadLn;

End. Модифицируя программу Му7_1, решить следующие задачи: • найти сумму ц и ф р числа; • найти первую цифру числа, например для числа 7265 это

ц и ф р а 7; • поменять порядок цифр числа на обратный. Например,

было 12345, стало 54321; • найти количество четных цифр числа; • н а й т и самую большую цифру числа; • найти сумму цифр числа, больших 5; • ответить на вопрос, сколько раз данная цифра встречается

в числе? Придумать еще 5 задач (как минимум), решаемых по дан-

ной схеме. 2. В программе Му7_2 реализована «гипотеза Сиракуз». Осу-

ществляется последовательное преобразование натурально-го ч и с л а п в 1. Запустите программу, проверьте ее работу при нескольких значениях п. Результат есть. Мы делаем пред-положение, что при любом значении п работа программы за-вершится , т. е. будет получен результат . Однако это неиз-вестно, доказательства факта завершения работы программы (алгоритма) п р и любом значении п в настоящее время ни-к е м не получено. Проверка циклов типа While требует осо-бо тщательной работы, ибо ц и к л ы этого типа «потенциаль-но бесконечны во времени».

Page 68: Pascal 8 Класс - Окулов

67 Часть первая

Program Му7_2; Var п:Integer; Begin

WriteLn(Введите натуральное число:'); ReadLn (п) ; Write (п) ; While п<>1 Do Begin

I f n Mod 2=0 Then n:=n Div 2 Else n:=(3*n+l) Div 2;

Write ( ' — \n) ; End; ReadLn;

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

среднюю длину цепочек чисел при изменении п от 2 до 20. Как избавиться от этой ручной работы по запуску программы? На-блюдая за полученными цепочками чисел нетрудно заметить, что фрагменты этих цепочек часто повторяются. Например , 8=>4=>2=>1, 5=>16=>8=>4=>2=>1. Как использовать этот факт при подсчете средней длины цепочек для чисел (л) из большого ин-тервала, например типа Integer? Потребуется ли в приведенном фрагменте программы что-либо изменять в этом случае?

Задания для самостоятельной работы.

1. Дана последовательность операторов:

а :-1; Ь:=1; While a+b<8 Do Begin a:=a+l; b:=b+2 End; S:=a+b

Сколько раз выполняется проверка логического в ы р а ж е н и я в операторе While? Определите значения переменных а, Ь, и s после завершения этой последовательности операторов?

2. Определите значения переменных а и b после выполнения опе-раторов:

а: =1 ; Ь:=1; While а<=3 Do а:=а+1; b:=b+l.

3. Определите значение переменной s после выполнения следу-ющих операторов:

• s:=0; i:=0; While i<5 Do Inc(i); s:=s+100 Div i; • s:=0; i:=1; While 1>1 Do Begin s:=s+100 Div i;

dec(i) End;

Page 69: Pascal 8 Класс - Окулов

Основные управляющие конструкции 68

4. Д а н ф р а г м е н т п р о г р а м м ы с о ш и б к а м и (их не больше 5) вы-ч и с л е н и я ф а к т о р и а л а f ч и с л а п:

А : -1; f:= 0; While к<п Do f=f*k к:=к + 1 ;

Н а й д и т е эти о ш и б к и . 5. Н а й д и т е и и с п р а в ь т е о ш и б к и в с л е д у ю щ е м ф р а г м е н т е про-

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

р • =л ; While р>=0 Do Begin

а:=з+р Mod 10; р: =р Div 10

End;

П р и м е ч а н и е Задания № 1 - 5 рекомендуется выполнять, используя режим руч-ной трассировки.

6. Н а й т и м и н и м а л ь н о е число , большее 300 , которое нацело де-л и т с я на 19.

7. П р и п и с а т ь по 1 в н а ч а л о и в к о н е ц записи числа п. Напри-мер , б ы л о п = 3 4 5 6 , стало п = 1 3 4 5 6 1 .

8. П о м е н я т ь местами первую и последнюю ц и ф р ы числа. Напри-мер, из ч и с л а 8 5 4 7 д о л ж н о быть получено число 7548.

9. П р и п и с а т ь к исходному числу п такое ж е число . Например , и з ч и с л а 1 9 0 3 д о л ж н о быть получено число 19031903 .

10. Определить , я в л я е т с я л и заданное число степенью 3. 11. Составить п р о г р а м м у , п р о в е р я ю щ у ю , я в л я е т с я ли заданное

н а т у р а л ь н о е число п а л и н д р о м о м , то есть т а к и м , десятичная з а п и с ь которого ч и т а е т с я одинаково слева направо и справа налево . П р и м е ч а н и е Задача отличается от ранее рассмотренной тем, что количество цифр в числе неизвестно, а из этого следует, что тип используемо-го цикла должен быть другой.

12. В ы я с н и т ь , я в л я е т с я л и последовательность ц и ф р натураль-ного числа п р и просмотре и х справа налево возрастающей по-следовательностью. Н а п р и м е р , д л я числа 76431 ответ поло-ж и т е л ь н ы й , д л я чисел 6331 , 9782 — отрицательный.

Page 70: Pascal 8 Класс - Окулов

69 Часть первая

13. Вводится последовательность целых ненулевых чисел, при-знак окончания ввода — ввод 0. Количество чисел не мень-ше 2. Выяснить: • является ли последовательность возрастающей; • есть ли в ней хотя бы одна пара одинаковых «соседних»

чисел; • является ли последовательность знакочередующейся (3,

- 2 , 4, - 5 , 0 — «да»; 5, - 4 , - 7 , 8, 0 — «нет»), 14. Выяснить, сколько раз в натуральном числе встречается его

максимальная цифра. Например, в числе 581088 — 3 раза, в числе 4537 — 1 раз.

15. Выяснить, является ли разность максимальной и минималь-ной цифр числа четной.

Материал для чтения Объектно-ориентированное программирование. Компь-

ютеры 4-го поколения конструируются на основе БИС — бо-льших интегральных схем и СБИС — сверхбольших. Персо-нальные компьютеры определяют лицо компьютеров этого поколения. Скорости обработки огромны, так же как и объе-мы оперативной памяти. Избыточность программного кода в несколько тысяч строк не играет принципиальной роли. Тех-нологии программирования, сделав виток, возвращаются на новом уровне к «детской игре в кубики». Но если в период первого, второго поколений программа «собиралась» из отде-льных операций и пирамида Хеопса не получалась (она разва-ливалась), то на этом этапе развития пирамида собирается из объектов — кубиков, интегрирующих в единое целое данные и допустимые действия над этими данными — объектно-ориен тированное программирование (языки программирования Тур-бо Паскаль, начиная с версии 5.5, Смоллток, С++). Объект-но-ориентированные языки программирования характеризуют-ся тремя основополагающими идеями: инкапсуляцией, насле-дованием, полиморфизмом. Инкапсуляция. Сочетание данных с допустимыми действиями над этими данными приводит к «рождению» нового элемента в конструировании программы — объекта. «Рожденный ползать — летать не может» — и наш объект действует только так, как это в нем заложено, и только над тем, что в нем описано. Обращение к данным объекта не через его действия недопустимо. Наследование. Программист для решения определенного класса задач строит иерархию объектов, в которой, и это самое главное, каждый следующий

Page 71: Pascal 8 Класс - Окулов

Основные управляющие конструкции 71

производный объект имеет доступ (наследует) к данным и дей-ствиям всех своих предшественников («прародителей») . Ха-рактер связей между объектами вертикальный. Полиморфизм. Выделение некоторого действия , т. е. действие должно иметь имя и создание средств использования действия объектами и е р а р х и и . П р и ч е м к а ж д ы й объект реализует это действие так , к а к оно д л я него подходит. Пример : есть множество геометри-ческих фигур , образующих иерархию. Действие — перемеще-ние по э к р а н у . Мы видим «скачок» в технологии программи-рования , впервые действия и данные образуют нечто единое — новый уровень абстрагирования .

Д л я х а р а к т е р и с т и к и объектно-ориентированной технологии проектирования программ обратимся к классической работе Г. Бруча . Этой технологии присущи определенные принципы абстрагирования и декомпозиции. Задача описывается некой иерархической структурой из классов. Основным инструмен-том построения такой структуры является реализация концеп-ции наследования . Наследование означает такое соотношение между классами, когда один класс использует структурную или функциональную часть одного или нескольких других клас-сов (соответственно простое и множественное наследование). И н ы м и словами, наследование — это иерархия абстракций, в которой подклассы наследуют строение от одного или несколь-ких суперклассов. Логическое завершение в объектно-ориенти-рованных системах получила концепция типизации, которая строится на понятии типов абстрактных данных. «Тип — это точное определение свойств строения или поведения, которое присуще некоторой совокупности объектов». При этом возмож-но определение как статических, так и динамических связей. Если, например, Турбо-Паскалю присуща строгая типизация , при которой осуществляется контроль на соответствие типам данных и связи статичны во времени, имена связываются с ти-пами во время компиляции , и связь не изменяется во время ра-боты п р о г р а м м ы , то в объектно-ориентированных средах воз-м о ж н а д и н а м и ч е с к а я связь (поздняя связь) . Это означает ситуацию, когда тип всех переменных и выражений определя-ется только во время исполнения программы, что позволяет ре-ализовать идею полиморфизма. Это свойство является самым существенным в объектно-ориентированном программировании наряду со свойством реализации абстракций. Именно это свой-ство отличает объектно-ориентированное программирование от более традиционных методов программирования с использова-нием типов абстрактных данных.

Page 72: Pascal 8 Класс - Окулов

72 Часть первая

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

Page 73: Pascal 8 Класс - Окулов

Основные управляющие конструкции 73

З а н я т и е № 8 . О п е р а т о р Repeat-Until

План занятия • обсуждение оператора; • эксперименты с программами определения простоты чис-

ла , н а х о ж д е н и я наибольшего общего делителя двух чисел с помощью алгоритма Евклида;

• выполнение самостоятельной работы.

Оператор Repeat (повторять)- [ /пШ (до тех пор, пока) содер-ж и т логическое выражение (после Until), которое управляет повторением выполнения последовательности операторов, за-писанных между Repeat и Until. Повторение продолжается до тех пор, пока логическое выражение не примет значение True. Последовательность операторов тела ц и к л а выполняется не ме-нее одного раза .

Repeat < о п е р а т о р 1>; < о п е р а т о р 2>;

< о п е р а т о р п>; Until л о г и ч е с к о е выражение>;

При использовании оператора Repeat-Until (цикла с постус-ловием) необходимо учитывать следующее:

• перед первым выполнением оператора логическое выра-жение его окончания (или продолжения) должно быть определено;

О последовательность операторов должна содержать хотя бы один оператор, влияющий на значение логического выра-ж е н и я , иначе оператор Repeat-Until работает бесконечно долго;

D логическое выражение в конечном итоге должно принять значение True.

Пример простейшей, бесконечной по времени работы, конст-рукции: Repeat ... Until False.

Экспериментальный раздел работы

1. Целое положительное число р называется простым, если оно имеет только два делителя, а именно 1 и р. По соглашению 1 не считают простым числом. Начало последовательности про-стых чисел имеет вид:

Page 74: Pascal 8 Класс - Окулов

74 Часть первая

2 , 5 , 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,

Н а у ч и м с я у с т а н а в л и в а т ь ф а к т : я в л я е т с я л и число п про-с т ы м ? Н и ж е приведен текст р е ш е н и я . Считаем, что д е л и т е л и числа н а х о д я т с я в интервале от 2 до п Div 2, точнее в интервале от 2 до целой части Sqrt(n).

Program Му8_1; Var i,n:Longlnt; Begin

WriteLn С Введите натуральное число'); ReadLn (п) ; 1: =1 ; Repeat

Inc(i) ; Until (i> n Div 2) Or (n Mod l = 0) ; I f i> n Div 2 Then Writeln ('Число ' ,n,' простое') Else WriteLn('Число ' , i , ' первый делитель числа ' , n ) ;

ReadLn; End.

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

П о д с к а з к а Логическое выражение оператора Repeat-Until упростится, оста-нется только условие i> n Div 2, а в операторах тела цикла появит-ся — If n Mod i =0 Then WriteLn( ,i).

Основная теорема арифметики. Всякое натуральное число п разлагается на произведения простых чисел . Это р а з л о ж е н и е однозначно с точностью до порядка записи простых чисел в произведении.

А сейчас задание . Выберите интервал чисел, не очень боль-шой , например от 101 до 120, и, используя программу , найдите р а з л о ж е н и я этих чисел на произведения простых чисел . В а ш и записи д о л ж н ы иметь вид: и ^ Р / ' Р г ^ ' - Р п 4 . где (а >0), р — простые числа , например , 24=2 3 *3 1 .

Page 75: Pascal 8 Класс - Окулов

Основные управляющие конструкции 75

2. Наибольший общий делитель (НОД) двух целых чисел а и b — это наибольшее целое число, которое делит нацело оба чис-ла. Для нахождения НОД чисел а и b можно представить оба ч и с л а в в и д е : а=р 1° 1р 2

а 2 . . . .р а ' и b=p / 1 p / 2 . . . . p q Pq a затем най-ти НОД(а,Ь)=р1

тт(а1Р1)р2П1"11а2()2'...рч

1Ш11("(1Рч) ; некоторые степе-ни простых чисел могут быть и нулевые. Отложим исследо-вание этого метода нахождения НОД, у нас пока не хватает знаний. Обратимся к алгоритму греческого математика Евк-лида, он открыл его в 3 3 0 - 3 2 0 гг. до н. э. Алгоритм основан на следующем свойстве целых чисел. Пусть а и b одновремен-но не равные нулю целые неотрицательные числа и а>Ь, тог-да если Ь=0, то НОД(а,Ъ) =а, а если Ь*0, то для чисел a, b и г, где г — остаток от деления а на Ь, выполняется равенство НОД(а,Ь)=НОД(Ь,г). Действительно, г = а M o d b = a - ( a .Diu b)*b. Если какое-то число делит нацело и а, и Ъ, то из приведенно-го равенства следует, что оно делит нацело и числа г и Ь.

Например , пусть а=48, а Ь=18, найдем их наибольший об-щ и й делитель. Приведем ручную трассировку логики, она, как обычно, сведена в таблицу.

Г " a ! b Результаты J 1 48 | 18 I 48 Mod 18=12 I 18 a>b НОД(48,18)=НОД(12,18)

I 12 18 mod 12=6 a<b НОД(12,18)=НОД(12,6) I 12 mod 6=0 6 a>b НОД(12,6)=НС)Д(0,6) ||

II 6 a=0 НС)Д(0,6)=6

Таким образом, НОД(48,18)=6. Программная реализация алгоритма Евклида с использова-

нием оператора Repea t -Un t i l имеет вид Program Му8_2;

Var a, b: Longlnt; Begin

WriteLn {'Введите два числа о 0'); ReadLn (а ,Ь) ; Repeat

I f a>b Then a:=a Mod b Else b:=b Mod a; Until (a = 0) Or (b=0) ; WriteLn ('НОД=',a+b) ); ReadLn;

End.

Page 76: Pascal 8 Класс - Окулов

76 Часть первая

И з м е н и т е программу так , чтобы вместо оператора Repeat-Until использовался оператор While. Какое ограничение в этом случае м ы м о ж е м убрать?

Последовательность чисел ei = l + l = 2 , е 2 =2 + 1 = 3, е 3 = 2 * 3 + 1 = 7 , е 4 =2* 3*7 + 1 = 4 3 , е 5 = 2 * 3 * 7 * 4 3 + 1 = 1 8 0 7 , е 6 =2 *3*7* 4 3*1807 + 1 = 32 634 43 , Е 7 = 2 * 3 * 7 * 4 3 * 1 8 0 7 * 3 2 6 3 4 4 3 + 1 = 5 4 7 * 6 0 7 * 1 0 3 3 * 3 1 0 5 1 , и т . д .

называют числами Евклида . Первые 4 числа н а т а л к и в а ю т на мысль о том, что Е в к л и д о в ы числа простые. Однако у ж е е5 яв-ляется составным — 1807=13*139 . Известно, что Е в к л и д о в ы числа взаимно простые — НОД(е1 ,е ])=1 при i*]. Проверьте этот ф а к т с помощью программы Му8_2 д л я первых 6 чисел Евкли-да. Д л я работы с остальными числами Е в к л и д а типа Longlnt недостаточно, необходимо, по к р а й н е й мере, изучить основы «длинной» арифметики . Используя программу Му8_1, пока-жите , что число е6 простое.

М е т о д и ч е с к и е р е к о м е н д а ц и и В режиме ручной трассировки необходимо просчитать для заданий 1, 2 по 7-10 примеров. Найти НОД(342,612).

3. В теории чисел , разделе д и с к р е т н о й м а т е м а т и к и , д о к а з ы -вается следующая теорема. Если а и b одновременно не равны нулю, то существуют целые числа х и у, такие , что НОД(а,Ь)= =а*х+Ь*у. Теорема не утверждает , что х и у определены од-нозначно, она л и ш ь говорит о том, что НОД(а,Ь) м о ж е т быть в ы р а ж е н в т а к о м виде. Н а п р и м е р , 6 = Н О Д ( 1 2 , - 3 0 ) = 1 2 * 3 + + ( - 3 0 ) * 1 = 1 2 * ( - 2 ) + ( - 3 0 ) * ( - 1 ) . Сформируем последовательность: • а 0 =а , а 0 =а*х 0 +Ь*у 0 , где х 0 =1 , а у 0 =0; • a jHo, a 1 =a*x 1 +b*y 1 , где x t = 0 . а у х =1; D a 2 = a o _ a i * < l i > г Д е 1 i = a o D i v ai> подставляем значения а 0 и

ai> получаем a 2 =a*x 0 +b*y 0 - ( a*x I +b*y 1 )*q 1 =a*(x 0 -x 1 *q 1 )+ +b*(y 0 -y 1 *q 1 )=a*x 2 +b*y 2 ;

• a 3 - a 1 - a 2 * q 2 , где q 2 = a j Div a2 , подставляем значения a j и a2 , получаем a 3 =a*x 1 +b*y 1 - (a*x 2 +b*y 2 )*q 2 =a*(x 1 -x 2 *q 2 ) - t -+b*(y 1 -y 2 *q 2 )=a*x 3 +b*y 3 ;

• ...

Page 77: Pascal 8 Класс - Окулов

Основные управляющие конструкции 77

D a i = a i - 2 ~ a i - i * V i > г Д е <li-i=ai-2 D i v а1-1' a 1 =a*x 1 _ 2 +b + y 1 _ 2 --(a*x1_1+b*y l_1)*q1_1=a*x1+b*y1 ;

• ... • 0 = a k _ r a k * q k , где q k =a k _ 1 Div a k , ... 0 = a * x k + 1 + b * y k + r

• Ч т о м ы имеем? И т е р а ц и о н н ы й процесс, в котором: О Ч , - ! ^ 2 Div D

о x i = x 1 -2 _ x i - i* < i 1 - i ; D y ^ y . ^ y i - i H - r

Р а с с м о т р и м трассировку л о г и к и на примере чисел а = 4 8 и Ь=18.

J а, х, у, q, _0 48 1 0 1 18 0 1 2 12 1 -2 2

1 6 "j -1 3 2

И т а к , а = Н О Д ( 4 8 , 1 8 ) = 6 и 6 = 4 8 * ( - 1 ) + 1 8 * 3 . М е т о д и ч е с к и е р е к о м е н д а ц и и Проделайте аналогичную трассировку для нескольких примеров типа а=1292, Ь=798. Программное решение данной задачи имеет вид: Program Му8_3;

Var а ,Ь, а 0 ,al,х0,xl,у0,yl,д,t:Integer; (Нижних индексов у имен переменных на языке Турбо Паскаль нет.} Begin

WriteLn ('Введите два числа, первое должно быть больше второго'); ReadLn(a,b) ; а 0:=а;а 1:=Ъ;хО:=1;xl:=0;у0:=0;у1:=1; Repeat

q:=aO Div al; t:=aO;aO:=al;al:=t-al*q; t:-xO;xO:=xl;xl:=t-xl*q; t:=y0;y0:=yl;yl:=t-yl*q;

Until al=0; WriteLn (aO,' = ',a,'*(' ,x0,') + ' , b * (' , y 0 , ' ) ' ) ; ReadLn;

End.

Page 78: Pascal 8 Класс - Окулов

77 Часть первая

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

Задания для самостоятельной работы

1. Числа вида 2р—1, где р — простое число, называются числа-ми М. Мерсенна (1588-1648 гг.). Являются ли числа Мерсен-на при значениях р 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 про-стыми? Написать программу. Почему мы ограничились то-лько этими простыми числами. Ваша программа должна со-стоять из двух частей: в первой — вычисляется число Мер-сенна для значения р (вводится с клавиатуры) , во второй — проверяется является ли оно простым.

2. Линейные уравнения от двух переменных (линейные диофан-товые уравнения), т. е. уравнения вида: а*х+Ь*у=с, в кото-ром а и b не равны нулю одновременно, имеют целые реше-ния тогда и только тогда, когда d (d=HOfl(a,b)) делит нацело значение с. Причем, если х0 , у0 — частное решение, то все ре-шения имеют вид: x=x0~n*(b Div d), y=y0+n*(a Div d) для всех п. Пример: 12*х-30*у=84, НОД(12,-30)=6, 84 делится наце-ло на 6. Уравнение разрешимо. Одним из его решений яв-ляется (х ,у)=(2,-2) . Все остальные решения имеют вид: х=2+5*п, у=-2+2*п.

Даны целые числа а, Ь, с. Написать программу определения разрешимости соответствующего диофантового уравнения и, если оно разрешимо, поиска частного решения.

3. Пусть а и b — ненулевые целые числа. Целое число т > 0 назы-вается наименьшим общим кратным (НОК) чисел а и Ь, если m делится и на а, и на b нацело, а также для любого с, которое делится нацело и на а и на Ь, верно, что оно делится нацело и на т .

Если а и b — ненулевые числа, то их наименьшее общее крат-ное существует и справедливо равенство HOK(a,b)=.Abs('a*bJ Div НОД(а,Ь). Написать программу нахождения НОК двух не-нулевых чисел.

4. Числа Фибоначчи (fn) определяются формулами: f 0 = f 1 = l ; f n

= f n - i + f n - 2 ПРИ 11=2, 3,. . . , т.е. это бесконечная последовате-льность чисел вида: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...Соста-вить программу • определения номера последнего числа Фибоначчи, кото-

рое входит в диапазон типа Integer (Longlnt);

Page 79: Pascal 8 Класс - Окулов

Основные управляющие конструкции 79

• вычисления s — суммы первых чисел Фибоначчи, таких , что значение s не превышает диапазона типа Integer (Longlnt).

5. С о в е р ш е н н ы м числом н а з ы в а е т с я число, равное сумме всех своих д е л и т е л е й , м е н ь ш и х , чем оно само. Н а п р и м е р : 6 = 1 + 2 + 3 , 2 8 = 1 + 2 + 4 + 7 + 1 4 . Древним грекам были известны только четыре первых числа. Составить программу, проверяю-щую, является ли заданное натуральное число совершенным. П р и м е ч а н и я

1. Если Вы составите программу поиска 4 совершенных чисел, то материал следующего занятия Вы уже поняли. Подсказка: пер-вые два числа Вам известны, а четвертое число не превышает зна-чения 9999. 2. Поиск пятого числа и т. д. — отдельная проблема. Евклидом до-казано, что каждое число вида 2р1*(2р-1) является совершенным числом, если 2р-1 — простое число. Л. Эйлер доказал, что все чет-ные совершенные числа находятся по формуле Евклида, а относи-тельно нечетных совершенных чисел ничего неизвестно до сих пор.

6. Автоморфным называется такое число, которое равно послед-ним цифрам своего квадрата. Например: 52=25, 252=625. Оче-видно, что автоморфные числа должны оканчиваться либо на 1, либо на 5, либо на 6. Составить программу нахождения ав-томорфных чисел (с учетом приведенного факта) , не превы-ш а ю щ и х значения 999. П р и м е ч а н и е Не забывайте о диапазонах переменных целого типа, 9992=998001.

7. Кубические автоморфные числа равны последним цифрам сво-их кубов. Например: 6 3 =216. Верно ли, что и такие числа дол-ж н ы оканчиваться либо на 1, либо на 5, либо на 6? С учетом этого факта составить программу нахождения двузначных, трехзначных кубических автоморфных чисел.

Материал для чтения

1. Одно из основных свойств целых чисел — это свойство де-лимости, или евклидовости. Мы его неявно использовали на предыдущих занятиях . В математике оно формулируется как теорема и, естественно, доказывается.

Теорема. Д л я любого а и любого ненулевого b существуют един-ственные (целые) частное q и остаток г, такие, что a=b*q+r , 0<r<abs(b).

Page 80: Pascal 8 Класс - Окулов

79 Часть первая

Идея доказательства. Рассмотрим множество целых чисел вида a-k*b, где к пробегает все множество целых чисел, поло-жительных и неположительных: ..., a-3*b, а-2*Ь, а -b , а, а+Ь, a+2*b, а+3*Ь, ... В этой последовательности выбирается наимень-шее неотрицательное число и обозначается через г, а через q обо-значается соответствующее значение k(r=a-q*b). Существование такого г следует из конечности множества чисел последователь-ности, удовлетворяющих условию теоремы, и их упорядоченно-сти. Далее, методом от противного, доказывается единственность значения г.

2. Простых чисел бесконечно много. Это доказано еще Евк-лидом. Его доказательство. Предположим, что простых чисел конечное множество. Пусть их к: 2, 3, 5, ....Рк. Рассмотрим чис-ло М=2*3*5*...*Рк+1. Ни одно из простых чисел не может де-лить М, ибо каждое из них делит М-1 . Тогда должно быть дру-гое простое число, делящее М, или само М — простое. А это противоречит предположению о конечности множества про-стых чисел, тому, что только 2, 3, 5, ..., Рк простые числа.

3. Продолжим наше знакомство с основными понятиями технологий программирования. Пусть Вам после первого чте-ния не все понятно, это нормальное явление. Только очень про-стые книги читаются «с листа». Познакомимся с понятием мо-дели жизненного цикла программ. В настоящее время понятие жизненного цикла программы является одним из базовых по-нятий в технологии программирования. Жизненный цикл — это непрерывный процесс, который начинается с момента при-нятия решения о необходимости создания программы и закан-чивается в момент ее полного изъятия из эксплуатации. Основ-ным нормативным документом, регламентирующим жизненный цикл программы, является международный стандарт ISO/IEC 12207 (ISO — International Organization of Standardization — Международная организация по стандартизации, IEC — Inter-national Electrotechnical Commission — Международная комис-сия по электротехнике). Он определяет структуру жизненного цикла, которая базируется на трех группах процессов:

• основные процессы (приобретение, поставка, разработка, эксплуатация, сопровождение);

• вспомогательные процессы, обеспечивающие выполнение основных процессов (документирование, управление кон-фигурацией, обеспечение качества, верификация, аттеста-ция, оценка, аудит);

• организационные процессы (управление проектами, созда-ние инфраструктуры проекта, обучение).

Page 81: Pascal 8 Класс - Окулов

Основные управляющие конструкции 81

К настоящему времени известны две основные модели жиз-ненного цикла:

• каскадная (до 1985 г.) — первые два рисунка;

Проектирование

Сопровождение

| Проектирование I—|

Реализация

Сопровождение

• спиральная модель (с 1986 г. по 90-е гг.) — третий рису-нок.

Реализация

Page 82: Pascal 8 Класс - Окулов

К а с к а д н ы й подход хорошо зарекомендовал себя п р и постро-ении программ, д л я к о т о р ы х в самом начале ра зработки м о ж н о достаточно точно и полно сформулировать все требования . При-т е р _ расчетные задачи (первое, второе п о к о л е н и я ) . В случае неточного и з л о ж е н и я требований и л и и х и з м е н е н и я п о л у ч а е т с я программа , не о т в е ч а ю щ а я потребностям пользователя , з аказ -ч и к а , поэтому р е а л ь н ы й процесс ра зработки имеет вид, приве-д е н н ы й на втором р и с у н к е .

Д л я преодоления недостатков к а с к а д н о й схемы была пред-л о ж е н а , исторически с л о ж и л а с ь (наверное, н а ч и н а я с визуаль-н ы х сред) с п и р а л ь н а я модель ж и з н е н н о г о ц и к л а п р о г р а м м ы (третий рисунок) . Ц е л е в а я установка — упор на а н а л и з и про-ектирование , путем создание прототипов (моделей!!!). К а ж д ы й виток с п и р а л и соответствует созданию версии п р о г р а м м ы . Н а ней уточняются ц е л и и требования , определяется качество , и п л а н и р у ю т с я работы по следующему в и т к у . Т а к и м образом, уг-лубляются и последовательно к о н к р е т и з и р у ю т с я детали про-г р а м м ы и выбирается окончательная версия, которая доводится до реализации. Сходящий вид спирали подчеркивает «размы-тость» первоначальных требований и их последующее уточне-ние.

4. О ш и б к и в программах разделяют по т и п а м на синтаксиче-ские, семантические и логические . С п е р в ы м и м ы встречались на предыдущих з а н я т и я х . Они достаточно просто у с т р а н я ю т с я и говорят о несоответствии текста п р о г р а м м ы п р а в и л а м я з ы к а Турбо П а с к а л ь . Вторые возникают на стадии в ы п о л н е н и я про-г р а м м ы . Например , деление на ноль не устраняется на стадии к о м п и л я ц и и , а приводит к ошибке на стадии в ы п о л н е н и я , ибо зависит от конкретного значения переменной. Третий тип оши-бок самый с л о ж н ы й в обнаружении . П р о г р а м м а работает так , к а к написана , но не так , к а к требуется. Т а к и е случаи я в л я ю т с я следствием многих причин . Главное, что их сложно обнару-ж и т ь . В системе Турбо П а с к а л ь имеется достаточно м о щ н ы й отладчик, п о з в о л я ю щ и й находить о ш и б к и третьего т и п а в про-граммах . Он работает с исходным текстом программы, позволя-ет в ы п о л н я т ь программу по ш а г а м и отслеживать изменение переменных программы в процессе работы последней.

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

Page 83: Pascal 8 Класс - Окулов

Основные управляющие конструкции 82

Команда меню Функциональная 1 клавиша

Назначение |

Run/Run Ctrl+F9 Запуск программы ] Run/Go tu cursor F4 Выполняет программу до j

строки, в которой находится курсор !

Run/Trace into F7 Выполняет оператор f (операторы), записанный в |> текущей строке Если в этой i строке записан вызов 1

процедуры или функции, то осуществляется вход в эту : процедуру или функции, отслеживается их работа |,

Run/Step over F8 Выполняется оператор, записанный в текущей строке без вхождения в процедуры или функции

Debug/watch Открывает окно для наблюдения за значениями переменных (окно Watches)

Debug/Breakpoints 1 Открывает окно для работы с 1 точками останова ,

1 Debug/Evaluate Ctrl+F4 Открывается окно Evaluate and J Modify Набирается выражение j; (можно с использованием !; переменных прогоаммы) и вычисляется его значение

Debug/Add watch Ctrl+F7 Открывает окно Add watch для 1

набора отслеживаемых значений выражений или | переменных Эти действия J можно выполнить и другим j способом Сделать окно Watch активным и использовать 1 клавиши Insert и Delete для вставки и удаления выражений и переменных

Debug/Add breakpoint Clrl+F8 Текущая строка (в ней 1 находится курсор) 1

Page 84: Pascal 8 Класс - Окулов

З а н я т и е № 9 . В л о ж е н н ы е ц и к л ы

План занятия • обсуждение к о н с т р у к ц и и ; • э к с п е р и м е н т ы с п р о г р а м м а м и (с и с п о л ь з о в а н и е м отладчи-

к а системы п р о г р а м м и р о в а н и я ) в ы ч и с л е н и я в ы р а ж е н и я l k + 2 k + . . . + n k , н а х о ж д е н и я цифрового к о р н я ч и с л а ; ста-ринной задачи о б ы к а х , коровах и т е л я т а х , н а х о ж д е н и я натуральных чисел, удовлетворяющих определенному усло-вию, и сведения чисел , к р а т н ы х 3, к числу 153;

• выполнение самостоятельной работы. Д л я р е ш е н и я задачи достаточно часто требуется использо-

вать две и более ц и к л и ч е с к и е к о н с т р у к ц и й , одна и з к о т о р ы х помещается в тело ц и к л а другой. Т а к и е к о н с т р у к ц и и н а з ы в а ю т в л о ж е н н ы м и ц и к л а м и . Внутренний и в н е ш н и й ц и к л ы могут быть любыми из трех рассмотренных ранее видов . П р а в и л а ор-г а н и з а ц и и внешнего и внутреннего ц и к л о в т а к и е ж е , к а к и со-ответствующих простых операторов. Однако при использовании вложенных циклов необходимо соблюдать следующее условие: внутренний ц и к л должен полностью у к л а д ы в а т ь с я в ц и к л и ч е -скую часть внешнего ц и к л а .

Экспериментальный раздел работы

1. Д а н ы натуральные числа п и к . Составить п р о г р а м м у вычис-ления в ы р а ж е н и я 1 к + 2 к + . . . + п к .

Д л я вычисления указанной суммы целесообразно использо-вать оператор For с у п р а в л я ю щ е й переменной 1, и з м е н я ю щ е й с я от 1 до п. В теле ц и к л а , во-первых, вычислялось бы очередное значение y=i k и , во-вторых, осуществлялось бы накопление суммы прибавлением полученного слагаемого к сумме всех предшествующих (s:=s+i/).

Program Му9_1 ; Var п, к, у, i , s, j: Integer; Begin

WriteLn('Введите исходные данные п и к ' ) ; ReadLn (п, к) ; s: =0 ; For i:=1 То п Do Begin У--1; For j : =1 To к Do y:=y"i; s:=s+y;

End; WriteLn('Ответ: ' , s ) ;

End.

Page 85: Pascal 8 Класс - Окулов

Основные управляющие конструкции 85

А сейчас попробуем работать с отладчиком нашей системы программирования. Вспомните материал для чтения предыду-щего занятия, пункт 4.

Итак: • Выполните команду Debug/Watch. Появится окно Watc-

hes. • Используя клавишу Ins (инициируется открытие окна Add

Watch), введите переменные программы (n, k, s, i, j, у). • Измените положение и размер окна Watches на экране.

Оно должно находится в правом верхнем углу и быть та-ким, чтобы все введенные переменные были обозримы. С этой целью выполните команду Window/Size/Move (Ctrl+ +F5) и используйте клавиши Shift+(<=, 1)) для изменения размера окна и клавиши (=>, U) для его перемещения.

• Выполните команду Run/Step over (F8). Строка с операто-ром Begin выделяется другим цветом (курсор). При нажа-тии на F8 курсор перемещается на следующую строку — пошаговое выполнение программы.

П Выполните программу в пошаговом режиме (последовате-льное нажатие на F8), отслеживая изменение значений переменных в окне Watches. При выполнении ReadLn (п,к) введите, например, значения 4 и 2 для того, чтобы количество повторений в операторах For было не очень бо-льшим.

Продолжим изучение отладчика. • Установите курсор на строку программы с оператором

s~s+y. Выполните команду Debug/Add Breakpoint (Ctrl+ +F8). Строка будет выделена другим цветом. Мы создали точку останова в программе.

• Запустите программу (Ctrl+F9). Работа программы при-останавливается в том случае, когда достигнута точка останова.

П Выполните один шаг в работе программы — нажмите кла-вишу F8.

• Выполните команду Debug/Evaluate... В строке Expression окна Evaluate and Modify наберите имя переменной s. В строке Result окна мы видим значение переменной s, оно равно 1.

• Продолжите работу с программой по описанной схеме. Следующее значение переменной s равно 5.

Page 86: Pascal 8 Класс - Окулов

86 Часть первая

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

2. Изменить программу так, чтобы вычислялась сумма 1 1 +2 2 + + ...+П". Подсказка. Требуется изменить параметры у внут-реннего оператора For (For j~l То i Do y~y*i;) и убрать пе-ременную k.

3. Пусть значение к зафиксировано, например равно 5. Опреде-лить значение п, при котором диапазон целого типа данных Integer окажется недостаточным для хранения суммы степе-ней чисел.

4. Сложим все цифры какого-либо числа. Получим новое чис-ло, равное сумме всех цифр исходного числа. Продолжим этот процесс до тех пор, пока не получим однозначное число (циф-ру), называемое цифровым корнем данного числа. Например, цифровой корень числа 34697 равен 2 (3+4 + 6+9+7=29 ; 2+9=11; 1+1=2). Составить программу нахождения цифро-вого корня натурального числа.

Program Му9_2; Var г,к,s:Longlnt ; Begin

WriteLn('Введите число');ReadLn (п); s: =п ; While s>9 Do Begin к:=s; s:=0; Repeat

s:=s+k Mod 10; k:=k Div 10;

Until k=0; End; WriteLn('Цифровой корень числа ' ,n,' равен ' ,s);

End. 5. Старинная задача. Сколько можно купить быков, коров и

телят, если плата за быка 10 рублей, за корову — 5 рублей, за теленка — полтинник (0.5 рубля), если на 100 рублей надо купить 100 голов скота.

Решение. Обозначим через Ъ — количество быков; к — коли-чество коров; t — количество телят. После этого можно за-писать два уравнения: 10b+5k+0.5t=100 и b+k+t=100. Пре-образуем их: 20b+10k+t=200 n b + k + t = 1 0 0 .

Page 87: Pascal 8 Класс - Окулов

Основные управляющие конструкции 87

На 100 рублей можно купить : • не более 10 быков, т. е. 0<Ъ<10, • не более 20 коров, т. е. 0<к<20, • не более 200 телят , т. е. 0<t<200.

Т а к и м образом, получаем:

Program Му9_3; Var Ь, к, t ; I n t e g e r ; Begin

For b:=0 To 10 Do For к:=0 To 20 Do Foi t:=0 To 200 Do

I f (20*o+10*k+t=20C) Ana (v+K+t=100) Then WriteLn ('быков ',o,' коров ' ,k,' телят ' , t) ;

End. Сколько раз будет проверяться условие в данной программе

(оператор I f ) ? Значение переменной b изменяется 11 раз (от 0 до 10), для каждого ее значения переменная к изменяется 21 раз, а д л я каждого значения переменной к переменная t изме-няется 201 раз. Таким образом, условие будет проверяться 11*21*201=46431 раз. Но если известно количество быков и коров, то количество телят можно вычислить по формуле t=100—(b+k) и ц и к л по переменной t исключается.

Program My9_3m; Var Ь, k, t: Integers-Begin

For b:=0 To 10 Do For к:=0 To 20 Do Begin

t: =100- (b+k) ;

I f (20*b+10*k + t=200) Then WriteLn ( 'быков ' ,b, ' коров ' , k , ' телят ' , t ) ;

E n d ; End.

При этом решении условие проверяется 11*21=231 раз. Тре-буется еще уменьшить количество проверок.

4. Написать программу, которая находит все четырехзначные числа abed (а, Ь, с, d — цифры числа, причем между ними нет совпадений, т. е. числа, например, типа 1221 нас не устраи-вают, т , е. любые две цифры числа различны), для которых выполняется условие: ab-cd=a+b+c+d. Другими словами, раз-ность чисел, составленных из старших цифр числа и из млад-ших, равна сумме цифр числа.

Page 88: Pascal 8 Класс - Окулов

88 Часть первая

З а д а ч у м о ж н о р е ш а т ь р а з н ы м и способами. Один из них — перебор всех ч е т ы р е х з н а ч н ы х чисел и проверка в ы п о л н е н и я условия . Попробуем сократить перебор, д л я этого преобразуем условие . И з р а в е н с т в а 1 0 * a + b - ( 1 0 * c + d ) = a + b + c + d п о л у ч а е м 9*(a -c )=2*(c+d) и л и ( a - c ) / ( c + d ) = 2 / 9 , а = с + 2 , d = 9 - c и 0<с<7.

Program Му9_4; Var a,b,с,d:Integer; Begin

For c:=0 To 7 Do Begin a:=c+2; d:=9-c; For b:=0 To 9 Do

I f (b<>c) And (bOa) And (bod) Then Игi tehn (a ,b, c, d) ;

End; End.

5. Дано натуральное число, кратное 3. Найдем сумму кубов ц и ф р данного числа . П о л у ч и м новое число. П р и м е н и м к нему та-кое ж е преобразование . А что в итоге? Оказывается , что лю-бая т а к а я последовательность чисел, начиная с некоторого ме-ста, становится постоянной и ее элементы р а в н ы 153.

Из теории чисел. Если число aja 2 . . .a k делится на 3, то и s=a 1 +a 2 +. . .+a k делится на 3. Это необходимый и достаточный признак делимости числа на 3. Из него следует, что и сумма ку-бов цифр числа кратна 3. Действительно: s 3 = a j 3 + a 2

3 + . . . + a k3 +

+3*Р , где Р — сумма п о п а р н ы х произведений ц и ф р и и х квад-ратов. Отсюда следует, что a j 3 + a 2

3 + . . . + a k3 = s 3 - 3 * P , т. е. сумма

кубов ц и ф р рассматриваемого числа кратна 3. Осталось пока-зать, что последовательность я в л я е т с я с х о д я щ е й с я . Это дока-зывается методом математической и н д у к ц и и . Вам ж е предлага-ется составить таблицу: в первой строке параметр к, з а д а ю щ и й количество девяток в числе , во второй строке — сумма кубов цифр такого числа . Значение к и з м е н я е т с я от 1 до 10.

к 1 2 3 4 5 6 7 8 9 "1 Сумма кубов цифр

729 1458 2187 ? •

Проанализируйте таблицу. Вы увидите, что при к>5 количе-ство ц и ф р в числе, составленном из с у м м ы кубов ц и ф р , умень-шается . Т а к и м образом, если Вы проверите сходимость после-довательности д л я всех натуральных чисел, например, меньших

Page 89: Pascal 8 Класс - Окулов

Основные управляющие конструкции 89

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

Program Му9_5; Var n,m,t,s,q:Longint; Begin

WriteLn ('Введите число, оно умножается на 3, и для полученного числа строится последовательность');ReadLn(п); л : =3 *п ; Wri te (m, ' ' ) , Repeat

hhile m>C Do Begin q: =ni Mod 10; s:=s+q*q*q; m:=m Div 10;

End; m: =s ; Wri te (m, ' ' ) ;

Until m=t; ReadLn;

End. Проверьте, что эта логика работает для всех чисел, напри-

мер, меньших 33333. Д л я этого потребуется, естественно, до-полнить программу.

Задания для самостоятельной работы

1. Что будет выведено на экране монитора после выполнения следующего фрагмента программы: а:=1; Ь:=1; For i:=0 То n Do Begin

For j : =1 To b Do Write ('*') ; WriteLn; c:=a+b; a:=b; b:=c;

End; если n=6? Решение какой задачи выражает этот фрагмент про-граммы?

2. Что будет выведено на экране монитора после выполнения следующего фрагмента программы:

Page 90: Pascal 8 Класс - Окулов

89 Часть первая

b: = 0 ; While а<>0 Do Begin b:=Ь*10+а Mod 10; а:=а Div 10;

End; Write (b) ; если a=13305? Решение какой задачи выражает этот фрагмент п р о г р а м м ы ?

3. Исходное данное — натуральное число q, в ы р а ж а ю щ е е пло-щадь . Н а п и с а т ь программу для н а х о ж д е н и я всех т а к и х пря-моугольников , п л о щ а д ь к о т о р ы х равна q и стороны выраже-н ы н а т у р а л ь н ы м и числами .

4. Составить программу д л я графического и з о б р а ж е н и я дели-мости чисел от 1 до п (п — исходное данное). В к а ж д о й стро-ке надо печатать число и столько плюсов, сколько делителей у этого числа . Например , если исходное данное — число 4, то на экране д о л ж н о быть напечатано:

1 +

2+ + 3+ + 4 + + +

5. Дано натуральное число п. Можно его представить в виде сум-м ы трех квадратов н а т у р а л ь н ы х чисел? Если можно , то: • указать тройку х , у, z т а к и х н а т у р а л ь н ы х чисел, что

x 2 + y 2 + z 2 = n ; • указать все тройки х, у, z т а к и х н а т у р а л ь н ы х чисел, что

x 2 + y 2 + z 2 = n . 6. Н а й т и натуральное число от 1 до 10000 с м а к с и м а л ь н о й сум-

мой делителей. 7. Даны натуральные числа а, Ъ (а<Ъ). Получить все простые чис-

ла р, удовлетворяющие неравенствам: а<р<Ь. 8. Д а н ы натуральные числа n, т . Получить все меньшие п на-

туральные числа, квадрат суммы цифр которых равен т . 9. Д а н ы натуральные числа п и т . Н а й т и все п а р ы дружествен-

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

10. В данном натуральном числе переставить ц и ф р ы т а к и м об-разом, чтобы образовалось наименьшее число, записанное эти-ми же цифрами.

Page 91: Pascal 8 Класс - Окулов

Основные управляющие конструкции 90

11. Составить программу, печатающую для данного натурально-го числа k-ю цифру последовательности: • 12345678910. . . , в которой выписаны подряд все натура-

льные числа; • 14916253649. . . , в которой выписаны подряд квадраты

всех натуральных чисел; • 1123581321. . . , в которой выписаны подряд все числа

Фибоначчи. 12. Составить программу возведения заданного числа в третью

степень, используя следующую закономерность:

1 3=1 23=3 + 5 3 J=7 + 9 + l l 4 J =13+15+17+19 5 3 =21+23+25+27+29

13. Составить программу для нахождения всех натуральных ре-шений уравнения n 2 + m 2 = k 2 в интервале [1, 10]. П р и м е ч а н и е Решения, которые получаются перестановкой п и ш, считать сов-падающими.

14. Выписать фрагмент программы для решения указанной ниже задачи и обосновать, почему был выбран тот или иной вари-ант оператора цикла : • вычислить факториал некоторого числа р; • задано число р, определить, является ли оно факториа-

лом некоторого числа, если «да», то найдите это число; • определить, является ли заданное число степенью числа 3; • вычислить y=l!+2!+3!+. . .+nl • вычислить хп , где п — целое положительное число; • вычислить значения х1 , х2 , х 3 , . . . , хп .

15. Дано трехзначное число. Найти все трехзначные числа, удов-летворяющие каждому из условий: • состоящих из тех же цифр, что и исходное число; • равное среднему арифметическому всех трехзначных чи-

сел (включая данное), имеющих тот же цифровой состав. 16. Стороны прямоугольника заданы натуральными числами М

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

Page 92: Pascal 8 Класс - Окулов

91 Часть первая

17. Даны натуральные числа N и р. Получить все натуральные числа, меньшие N и взаимно простые с р.

18. Даны целые числа р и q. Получить все делители числа q, взаимно простые с р.

19. Сумма квадратов длин катетов а и b прямоугольного треуго-льника равна квадрату длины гипотенузы с: а2+Ь2=с2 . Трой-ка натуральных чисел, удовлетворяющих этому равенству, называется Пифагоровыми числами Составить программу нахож-дения основных троек Пифагоровых чисел, используя следующие формулы a=u*v; b=(u*u-v*v) Div 2, c=(u*u+v*v) Div 2, где u и v — взаимно простые нечетные натуральные числа и u>v.

20. Найти наименьшее натуральное число N, представимое дву-мя различными способами в виде суммы кубов двух натура-льных чисел х3 и у3 (х>у).

21. Даны натуральные числа т , пх, п2 , . . . , пга (т>2). Вычислить НОД(п1 ,п2 , . . . ,п т) , воспользовавшись соотношением НОД(п1( п2 , . . . ,пт)=НОД(НОД(п1 ,п2,.. .,пт_1),пш) и алгоритмом Евкли-да.

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

Материал для чтения

Продолжим обзор технологий программирования. Визуаль-ные технологии. Визуальную технологию конструирования про-грамм (например, систему программирования Delphi) можно отнести к технологиям пятого поколения. Она, во-первых, пол-ностью поддерживает объектно-ориентированную технологию, во-вторых, идеи модульного программирования получают ло-гическое завершение, в-третьих, и это принципиально новое в данной технологии — создан инструментарий (автоматизация) программирования реакции на события (любая программа в процессе своей работы с чем-то или кем-то взаимодействует). Структура программного кода вероятностная. Не все маршру-ты, трассы кодов жестко определены. Элементы программного кода взаимодействуют, начинают работать при возникновении определенных событий. А главное — процесс разработки носит не каскадный, последовательный характер, он развивается по спирали.

Модель многокомпонентных объектов. Модель многоком-понентных объектов (Component Object Model — СОМ) лежит в

Page 93: Pascal 8 Класс - Окулов

Основные управляющие конструкции 93

основе OLE (Object Linking and Embedding — связывание и встраивание объектов) и ActiveX (одна из технологий Micro-soft). Традиционно программы разных типов предоставляли свои сервисы по-разному (вызовы локальных функций, переда-ча сообщения средствами связи между процессами, системные вызовы и др.). СОМ определяет общий способ доступа к про-граммным сервисам. Объекты СОМ предоставляют сервисы че-рез методы, объединенные в интерфейсы. Вспомним идеи объ-ектно-ориентированного программирования. Объединяются в единое целое данные и действия над данными. В визуальном программировании дается инструментарий для разработки взаи-модействия программы и пользователя, программ между собой. Логика развития программирования (как науки, так и отрасли производства) подводит к объединению определенных схем взаи-модействия объекта с другими объектами в нечто целое, назы-ваемое интерфейсом. Заметим, что в результате появляется новый инструментарий абстрагирования. Продолжим. Объ-ект, использующий ресурсы других объектов, обычно называ-ют клиентом. Описание поведения объекта включает описание операций, которые могут выполняться над ним, и операций, которые сам объект выполняет над другими объектами. При этом концентрируется внимание на внешних особенностях объ-екта. Полный набор операций, которые объект может осуществ-лять над другим объектом, называется протоколом. Протокол отражает все действия, которым объект может подвергаться сам и которыми может оказывать влияние на другие объекты, определяя тем самым полностью внешнее поведение абстрак-ции со статической и динамической точек зрения. Метод — это функция или процедура, которая выполняет некоторое дейст-вие и может быть вызвана программным обеспечением, исполь-зующим данный объект (клиентом объекта). Методы, составля-ющие каждый из интерфейсов, обычно определенным образом взаимосвязаны. Клиенты могут получить доступ к сервисам объекта СОМ только через вызовы методов интерфейсов объек-та — у них нет непосредственного доступа к данным объекта. Методы интерфейса служат для предоставления определенного сервиса. Объект СОМ реализуется внутри сервера и обычно под-держивает несколько интерфейсов. Сервер может быть либо ди-намически подключаемой библиотекой (DDL), подгружаемой во время работы приложения, либо отдельным самостоятель-ным объектом.

Соотношение СОМ и объектно-ориентированного програм-мирования. Объекты — центральная идея СОМ. Объект состоит

Page 94: Pascal 8 Класс - Окулов

94 Часть первая

из двух элементов: набора данных и групп методов. В отличие от СОМ большинство объектно-ориентированных технологий допускает не более одного интерфейса на объект.

Распространенная концепция в объектно-ориентированном программировании — понятие класса. Объекты относятся к определенному классу, являются экземплярами класса (идея типизации в Турбо Паскале). СОМ объекты имеют классы. Класс в СОМ понимается как конкретная реализация набора интерфейсов. Может существовать несколько разных реализа-ций одного и того же набора интерфейсов, каждая из которых будет отдельным классом. Идеи объектно-ориентированного про-граммирования нашли свое отражение в СОМ технологии. Ин-капсуляция. Данные объекта недоступны его клиентам непо-средственно, они инкапсулируются, скрываются от прямого доступа извне. Клиент имеет доступ к данным объекта только через методы интерфейсов этого объекта. Полиморфизм. Воз-можность работы с объектами разных типов, каждый из кото-рых поддерживает данный набор интерфейсов, но реализует их по-разному. Наследование. Идея проста: имея некоторый объ-ект, можно создавать новый, автоматически поддерживающий все или некоторые «способности» старого. Различают наследо-вание реализации и наследование интерфейса. В первом случае объект наследует от своего родителя код. Когда клиент дочер-него объекта вызывает один из унаследованных методов, на са-мом деле выполняется код метода родителя. Таким образом, это механизм повторного использования кода (языки С++, Smal-ltalk). Наследование интерфейса означает повторное использо-вание спецификаций — определение методов, поддерживаемых объектом, облегчает решение задач полиморфизма. Определе-ние нового интерфейса путем наследования от существующего гарантирует, что объект, поддерживающий новый интерфейс, можно рассматривать как объект, который поддерживает ста-рый интерфейс. В чем отличие СОМ от объектно-ориентирован-ных технологий? СОМ действительно объектно-ориентирован-ная технология, однако способ определения и поведения объектов трактуется иначе.

Идея, заложенная в СОМ технологии. Повторное использо-вание программных компонент. Так, разработчику аппаратуры значительно легче за счет интенсивного повторного использова-ния существующих компонентсоздавать продукт (изделие). Идею трудно назвать новой. Библиотеки (динамически подключае-мые), объекты дают эту возможность. Библиотеки поставляют-ся в двоичном виде, что позволяет скрыть секреты реализа-

Page 95: Pascal 8 Класс - Окулов

Основные управляющие конструкции 94

ции. Основная сложность — расширение функциональных возможностей, библиотечный подход просто недостаточен. По-вторное использование объектов возможно, однако рынка объек-тов нет, то есть возможно использование только в рамках рабочей группы программистов. Причины: стандартов для компоновки двоичных объектов в единое целое нет. Необходимо распростра-нять объекты вместе с исходным текстом, что вряд ли возмож-но. Повторное использование объектов в различных системах программирования тоже не возможно. Рынок обязан постав-лять объекты, которые могут использоваться разными языка-ми и средами разработки. Но этого нет, объект на С++ затруд-нительно использовать в среде Delphi. Еще одна проблема связана с перекомпоновкой и перекомпиляцией всего приложе-ния при изменении одного объекта. СОМ технология решает эти проблемы, при этом преимущества библиотек и объектов сохраняются и вносится в программирование преимущество технологии всеобщего повторного применения. В WWW уже существуют узлы, полные компонентов, основанных на СОМ.

Преимущества СОМ: • Все преимущества объектно-ориентированного проектиро-

вания программ. Разработчик может организовать проект в виде СОМ объектов, а затем определить интерфейсы каждого объекта.

• Общий подход к созданию всех типов программных серви-сов. Находит нужное программное обеспечение в библиоте-ке, в другом процессе, в операционной системе. Сглаживает различия между системным и прикладным программным обеспечением.

• Безразличен язык программирования. СОМ определяет двоичный интерфейс, который должны поддерживать объ-екты. Объекты СОМ можно создавать на любом языке, способном поддерживать данный интерфейс.

• Замена текущей версии программ на новую, с дополните-льными возможностями, не повредив существующим кли-ентам старой версии. Способность СОМ-объекта поддер-живать более одного интерфейса — ключ к решению этой проблемы.

Технологии Active X и OLE. Первая реализация OLE была предназначена для обеспечения механизма создания и работы с составными документами. Элементы, созданные в различных приложениях, например Microsoft Excel и Microsoft Word, ин-тегрируются в рамках единого документа. Составные докумен-ты создаются либо связыванием двух разных документов, либо

Page 96: Pascal 8 Класс - Окулов

95 Часть первая

внедрением одного документа в другой. Частный случай. Даль-нейшие версии — разные программные компоненты должны предоставлять друг другу сервисы. По-новому взглянуть на взаимодействие любых типов программ (библиотек, приложе-ний, системного программного обеспечения и др.). Под терми-ном OLE понималось все, что создавалось с использованием па-радигмы СОМ. В 1996 году Microsoft ввела в оборот новый термин ActiveX. Сначала он относился к технологиям, связан-ным с Интернетом, и приложениям, выросшим из него, вроде W W W (World Wide Web). Но так как разработки Microsoft были основаны на СОМ, то ActiveX также была связана с OLE. Все вернулось на круги свои: OLE — означает в настоящее вре-мя только технологию создания составных документов связы-ванием и внедрением, а разнообразные технологии на основе СОМ, ранее объединенные под названием OLE, называют Acti-veX. Технологии ActiveX и OLE — это не что иное, как про-граммное обеспечение, предоставляющее клиентам сервисы через СОМ-интерфейсы, поддерживаемые СОМ объектами. Различные части ActiveX и OLE определяют стандартные интерфейсы для различных целей. Рынок стандартных компонентов. Такую цель и преследует финансируемая Microsoft программа разработки интерфейсов подобного рода — OLE Industry Solutions (Про-мышленные решения на основе OLE). В рамках этой программы группы финансовых компаний, организаций здравоохранения, поставщиков оборудования для торговых точек и др. определили стандартные интерфейсы компонентов, применяемых в соот-ветствующих областях.

Case технологии. (Computer Aided Software Engineering — автоматизация разработки ПО) При проектировании больших программ разработчик начинает с планирования своей работы, рисования некоторых диаграмм, написания каких-то предвари-тельных спецификаций, разработки некоторого макета, позво-ляющего определить, как все составные части будут взаимодей-ствовать между собой, решать поставленную проблему. В 60-х годах этот процесс был формализован с помощью блок-схем. Все, кто в то время занимался программированием, проходили через это. Считали, что эти понятия и инструментальные сред-ства можно было использовать не только для разработки про-грамм, но и для описания всех процессов на предприятии. Диаграммные методы играли ключевую роль в разработке про-грамм и в 80-е годы. С их помощью пытались описать все ста-дии разработки программ. Появились даже специальные рабо-чие станции для автоматизированного процесса разработки

Page 97: Pascal 8 Класс - Окулов

Осповпые управляющие конструкции

диаграмм. Идея полного описания и контроля разработки по-лучила название программной инженерии (software enginee-ring), дисциплины, позволяющей сложную программу строить в предсказуемом стиле и с качеством, которое можно измерить и гарантировать. Была сделана попытка автоматизации собст-венной работы («сапожник без сапог>>). Эта старая идея жива и в настоящее время, она имеет только другое название — CASE технологии. Первоначально под этим термином понимались средства автоматизации разработки программ. Сейчас он трак-туется как программные средства, поддерживающие процессы создания и сопровождения программ, включая анализ и форму-лировку требований, проектирование, генерацию кода, тести-рование, документирование, обеспечение качества и управле-ния проектом разработки. Отношение к CASE технологиям не однозначное. С одной стороны, считают, что CASE технологии обладают высокими потенциальными возможностями в части увеличения производительности труда, улучшения качества про-граммных продуктов, поддержки унифицированного и согласо-ванного стиля работы. При этом существующие CASE средства, используя методы структурного и объектно-ориентированного анализа и проектирования программ, имеют различные инст-рументарии (например, диаграммы, тексты и т. д.) для описа-ния внешних требований, связей между компонентами про-граммы, динамики работы. Другая точка зрения заключается в том, что CASE технологии не более чем пакеты рисования и конструирования диаграмм.

Истина, вероятно, находится посередине. Д. Васкевич при-водит пример из прошлого, связанного с индустрией обработки текстов. В 80-е годы системы обработки текстов были дорого-стоящими, большими и привязанными к конкретным ЭВМ. Эти системы были далеко за пределами возможностей обычных пользователей. Затем появилось следующее поколение для пер-сональных компьютеров, но и они были слишком ограничены и тяжелы в применении. Большинство пользователей не приме-няли их. В настоящее время программы типа Microsoft Word выполняют больше, чем аналогичные разработки 80-х годов, и для миллионов людей использование текстовых процессоров стало таким же привычным, как использование ручки. Исто-рия повторяется. Программ требуется все больше. Программи-стам необходимы инструментальные средства проектирования на базе компьютера.

Подведем итог нашего сжатого обзора технологий програм-мирования. Их развитие происходило и происходит по спира-4 - 4 5 2

Page 98: Pascal 8 Класс - Окулов

98 Часть первая

ли, не плоской, а пространственной. Каждый новый виток, «вбирая» все от предыдущего, решает основную проблему тех-нологии (разработка надежного и эффективного программного проекта с минимальными затратами) новым, более совершен-ным инструментарием (принципы декомпозиции и абстрагиро-вания, приемы анализа и синтеза и т. д.). Мера дезорганизации программных проектов (имеется в виду весь их «жизненный цикл») как сложных систем уменьшается. Первый виток — операциональный. Языки программирования: FORTRAN I и II, ALGOL 58, 60, COBOL, LISP и др. Нарабатываемые идеи: под-программа (подпрограммы возникли до 1950 года, но рассмат-ривались не как элемент абстрагирования, а как средство, упрощающее работу); типы данных и их описание, раздельная компиляция, блочная структура, обработка списков, указатели и т. д. Второй виток — структурный (объединяем этим терми-ном и нисходящее проектирование, и модульное). 1966-1985 годы. Языки программирования П Л / 1 , ALGOL 68, Pascal, Si-mula, С, Ada (наследник ALGOL 68, Pascal, Simula), Clos, С++ (возникший в результате слияния С и Simula) и т. д. В 70-е годы созданы тысячи языков и диалектов. Нарабатываемые идеи: подпрограммы как элемент абстрагирования (разработа-ны механизмы: передачи параметров; вложенности подпрог-рамм; локальных и глобальных переменных; теория типов; развитие модулей от группы логически связанных подпрог-рамм до раздельно компилируемых фрагментов со строго опре-деленным интерфейсом). Третий виток — объектно-ориентиро-ванный, с 1986 года. Языки программирования: Smalltalk, Object Pascal, С++. Основным элементом конструирования программы является модуль, составленный из логически связанных объек-тов и т. д. Учебник, если так можно выразиться, посвящен вто-рому витку развития этого сложного предмета — программиро-вания.

Page 99: Pascal 8 Класс - Окулов

Часть вторая

Процедуры и функции — элементы структуризации

программ

З а н я т и е № 10. О д н о м е р н ы е м а с с и в ы . Работа с э л е м е н т а м и

План занятия • раздел описания типов данных; • простой пример нахождения суммы чисел; • экспериментальная работа с программами поиска суммы

элементов массива, кратных заданному числу; нахожде-ния элементов с определенными свойствами в массиве це-лых чисел; формирование значений элементов массива с помощью генератора случайных чисел; вычисления фак-ториала числа;

• выполнение самостоятельной работы. Описание типов данных. При описании переменных через

двоеточие указывается ее тип. До этого занятия использова-лись только два типа данных: Integer и Boolean. Типы данных Integer и Boolean относятся к простым типам (простые, потому что не могут состоять ни из каких других типов). Тип перемен-ной определяет множество значений, которые она может при-нимать, и операции, которые могут быть над ней выполнены. Утверждение, после проделанной работы, понятное. С каждой встречающейся в программе переменной может быть связан то-лько один тип данных. Естественно, перечнем Integer и Boolean не исчерпывается весь список типов Турбо Паскаля. Он не ис-черпывается любым списком, ибо в языке есть возможность для конструирования своих типов данных. Однако, так или иначе, но любой, сверхсложный тип данных, который Вы со-здали в своей программе, сводится (или строится) к простым типам.

Описание типов данных имеет следующий вид: Туре <имя_типа>=<тип_данных>;

Page 100: Pascal 8 Класс - Окулов

100 Часть вторал

После этого в разделе описаний переменных у Вас появляет -ся возможность ссылаться на введенный тип д а н н ы х , он ведь имеет имя!

Var < и м я _ п е р е м е н н о й > : < и м я _ т и п а > ; П р и м е ч а н и е Не будем терять время на рассмотрение примеров. Схема использо-вания описания типов прояснится из последующего материала за-нятий. Простой пример. Необходимо н а й т и сумму 5 ц е л ы х чисел .

Р е ш е н и е очевидно. Program Му10_1; Var al,а2,аЗ,а4,а5,s: Integer; Begin

WriteLn (' Введите пять целых чисел'); ReadLn(al,а2,аЗ,а4,а5) ; s:= а1+а2+аЗ+аА+а5; WriteLn ('Ил сумма равна ' ,s) ReadLn;

End. А если требуется найти сумму 30 ц е л ы х чисел? Р е ш е н и е по

аналогии требует введения 30 однотипных переменных .

Одномерный массив — это фиксированное количество эле-ментов одного и того же типа , объединенных одним именем, где к а ж д ы й элемент имеет свой номер. Обращение к элементам массива осуществляется с помощью у к а з а н и я имени массива и номеров элементов. Н а м для работы требуется массив из 30 це-л ы х чисел.

Опишем в разделе типов свой тип — одномерный массив, со-с т о я щ и й из 30 целых чисел. Туре МуАггау = Array [1..30] Of Integer; Н а п о м н и м , что раздел типов начинается со служебного слова Туре, после этого идет и м я нового типа и его описание. Между именем типа и его описанием ставится з н а к «равно» (в разделе переменных между именем переменной и ее описанием ставится двоеточие). В нашем случае МуАггау — и м я нового типа данных; Array — служебное слово (в переводе с англий-ского означает «массив», «набор»); [1 . .30] — в квадратных скобках указывается номер первого элемента, затем, после двух точек, номер последнего элемента массива; Of — служебное слово (в переводе с английского «из» ); Integer — тип элемен-тов массива. И решение, простое решение без 30 переменных.

Page 101: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 101

Program Му10_1т; Const п=30; Type MyArray=Array[1..п] Of Integer; Var A: MyArray;

s,i: Integer; Begin

WriteLn ('Введите ' , n , ' чисел'); For l :=1 To n Do ReadLn ( A f i J ) ; s: = 0 ; For i:=l To n Do s:=s+A[i] ; WriteLn(' Их сумма равна ' ,s); ReadLn;

End.

Экспериментальный раздел работы

1. Н а й т и сумму элементов массива, кратных заданному числу. Предыдущее решение изменится незначительным образом. Добавляется описание еще одной переменной для хранения значения числа , на кратность которому проверяются значе-н и я элементов массива. Появляются операторы WriteLn('Введите число ' ) ; ReadLn (к) ;

и изменяется оператор из тела ц и к л а

I f Ah] Моа к = 0 Then s:=s+A[i];

П р и м е ч а н и е Не забудьте изменить значение константы п. Вводить при каждом запуске программы 30 чисел — утомительное занятие.

Изменить программу так, чтобы определялось количество положительных и отрицательных элементов в данном массиве. Суть основного изменения программы заключается во введении двух переменных (счетчиков — pos, neg) для хранения значе-ний количества положительных и отрицательных элементов в массиве. Найдите местонахождение в программе следующих строк программного кода:

pos, neg:Integer; pos:~0;neg:=0; I f A[ i ]>0 Then In с (pos) Else I f A [ l ]<0 Then In с (neg) ; WriteLn (pos: 4, neg: 4) ;

Page 102: Pascal 8 Класс - Окулов

102 Часть вторал

Д а н н а я п р о г р а м м а не решает задачу н а х о ж д е н и я и количе-ства н у л е в ы х элементов в массиве . Проведите ее м о д и ф и к а ц и ю и д л я р е ш е н и я этой задачи .

И з м е н и т ь п р о г р а м м у т а к , чтобы в массив В з а п и с ы в а л и с ь номера ч е т н ы х элементов массива Л . Введение м а с с и в а В и ра-бота с н и м требует в в е д е н и я переменной (j) д л я о б р а щ е н и я к э л е м е н т а м В. Суть р е ш е н и я з а к л ю ч а е т с я в просмотре элемен-тов массива А (это м ы умеем) , в ы я в л е н и и ч е т н ы х элементов и з аписи и х номеров по т е к у щ е м у з н а ч е н и ю п е р е м е н н о й ; в мас-сив В.

П р и м е ч а н и е Мы записываем не значения элементов, а их номера! К сожалению, отличие между этими понятиями осознается учащимися при пер-вом знакомстве не сразу, даже не за решение одной задачи.

Program Му10_2; Const п=10; Type MyArray=Array[l..п] Of Integer; Var А,В: МуАггау;

i , j : Integer; Begin

WriteLn('Введите ' , п , ' чисел'); For 1: =1 То п Do Rea dLn (А [ l ] ) ; 3--=0; For i:=l То л Do

I f А[ i ] Mod 2 = 0 Then Begin Inc ( j ) ;B [ j ] : =1 ; End;

For i:=l To j Do Wnte(B[i]:3); W r i t e L n ; ReadLn;

End. 2. Научимся определять, есть ли в массиве элемент с определен-

ными свойствами, например, есть ли отрицательный элемент. А если таких элементов несколько? Требуется ли определять номера этих элементов в массиве или выводить их значения? Это все разные задачи. Уточним постановку — номер послед-него отрицательного элемента. Напрашивается очевидное решение, его фрагмент:

For i:=l То п Do I f A[i]<0 Then к:=i;

Page 103: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 103

I f к=0 Then <отрицательных элементов в массиве нет> Else <последний отрицательный элемент является к по счету в массиве А>;

Это решение плохое. Действительно, если единственный от-рицательный элемент в массиве записан на первом месте, то мы выполняем л и ш н и е п-1 сравнение, просматривая массив до конца . Договоримся о том, что наш программный код должен экономно, рационально использовать ресурсы компьютера, в частности и время его работы. Эффективный алгоритм значит в computer science больше, чем сверхбыстродействующий компь-ютер. Следующее изменение

I f A[i]<0 Then Begin k:=i; Break; End; решает возникший вопрос, но... Это изменение приводит к тому, что наш небольшой фрагмент логики имеет одну точку входа и две точки выхода. Тоже плохо, очень плохо. Обилие Break, GoTo превратит нашу программу в «спагетти», а у нас не 60-е годы, первый виток развития технологий программирования м ы оставили истории. И еще одно замечание. В элементарной задаче задействованы две переменные. А если задача не элемен-тарная и м ы будем так их «транжирить»? Нам не хватит, об-разно выражаясь , никакого алфавита. Приемлемый вариант решения имеет вид:

1: =л ; While (1>=1) And (A[i]>=0) DoDec(i); I f Kl Then <отрицательных элементов нет в массиве> Else <последний отрицательный элемент имеет номер i>;

Чуть-чуть изменим задачу. Требуется найти значения и но-мера всех отрицательных элементов. В этом случае использова-ние оператора For неизбежно, ибо нам необходимо просмотреть все элементы массива. Итак, не наше желание определяет тип используемого оператора цикла , а решаемая задача!

($R+) Program Му10_3; C o n s t n—7; Туре МуАггау=Аггау[1..n] Of Integer; Var A:MyArray;

i:Integer; Begin

Page 104: Pascal 8 Класс - Окулов

104 Часть вторал

WriteLn('Ввод элементов массива. Не забудьте об отрицательных элементах. ' ) ; For i:=l То л Do Read (Al 1] ) ; WriteLn ('Вывод отрицательных элементов массива и их номеров (индексов)'); For 1:=1 То л do

I f A[i]<0 Then WriteLn (Ali],' ' , i , ' ' ) ; Readln;

End. Текст программы поиска номеров всех отрицательных эле-

ментов в массиве содержит строки: For i:=l То п do Измените во второй строке п на п+1 и поставьте перед текстом програм-м ы директиву компилятора {$R+}. При запуске программы по-явится ошибка периода выполнения: Error 201: Range check er ror. Она является сообщением о том, что значение переменной i (индекс массива Л) в ы ш л о за допустимый предел, т. е. за значе-ние константы п.

Директивы компилятора управляют режимом компиляции . Ди-ректива начинается с символа $ после открывающей скобки комментария, за которым следует и м я директивы (состоящее из одной или нескольких букв), определяющее ее назначение. Контроль вхождения в диапазон осуществляется с помощью директивы {$R+} ({$R-}), при этом устанавливается или отме-няется генерация кода контроля вхождения в диапазон. В ре-жиме {$R-^} все индексы массивов и строк контролируются на выход за границы, а все присваивания значений скалярных пе-ременных и ограниченных типов проверяются на вхождение в диапазон. При выходе за границы выполнение программы пре-кращается и выдается сообщение об ошибке.

П р и м е ч а н и е При работе с массивами настоятельно рекомендуем использовать эту директиву при отладке программ.

3. Формирование значений элементов массива путем ввода их с клавиатуры — достаточно утомительное занятие. Давайте попробуем использовать для этих целей генератор случайных чисел. Это сложное название мы будем понимать очень про-сто. Есть что-то (черный ящик) , и это что-то выдает числа, причем какое число выдается следующим за очередным, нам неизвестно. Этим черным ящиком в Турбо Паскале является функция Random. Она возвращает случайное число.

Page 105: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 105

Ф у н к ц и я Random. Описание: Random[(range:Word)]. Напомним, что в квадрат-ных скобках указывается необязательный параметр конструк-ц и и Турбо П а с к а л я . Тип результата: Real или Word в зависимости от н а л и ч и я пара-метра. Комментарий. Если параметр не задан, то результатом являет-ся число типа Real в диапазоне 0<х<1. При наличии параметра возвращается число типа Word в диапазоне 0<x<range. Обрати-те внимание на то, что верхняя граница диапазона не достига-ется — строгое неравенство.

П р и м е ч а н и е С типом Real у нас еще не было работы, она впереди.

I $R~} Program Myl 4 ;

Const п=10; Type MyArray=Array[1..п] Of Integer; Var A:MyArray;

l: Integers-Begin

(Randomize;} WriteLn('Формирование значений элементов массива А') ; For i: =1 То п Do A[i] := Random (21) (-10} ; WriteLn('Вывод'); For i : =1 To n Do Write (A[ l] : 5) ; ReadLn;

End. Запустите несколько раз программу. На экране мы видим

одну и ту ж е последовательность чисел в диапазоне от 0 до 20. Уберите фигурные скобки у - 1 0 и снова запустите несколько раз программу. Последовательность чисел на экране одна и та же, но числа из интервала от - 1 0 до 10. Объясните этот факт. Попробуйте получать числа из любого интервала, например от - 1 7 до 25. Продолжим наши эксперименты. Уберите фигурные скобки у процедуры Randomize. Повторите многократный за-пуск программы. Последовательности чисел на экране разные. В чем дело? Н а ш черный я щ и к , функция Random, начинает ге-нерировать в первом случае числа (каким-то неизвестным нам образом) от фиксированного начального числа. Во втором слу-чае эти начальные числа меняются от запуска к запуску (про-

Page 106: Pascal 8 Класс - Окулов

106 Часть вторал

цедурой Randomize) и последовательности получаются разные. Следующим изменением является включение контроля на вы-ход за допустимый диапазон {$R+}. После запуска м ы видим у ж е знакомую ошибку: Error 201: Range check error. Прочитай-те еще раз внимательно описание функции R a n d o m и попробуй-те найти объяснение причины возникновения ошибки . Если объяснение не найдено, то измените текст программы.

<$R+) Program Му10_4т;

Const п=10; Var 1:Integer; Begin

WriteLn ('Вывод 10 случайных чисел в диапазоне от -10 до 10') ;

For i:=l То п Do WriteLn (Random (21) -10) ; ReadLn;

End. Ошибки после запуска программы нет, но на экране не чис-

ла из интервала от - 1 0 до 10, а какая -то странная последовате-льность чисел. Она может быть и такой: 65530, 1, 2,4, 65527, 2, 65528, 65534, 3, 2. Итак , где же истина? Напомним, что диа-пазон для целых чисел типа Word от 0 до 65535 и число 65535 1 0 ( 2 1 6 -1 )=1111111111111111 2 , а это - 1 в дополнитель-ном коде. Число 65534 соответствует - 2 в дополнительном коде. В первой версии программы результат функции Random имеет тип Word. Из этого числа вычитается целое число типа Integer. Типы разные, выполняется преобразование типов по правилу, описанному в занятии 2, т. е. к типу Longlnt. А эле-мент массива имеет тип Integer — противоречие. Измените тип элементов массива А на Longlnt и сравните результаты работы программ. Во второй версии контроль на выход за диапазон не выполняется, может быть, потому что нет операции присвое-ния (контролировать нечего и некого)? Верните тип Integer в описании элементов массива и измените одну строку програм-мы на

For i:=l То n Do A[i] :=In teger (Random (21)) -10 ; Запустите программу. Она прекрасно работает. Объясните,

что происходит в этом случае. 4. Научимся вычислять факториал натурального числа N. Фак-

ториал числа — это произведение чисел 1*2*3*...*(N-1 )*N (обозначается к а к N!). Сложность задачи в том, что уже 8!=40320, а 131=6227020800. Типы данных Integer, Longlnt

Page 107: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 107

п р и м е н и м ы весьма в ограниченном диапазоне натуральных чисел. Д л я представления факториала договоримся исполь-зовать массив. Пример.

В массиве записано значение 11!=39916800. К а к и м образом? В А[0] фиксируется число занятых элементов массива, в А[1] — цифра единиц результата, в А[2] — цифра десятков результата, в А[3] — цифра сотен результата и т. д. Почему так , а не наоборот? Такая запись позволяет исключить сдвиг элементов массива при переносе значений в старший разряд. А сейчас наберите, к а к обычно, текст программы, выполните ком-пиляцию и, открыв окно Watch , выполните ее в пошаговом ре-жиме , отслеживая изменение значений переменных при не очень большом значении N. Добейтесь полного понимания ло-г и к и работы программы.

Program Му10_5; Uses Crt; Const MaxN=300; Type MyArray=Array[0..MaxN] Of Integer; Var A:MyArray; Var i,j,r,w,N:Integer; Begin

ClrScr; FillChar (A, SizeOf (A) ,0) ; WriteLn('Введите число, факториал которого необходимо подсчитать.'); ReadLn (N) ; А [ 0 ] :=1 ; А[1 ] : =1 ; j : =2 ; { *Начальные присвоения, начинаем вычислять 2'.*} While (j<=N) And (A[0]<MaxN) Do Begin (*Второе условие фиксирует факт выхода за пределы массива. *}

r:=0;i:=l;{*г - перенос из разряда в разряд при выполнении умножения числа j на очередную цифру A[i] предыдущего результата.*) While (i <=А [0]) Or (roO) Do Beginf *Пока не «прошли» все цифры предыдущего результата или есть перенос цифры в старший разряд. *}

А[1] А[2] А[3]

0

Page 108: Pascal 8 Класс - Окулов

108 Часть вторал

w:=A[i]*j + r;{*Умножаем очередную цифру и прибавляем цифру переноса из предыдущего разряда. * 1 А[1]:=ы Mod 10;{*Находим новую цифру с номером 1. *} r:= w Div 10;{*Вычисляем значение переноса. *) I f А[А[0]+1]<>0 Then Inc(A[0]);I*Иэменяем, если необходимо, количество задействованных элементов массива А, длину полученного результата. *) Inc(i) ;

End; I n c ( j ) ;

End; For l:=A[0] DownTo 1 Do Write(A[l]);(*Вывод результата. *} WriteLn; ReadLn;

End. Найдите число N, при котором 300 элементного массива А

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

всех чисел в интервале от 1 до N. Измените программу так , чтобы вычислялись степени чисел

a N , например З 1 0 0 .

Задания для самостоятельной работы

1. Дан массив целых чисел. Найти: • сумму элементов массива, больших данного числа А (А

вводить с клавиатуры) ; • сумму элементов массива, п р и н а д л е ж а щ и х промежутку

от А до Л (А и Л вводить с клавиатуры) ; • максимальный элемент массива и его номер, при усло-

вии, что все элементы различные; • номера всех элементов массива с м а к с и м а л ь н ы м значе-

нием. • минимальный элемент массива; • сумму элементов массива с krro по кг-й, где k1 и k2 вво-

дятся с клавиатуры; • количество нечетных элементов массива; • количество отрицательных элементов массива; • сумму первых пяти элементов массива;

Page 109: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 109

• все элементы, кратные 3 или 5. Сколько их; • сумму всех четных элементов массива, стоящих на чет-

ных местах, то есть имеющих четные номера; • сумму всех четных элементов массива (или сумму эле-

ментов, кратных заданному числу); • сумму положительных элементов массива; • сумму элементов, имеющих нечетное значение: • сумму элементов, имеющих нечетные индексы; • сумму положительных элементов, значения которых ме-

ньше 10; • удвоенную сумму положительных элементов; • сумму отрицательных элементов; • индексы тех элементов, значения которых больше задан-

ного числа А; • количество элементов массива, значения которых боль-

ше заданного числа А и кратны 5; • индексы тех элементов, значения которых кратны 3 и 5; • индексы тех элементов, значения которых больше значе-

ния предыдущего элемента (начиная со второго); • количество тех элементов, значения которых положите-

льны и не превосходят заданного числа А. 2. Определить:

• сколько элементов массива превосходят по модулю за-данное число А;

• есть ли в данном массиве два соседних положительных элемента? Найти номера первой (последней) пары;

• есть ли в данном массиве элемент, равный заданному числу? Если есть, то вывести номер одного из них;

• есть ли в данном массиве положительные элементы, крат-ные k (ft вводить с клавиатуры);

• номер первого отрицательного элемента, делящегося на 5 с остатком 2;

• пару соседних элементов с суммой, равной заданному числу;

П есть ли 2 пары соседних элементов с одинаковыми знаками; • номер последней пары соседних элементов с разными

знаками. 3. Измените программу вычисления факториал числа N так, что-

бы вычислялись: 1*3*5* ... * (2*N-1), 2*4*6*8* ... * (2*N). 4. Измените программу вычисления степени числа так, чтобы

она могла вычислять степени отрицательных целых чисел. 5. Измените программу вычисления степени числа так, чтобы

в степень возводились рациональные числа.

Page 110: Pascal 8 Класс - Окулов

110 Часть вторал

Материал для чтения

1. Одномерный массив — это конечное упорядоченное мно-жество элементов. За первым элементом идет второй, за вто-рым — третий и т. д. В силу этого элементы массива можно пере-нумеровать целыми числами — индексы элементов. Для доступа к элементу массива на логическом уровне достаточно знать имя массива и индекс элемента. Память компьютера представляет собой последовательность ячеек, имеющих адреса, с возможно-стью обращения к ячейкам по их номерам. Для массива обычно выделяется сплошной участок памяти компьютера. Как из ло-гического адреса элемента массива вычисляется физический адрес? С этой целью массив имеет, если так можно выразиться, описатель (дескриптор). Приведем его структуру, например, для массива Var A: Array[1..10] Of Integer.

j Имя 1 А

j Адрес начального элемента j Определяется системой, обозначим — Addr(A[1]}

Индекс начального элемента 1 1

Индекс последнего элемента | 10 | Тип элемента | Integer i

!| Длина элемента i 2 байта j

Вычисление адреса элемента г выполняется по формуле: Addr(A[i])=Addr(A[ 1 ])+(i-l )*2.

2. Общие сведения no организации ЭВМ. Структура простой ЭВМ показана на рисунке.

Page 111: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 111

«Мозгом» вычислительной машины является центральный процессор. В его функцию входит выполнение программ, находя-щихся в основной памяти, путем выборки, проверки и последова-тельного выполнения составляющих их команд. Центральный процессор состоит из нескольких частей. Устройство управления осуществляет выборку команд из основной памяти и определе-ние их типа. Арчфметическо-логическое устройство осуществ-ляет выполнение таких операций, как сложение, сдвиг и т. д. Центральный процессор содержит высокоскоростную память для запоминания промежуточных результатов и управляющей информации. Эта память состоит из регистров, каждый из ко-торых имеет определенное назначение. Одним из регистров яв-ляется счетчик команд, указывающий адрес команды, которую необходимо выполнить следующей. Регистр команд содержит текущую выполняемую команду. Центральный процессор вы-полняет каждую команду в виде последовательности простых операций:

• выбор очередной команды из основной (оперативной) па-мяти в регистр команд;

• изменение в счетчике команд, чтобы он указывал на адрес команды, следующей за выбранной;

• определение типа выбранной команды; • проверка, требуются ли для выполнения выбранной коман-

ды какие-либо данные, и если они нужны, то определение их места нахождения в памяти;

• загрузка во внутренние регистры центрального процессо-ра требуемых данных из памяти;

• выполнение команды; • запоминание результатов выполнения команды в задан-

ных ячейках памяти; • переход к первому действию для выполнения следующей

команды. Такое описание функционирования центрального процессо-

ра по существу представляет программу, написанную на ка-ком-либо языке программирования.

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

Page 112: Pascal 8 Класс - Окулов

112 Часть вторал

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

Выше по тексту сказано, что на рисунке приведена структу-ра простой ЭВМ. В материале для чтения занятия 3 мы рас-сматривали понятие структуры. Структура ЭВМ определяется внутримашинным системным интерфейсом. Говоря простым язы-ком, это то, как связаны составные части. Логически возмож-ны два типа связей: каждая составная часть связана с другой отдельными «проводами»; все составные части связаны друг с другом через общую систему проводов (системная шина). Ка-кое-то количество проводов, объединенных (по назначению) об-щей целью, называют шиной. Все остальные «конфигурации» являются комбинациями этих типов. Исходя, например, из опи-сания принципов обмена данными между центральным процессо-ром и памятью, системная шина должна состоять из шины дан-ных, шины адреса, шины инструкции и, так как по проводам «бегают электрончики» — шины питания. При передаче адреса и данных логически возможны несколько вариантов: переда-вать их по различным проводам или передавать по одним и тем же проводам, но в разные моменты времени. Выбор того или иного принципа связей определяет структуру ЭВМ. Однако ограничимся этим. Детализация структуры ЭВМ не входит в круг наших задач, это предмет отдельных курсов, например «Архитектура ЭВМ», «Микроэлектроника».

3. Принципы Джона фон Неймана. Несмотря на большие разнообразия существующих в настоящее время ЭВМ, в основы их заложены некоторые общие принципы. Эти принципы были сформулированы в 40-х годах двадцатого столетия выдающим-ся американским математиком Джоном фон Нейманом и позво-лили создать устройство обработки информации с достаточно простыми и универсальными принципами работы. Это устрой-ство называется ЭВМ. Первый принцип — принцип произволь-

Page 113: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ И З

ного доступа к основной (оперативной) памяти. Структурно основная память, как мы говорили, состоит из ячеек. Принцип гласит о том, что процессору в произвольный момент времени доступна любая ячейка, причем время доступа (чтения или за-писи) одинаково для всех ячеек. Чтобы обеспечить такой до-ступ, ячейки памяти обязаны иметь свои уникальные имена. Этими именами являются номера ячеек. Для лучшего понима-ния действия этого принципа можно сравнить этот доступ с до-ступом к данным на магнитной ленте (магнитофон). Данные, записанные на магнитной ленте, также допустимо разбить на элементарные единицы — слова. При произвольном положе-нии магнитной ленты доступно лишь то слово, которое нахо-дится под головками чтения — записи. Для чтения слова на другом участке магнитной ленты необходимо предварительно переместить этот участок под блок головок, т. е. доступ к этому слову возможен лишь после просмотра предыдущих участков магнитной ленты. Поэтому магнитную ленту принято называть памятью с последовательным доступом.

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

4. Возникновение информатики как науки связано с появле-нием ЭВМ и относится к 60-м годам двадцатого столетия. В са-мом общем смысле под информатикой понимают фундамента-льную естественную науку, изучающую процессы передачи, накопления и обработки информации с помощью ЭВМ.

Термин «Informatique» введен во Франции на рубеже 60-70-х годов. Однако еще раньше в США был введен в употребле-ние термин «Computer Science» для обозначения науки о преоб-разовании информации с помощью ЭВМ. В настоящее время термины употребляются в эквивалентном смысле. Содержание ядра информатики, на наш взгляд, определяют программные и технические средства.

На этом занятии рассмотрено понятие массива, а в материа-ле для чтения дан обзор структуры простой ЭВМ и принципов Джона фон Неймана. Понятие массива носит фундаменталь-ный характер. Эта структура данных наиболее соответствует структуре ЭВМ. Образно выражаясь, оперативная память ЭВМ —

Page 114: Pascal 8 Класс - Окулов

114 Часть вторал

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

Page 115: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 115

З а н я т и е № 11. П р о ц е д у р ы

План занятия • структура программы; • о взаимодействии программы и процедуры; • стандартные определения; • экспериментальная работа с программами: перестановки

значений переменных а, Ь, с в порядке возрастания, вы-числения в ы р а ж е н и я у=ап

+хп+ап_1Л 'хп+.. .+а2*х2+а1

, , 'х1+а0 ; формирования значений элементов одномерного массива с помощью датчика случайных чисел; поиска элементов, п р и н а д л е ж а щ и х двум массивам одновременно; переста-новки частей массива;

• выполнение самостоятельной работы.

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

Program <имя программы>; Label <метки>; Const <описание констант>; Туре <описание типов данны\>; Var <описание переменных>; <процедуры и функции>; Begin

<основное тело программы>; End.

До этого момента времени мы использовали из раздела опи-саний только описание переменных и типов. На этом занятии мы начнем изучать процедуры. Структура процедуры повторя-ет структуру программы. Отличия выделены «полужирным» шрифтом.

Procedure <имя процедуры> (<параметры>); Label <метки>; Const <описание констант>; Туре <описание типов данных>;

Page 116: Pascal 8 Класс - Окулов

116 Часть вторал

Var <описание переменных>; <процедуры и функции>; Begin

<основное тело процедуры>; End;

К а к и е вопросы возникают при знакомстве со структурами программы и процедуры? Их много. Начнем со взаимодействия программы и процедуры к а к по управлению, так и по д а н н ы м .

П р и м е ч а н и е В наших решениях задач на протяжении всей книги описание ме-ток мы постараемся не использовать. О взаимодействии программы и процедуры. Чтобы объяс-

нение было н а г л я д н ы м , обратимся к примеру , приведенному на рисунке . Слева приведен фрагмент текста основной програм-мы, справа — процедура вычисления суммы двух целых чисел. Что м ы видим? Процедура вызывается указанием ее имени, ко-торое записывается после зарезервированного слова Procedure, используемого для обозначения процедуры.

Begin ~ 'иге Sum (\ ,у: Integer ;Var z: Integer) ;

WriteLn (с) ;

End Л и н е й н ы й ход выполнения основной программы становится

нелинейным — управление вычислительным процессом пере-дается на участок программного кода, з анимаемый процеду-рой. После выполнения процедуры осуществляется возврат на оператор основной программы, следующий за вызовом проце-дуры (End; в процедуре очень содержательный оператор, сверх-содержательный — это «стрелка», возврат управления в логи-ку, из которой вызывается процедура). Мы не рассматриваем вопросы о том, к а к это выполняется . Например , к а к осуществ-ляется возврат на оператор WriteLn(c). Н а ш е объяснение идет на уровне констатации фактов, и только. Итак , взаимодействие программы и процедуры по управлению м ы обозначили. Перей-дем к взаимодействию по данных. В программе определены пе-ременные а, Ъ, с. В процедуре — х, у, г её параметры, но они яв-ляются переменными процедуры. Причем х, у описаны без идентификатора Var, а г — с идентификатором Var. Поясним разницу очередным рисунком.

ReadLn (a ,b) Sum (a,b,c) ;

Page 117: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 117

При вызове процедуры Sum(a,b,c) из основной программы значение переменной а присваивается переменной х процедуры Sum, а значение переменной Ъ становится значением перемен-ной у, и все. Стрелочка на рисунке в одну сторону. Чуть слож-нее с переменными с и г . Стрелочка на рисунке и в ту, и в дру-гую стороны. Образно выражаясь, процедура Sum знает о том, где в памяти компьютера находится переменная с, и она знает, что при изменении значения переменной г необходимо произ-вести соответствующее изменение значения переменной с. Это обратная связь по данным от процедуры к основной программе. На такой тип связи указывает идентификатор Var в описании параметров процедуры.

Перейдем к ряду стандартных определений. В любой про-грамме все переменные делятся на глобальные и локальные. В нашем фрагменте программы переменные а, Ь, с — глобальные, а х, у, г — локальные. Глобальные переменные — это перемен-ные из раздела описаний основной части программы, а локаль-ные — из раздела описаний процедур и функций. Если разница только в месте описания переменных, то «стоит ли шкурка вы-делки»? Добавим ещё одно предложение. Локальные перемен-ные существуют только в течение времени работы процедуры, определяются (создаются) при её вызове и исчезают после за-вершении работы процедуры (после выполнения «сверхсодер-жательного» оператора End;). Таким образом, если бы в основ-ной программе было три фрагмента с вызовом процедуры Sum (желательно с различными параметрами), то для программного кода процедуры Sum три раза выделялось место в оперативной памяти, соответственно, и для переменных х, у, г и три раза

Page 118: Pascal 8 Класс - Окулов

освобождалось. Если такое объяснение облегчает понимание разницы между глобальными и локальными переменными, то считаем его правильным.

Параметры. При описании процедуры указывается список формальных параметров. Каждый параметр является локаль-ным по отношению к описываемой процедуре, к нему можно обращаться только в пределах данной процедуры (в нашем при-мере х, у, г — формальные параметры). Фактические парамет ры — это параметры, которые передаются процедуре при обра-щении к ней (а, Ъ, с — фактические параметры). Число и тип формальных и фактических параметров должны совпадать с точностью до их следования.

Параметры-значения. Другими словами, передача парамет-ров по значению. Копия фактического параметра становится значением соответствующего формального параметра. Внутри процедуры можно производить любые действия с данным фор-мальным параметром (допустимые для его типа), но эти изме-нения никак не отражаются на значении фактического пара-метра, то есть каким он был до вызова процедуры, то таким же и останется после завершения ее работы (х, у — парамет-ры-значения).

Параметры-переменные. Другими словами, передача пара-метров по ссылке. Это те формальные параметры, перед кото-рыми стоит идентификатор Var. Передается адрес фактическо-го параметра (обязательно переменной), после этого формальный параметр становится его синонимом. Любые операции с форма-льным параметром выполняются непосредственно над фактиче-ским параметром.

Договоримся о том, как мы будем стараться использовать процедуры, может быть, в ущерб эффективности программ, на-пример, с точки зрения количества переменных и т. д. Каждая процедура должна иметь одну точку входа и одну точку выхо-да, использование глобальных переменных в процедуре должно быть минимально, взаимодействие вызывающей логики с про-цедурой должно осуществляться (по возможности) только через параметры. Зачем нам эти оговорки? Мы осваиваем структур-ную технологию разработки программ, при этом на каждом этапе текущая задача разбивается на ряд подзадач, определяя тем самым некоторое количество отдельных подпрограмм (под программа — это повторяющаяся группа операторов, оформ-ленная в виде самостоятельной программной единицы). При этом мы стараемся структурировать задачу не только по управ-

Page 119: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 119

лению, но и по данным, используя при этом весьма ограничен-ный набор инструментов (параметры-значения, параметры-ссыл-ки). К о н ц е п ц и я процедур и функций — один из механизмов второго витка развития технологий программирования, а имен-но, структурного проектирования .

Экспериментальный раздел работы

1. Составить программу перестановки значений переменных а, Ь, с в порядке возрастания, т. е. так, чтобы а<Ь<с. Текст ре-ш е н и я простой.

Program Myll_l; Var а ,Ь,с:Integer; Procedure Swap (Var x,у:Integer);

Var t:Integer; Begin

t:=x; <:=y;y:=t; End;

Begin WriteLn('Введите три числа ' ) ; ReadLn (a ,b, c) ; I f a>b Then Swap (a,b) ; I f b>c Then Swap (b, c) ; I f a>c Then Swap (a,c) ; WriteLn (a, ' ' ,b, ' ' ,c) ; ReadLn ;

End. Найдите ошибку в решении. Исправлению подлежит и м я

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

2. Составить программу вычисления выражения у=ап*хп+аг1_1 * *хп+ . . .+а 2 *х 2 +а 1 *х 1 +а 0 , где все данные — целые числа. Ко-эффициенты a n , a ^ j , ..., а р а0 являются первыми членами арифметической прогрессии, определяемой первым элемен-том (q) и разностью между соседними элементами (d). Зна-чения q, d, n, х вводятся с клавиатуры. Например, q=2, d=3, п=5 и х = 4 . Н а м необходимо вычислить выражение 2*4 5 +5* *4 4 +8*4 3 +11*4 2 +14*4 1 +17*4° .

Page 120: Pascal 8 Класс - Окулов

Program Myll_2: Var q, d,n,x,t,w,1: Integer;

s: Integer; Procedure Degree(x,у: Integer; Var st: Integer);

Vai l:Integer; Begin

st:=1; For i:=l To у Do st:=st*x;

End; Begin WriteLn('Введите исходные данные - четыре не очень больших числа'); ReadLn(q,d,n,х); s:=0;t:=n; For i:=l To n+1 Do Begin

Degree(x,t,w);s:=s+q^w; Dec(t);Inc(q,d) ;

End; WriteLn('Результат r , s ) ; ReadLn;

End. Использование процедуры д л я р е ш е н и я этой з а д а ч и неско-

лько искусственно, но простит нас читатель . Н а ш а цель — от-работать механизм использования процедур, а д л я этого соста-вьте таблицу изменения значений переменных q, t, w при работе программы. Проверьте правильность з аполнения т а б л и ц ы , ис-пользуя отладчик и р е ж и м пошагового исполнения программы. Зафиксируйте значения q, d, х и найдите экспериментальным путем (или м о д и ф и ц и р у я программу) то значение п, при кото-ром диапазона типа Integer не д о л ж н о «хватать» д л я х р а н е н и я результата .

П р и м е ч а н и е Переменные с именем х в основной программе и в процедуре De-gree — это разные переменные! З а п и ш е м н а ш пример по-другому: 17+4*(14+4*(11+4*(8+

+4*(5+4*2)))) . Результат вычислений, естественно, тот же са-м ы й . В общем виде запись выглядит следующим образом: а 0 + +х*(а 1 + х * (а2 +х*(... +X* ( a n , 1 +х*ап)))). Запись полиномов, чи-сел, р а з л о ж е н н ы х по степеням основания системы счисления , называется схемой Горнера. Измените программу так , чтобы вычисление в ы р а ж е н и я осуществлялось по схеме Горнера. Уч-

Page 121: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 121

тите, что в этом случае Вам до первой итерации необходимо знать последний член арифметической прогрессии.

А что означает запись 4 2 * 5 + 4 * 8 + 4 * 11 + 4 * 14 + 4 * 1 7 + ?

Можно ли вычислить выражение? Оказывается, да. Берём два числа 4 и 2 и выполняем с ними операцию, записанную по-сле них, т .е. умножение . Сохраняем результат и продолжаем вычисление. Берём 8 и 5 и выполняем сложение. Потренируй-тесь. Н а п и ш и т е несколько примеров и преобразуйте их запись к такому виду. Она называется обратной польской записью, в честь математика Я. Лукашевича . Д л я общего случая в нашей задаче обратная польская запись имеет вид: an х * а п - 1 + ... а2 х * а : + х * а 0 + . У Вас есть процедура Part, выполняющая одно арифметической действие (умножение или сложение) над дву-мя числами. Напишите программу для решения нашей задачи с ее использованием.

Procedure Par t (у. ,у: Integer ; t: Boolean/Var z: In teger) ; Begin

I f t Then Else z:=x*y; End;

У нас есть три реализации одной и той же задачи. Сравните количество операций умножения и сложения, необходимых для получения результата в каждой из них. Какой вывод дол-ж н ы мы сделать?

3. Задание значений элементам одномерного массива (с помо-щ ь ю генератора случайных чисел) и вывод результата на эк-ран м ы рассмотрели на предыдущем занятии. Оформим эти действия к а к процедуры. Program Myll_3; Const n=8;l=-10;h=21;

Type MyArray=Array[1..n] Of Integer; Var A:MyArray;

Procedure I n i t ( t , v,w:Integer; Var X:MyArray); Var i-.Integer; Begin

Randomize; For i :=1 To t Do X [ l ] :=v+Integer (Random (w)) ;

Ends-Procedure Print (t: In teger ;X: MyArray) ; Var i:Integer;

Page 122: Pascal 8 Класс - Окулов

122 Часть вторал

Begin For i:=l To t Do Write (X [ 1 ] : 5) ; { *Да простят нас читатели за использование числа 5 (5 позиций на экране для вывода X[i]) в этой процедуре, мы еще только учимся. *}

WriteLn; End;

Begin WriteLn('Формирование значений элементов массива А') ; Irnt(n,l,h,A);{ * Значения элементов массива А формируются из интервала целы', чисел от 1 до h. Количество элементов п.*} Wn teLn (' Выв од') ; Print<п,А);f*п первые элементов массива А выводятся на экран (в данном случае) . * >

End. Казалось бы, что м ы только проиграли. Б ы л о несколько

строк программного кода, а сейчас ... Пожертвуем к а ж у щ е й с я простотой предыдущей версии программы. С этого момента бу-дем стараться создавать программы так , чтобы основная про-грамма состояла из вызовов процедур и ф у н к ц и й . Нарисуйте схему (по подобию тех, что приведены в начале занятия) , отра-ж а ю щ у ю взаимосвязь основной программы и процедур к а к по управлению, так и по данным.

Изменим заголовок процедуры Print на Procedure Print(n: Integer;X:Array[l..n] Of Integer) и запустим программу. Резу-льтат не заставит себя ждать . Это ошибка Error 54: OF expec-ted. Курсор находится на квадратной скобке, т .е. после слова Array ожидается Of. «Пойдем на поводке» у системы програм-мирования, изменим на Procedure Print(n:Integer;X:Array Of Integer). Результат чуть-чуть изменился , у ж е знакомая ошиб-к а — Error 201: Range check error. Отключим контроль выхода за пределы диапазона — {$R-j. Программа заработала, точнее, она выдает результат. Итак , сплошные загадки. Попробуйте дать им разумное объяснение.

4. Даны два одномерных массива. Н а й т и элементы, принадле-ж а щ и е и тому и другому массивам. Н а ш а технология напи-сания программ (использование процедур и функций) начина-ет приносить дивиденды. Процедуру Init при решении задачи требуется использовать два раза с различными параметрами, процедуру Print — три раза. Сделаем «костяк» программы. Процедуры Init и Print, естественно, не приводятся, они у нас

Page 123: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 123

почти универсальны и берутся из предыдущего задания. Про-грамма (ее «костяк») должна компилироваться. Сохраните ее. Набирать весь текст программы, а затем приступать к отлад-ке — это «дурной» тон в программировании, очень «дурной». Возьмите за правило и всегда его придерживайтесь — «в лю-бой момент времени у Вас должна быть компилируемая про-грамма, сохраненная на внешнем носителе». Кроме того, текст любой процедуры и, естественно, основной программы дол-ж е н помещаться на экране и, конечно, быть читаемым. ($R + ) Program Myll_4;

Const n=10;la=-10;ha=21; т=8 ; lb=-15;hb=31;

Type MyArray-Array[1..n] Of Integer; Var А,В,С:MyArray;

к: Integers-Procedure Solve (qx, qy: Integer ; Var qz:Integer; X, Y:MyArray; Var Z: MyArray);

Begin Ends-

Begin Init (n,la,ha, A) ; WriteLn ('Вывод значений элементов массива А') ; Print (п, А) ; Init(m,lb,hb,В) ; WriteLn('Вывод значений элементов массива В') ; Print (m,B) ; Solve(n,m,k,A,B,C);

WriteLn ('Вывод тех целых чисел, которые есть и в А, ив В') ; Print (к, С) ; ReadLn;

End. Начнем уточнять наше решение, а именно, процедуру Solve. Procedure Solve(qx,qy:Integer;Var qz:Integer; X,Y:MyArray; Var Z: MyArray);

Var i , j : Integers-Begin

qz:=0; For i:=1 To qx Do

For j:=l To qy Do I f X[i]=Y[j] Then Begin Inc(qz);Z[qz]:=X[i];(j:=qy;}End;

End;

Page 124: Pascal 8 Класс - Окулов

124 Часть вторал

После запуска программы окажется, что при некоторых ис-ходных данных результат неправильный. Например, в первом массиве 10 присутствует один раз, во втором — пять раз. Со-гласно формулировке задачи в ответе 10 должна присутство-вать один раз, а она выводится пять раз. Уберем фигурные скобки у оператора j:=qy. Мы «насильно» изменяем управляю-щую переменную цикла For j:=l ... Выводится правильный ре-зультат. Однако этот прием «насильственного», т. е. не самим оператором For, изменения управляющей переменной считаем плохим программированием.

Procedure Solve(qx,qy:Integer;Var qz:Integer; X,Y: My Array ; Va r Z: My Array) ;

Var i,j:Integer; Begin

qz:=0 ; For i:=l To qx Do Begin j:=l; While (J<=Q1) And (X [ i ] <>Y [ j ] ) Do I n c ( j ) ; I f J<=qv Then Begin Inc (qz) ; Z (qz ] : =X [ l ] ;End;

End; End;

Продолжите эксперименты с программой. Определите слу-чаи ее неработоспособности.

5. Дан массив А из п элементов и число от (1<т<п) . Не исполь-зуя дополнительных массивов, переставить первые m элемен-тов в конец массива, а элементы с т + 1 по п — в начало, со-храняя порядок элементов. Идея решения. Переворачиваем первые т элементов, переворачиваем элементы, начиная с 771+1. Переворачиваем все элементы массива. Пример. 71=10, 771=6., массив А: 5 1 - 4 3 7 2 - 1 9 8 6 .

2 7 3 - 4 1 5 - 1 9 8 6 первое переворачивание т элементов.

2 7 3 - 4 1 5 6 8 9 - 1 второе переворачивание элементов с т+1 по п.

- 1 9 8 6 5 1 - 4 3 7 2 третье переворачивание элементов с 1 ПО 71.

В тексте решения не приводится реализация процедур Init и Print. Для процедуры Rev рекомендуется выполнить «ручную» трассировку при различных значениях t и I.

($R+) Program Myll_5;

Page 125: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 125

Const п=10;т=6 ; Type MyArray—Array[1..п] Of Integer; Var A:MyArray; Procedure Imt (Var X:MyArray) ;

Procedure Print(X:MyArray); Procedure SwapfVar a,b: Integer) ;( *Из первого задания. *) Procedure Rev(t, 1:Integer;Var X:MyArray);

Var l:Integer; Begin

For l: = t To t+(l-t) Div 2 Do Swap <X[i],X[l-i + t ] ) ;

End; Begin

Imt I A) ; Print (A) ; Rev (1 ,m,A) ; Rev (m+1, n ,A) ; Rev (l,n,A) ; Print (A) ; ReadLn;

End.

Задания для самостоятельной работы

1. Д а н ы два различных выражения типа: у - а ^ х ' Ч - а ^ *хп+...+ +а 2 *х 2 +а 1 *х 1 +а 0 , z=bn

J rxn+bn_1*xn+...+b2 '<x2+b1, ! ;x1+b0 , где все

данные — целые числа. Коэффициенты выражений хранят-ся в массивах А и В. При заданном значении х найти макси-мальное значение (у,г).

2. Даны два одномерных массива из целых чисел. Найти эле-менты, которые есть в первом массиве и которых нет во вто-ром массиве.

3. Д а н ы два одномерных массива из целых чисел. Найти эле-менты, которых нет одновременно и в том и в другом масси-вах.

4. Р е ш и т ь задачу 4 из раздела экспериментальной работы для трех массивов.

5. Даны три одномерных массива из целых чисел. Найти эле-менты, которые есть в первом массиве и которых нет во вто-ром и третьем массивах.

6. Р е ш и т ь задачу 3 для трех одномерных массивов. 7. Д а н ы два одномерных массива из целых чисел, разной раз-

мерности. Найти среднее арифметическое элементов каждо-го массива и их сумму. Решить задачу для трех массивов.

Page 126: Pascal 8 Класс - Окулов

126 Часть вторал

8. Даны три одномерных массива из целых чисел одинаковой размерности. Сформировать четвертый массив, каждый эле-мент которого равен максимальному из соответствующих эле-ментов первых трех массивов.

9. Даны два одномерных массива (А, В) одинаковой размерно-сти п и число т . Поменять местами первые элементы масси-вов и выполнить перестановку элементов массивов в обратном порядке (задание № 5 раздела экспериментальной работы).

10. Дано четное число и; проверить для этого числа гипотезу Кри-стиана Гольдбаха (1742 год). Эта гипотеза (по сегодняшний день не опровергнутая и полностью не доказанная) заключа-ется в том, что каждое четное п, большее двух, представля-ется в виде суммы двух простых чисел. Ограничим диапазон проверяемых чисел интервалом 999>п>2. Оформить проце-дурой логику распознания простых чисел. Примеры: 6=3+3, 12=5+7, 30=7+23, 308=31+277, 992=73+919. Исследовать ги-потезу К. Гольдбаха для больших значений п.

11. Известны следующие признаки делимости числа п: • для делимости на 2 необходимо, чтобы последняя цифра

числа делилась на 2; • для делимости на 3 требуется, чтобы сумма цифр числа

делилась на 3; О для делимости на 4 необходимо, чтобы число из послед-

них двух цифр делилось на 4; • для делимости на 5 необходимо, чтобы последняя цифра

числа была 0 или 5; • для делимости на 8 необходимо, чтобы число из 4 по-

следних цифр делилось на 8; • для делимости на 9 необходимо, чтобы сумма цифр числа

делилась на 9; • для делимости на 11 необходимо, чтобы разность между

суммой цифр, стоящих на четных местах, и суммой цифр, стоящих на нечетных местах, делилась на 11.

Написать процедуры проверки признаков делимости. Прове-рить их для различных значений п.

Материал для чтения 1. Некоторые факты из элементарной теории вероятно-

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

Page 127: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 127

вверх; при бросании игральной кости — какая из шести сторон окажется вверху; при вытаскивании карты из игральной коло-ды — какая карта будет вытянута и т. д. Главное, что присуще всем этим ситуациям то, что исходы равноправны. Выпадет «орел» или «решка»; выпадет сторона с числом от 1 до 6; вытя-нута одна из 52 карт. Как оценивать исходы, какую меру взять для оценки того, что произойдет то или иное событие. Напри-мер, выпадет четное число при бросании игральной кости. Об-щее количество исходов 6, число благоприятных исходов 3. Вытянута карта пиковой масти, общее количество исходов 52, благоприятных — 13. Понятие меры в теории вероятности вво-дится следующим образом. Определяется множество элемен-тарных событий S, его элементы считаются равноправными, равновероятными. Каждое другое событие А — подмножество S, оно состоит из элементарных событий. Отношение IА | / | S I, где через I I обозначено количество элементов в множестве, на-зывается вероятностью события А и обозначается Р(А). Вероят-ность любого события А заключена между нулем и единицей: 0<Р(А)<1.

Примеры: • При бросании двух игральных костей вероятность получе-

ния 10 и более очков равна 4 /36=1/9 , так как всего имеет-ся 36 равновероятных исходов и четыре из них благопри-ятны — (5,5), (5,6), (6,5) и (6,6).

• Вероятность того, что на первой кости очков выпадет ме-ньше, чем на второй, равна 15/36.

О Стрелок попадает в цель в среднем 92 раза из 100 выстре-лов. Вероятность попадания равна 92/100 (0,92).

• На каждую 1000 готовых деталей некоторого предприя-тия приходится в среднем 16 бракованных. Вероятность изготовления брака для данного производства равна 0,016.

• Первый стрелок попадает в цель с вероятностью 0,8, вто-рой — 0,7. Найти вероятность поражения цели, если оба стрелка стреляют одновременно. Цель считается поражен-ной при попадании в нее хотя бы одной из двух пуль. Пусть производится 100 двойных выстрелов. Примерно в 80 из них цель будет поражена первым стрелком, а в 20 случаях он промахнется. Второй стрелок из 10 выстрелов примерно 7 раз поражает цель, в 20 выстрелах — 14 раз. Таким образом, при 100 выстрелах цель окажется по-раженной примерно 94 раза. Вероятность поражения — 0,94.

Page 128: Pascal 8 Класс - Окулов

128 Часть вторал

Событие, вероятность которого равна 1, называется досто-верным, — оно наступает всегда. Монета опустится «гербом» или «решкой», считаем, что на ребро она встать не может. Со-бытие, вероятность которого равна 0, называется невозмож-ным, — оно не наступает никогда. В нашем примере бросания игральной кости (и других) есть то, что называют элементар-ным событием — выпадение стороны кости с определенным числом. Остальные события составляются из элементарных со-бытий, например выпадение стороны кости с четным числом. События А и В называют несовместимыми, если появление од-ного из них исключает появление другого. При бросании моне-ты «герб» и «решка» ие могут выпасть одновременно. Если А и В — несовместимые события, то Р(А+В)=Р(А)+Р(В). Утвержде-ние обобщается на п несовместимых событий. Говорят, что не-сколько событий Aj , А2 , ..., А^ образуют полную группу, если вероятность того, что произойдет одно из них, является досто-верным событием.

Примеры.: • Опыт — бросание двух монет. События «два герба», «две

решки» и «один герб, одна решка» образуют полную груп-пу и являются несовместимыми.

• Опыт — бросание игральной кости. События «1 или 2 очка», «2 или 3 очка», «3 или 4 очка», «4 или 5 очков» и «5 или 6 очков» образуют полную группу, но не являются несовместимыми.

• Опыт — вынимание одной карты из колоды в 36 карт. Со-бытия— вынимание «туза», «короля», «дамы», «валета», «десятки», «девятки», «восьмерки», «семерки» и «шес-терки» образуют полную группу и являются несовмести-мыми.

• Опыт — передача в одинаковых условиях трех сообщений равной длины. События «искажено первое сообщение», «искажено второе сообщение» и «искажено третье сооб-щение» не образуют полную группу и не являются несо-вместимыми.

2. Информация. Итак, об информатике говорят как о науке по работе с информацией с помощью ЭВМ. Мы называем ЭВМ универсальной машиной по обработке информации. Обсудим кратко термин информация, понимая при этом, что строгого определения нам не дать, ибо понятие информации является первичным, неопределяемым. Слово информация используется в двух значениях — качественном и количественном. С одной

Page 129: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 129

стороны, мы понимаем под информацией конкретные сведения о чем-либо, представленные в виде речи, текста, изображения, цифровых данных, графиков, таблиц и т. п. С другой сторо-ны — её численную меру, т. е. выраженное в битах количество абстрактной информации. Остановимся на втором аспекте, ибо обсуждение первого заведет нас в дебри словесной эквилибри-стики. Одним из параметров измерения информации является ее объём: количество бит, байт и т. д. Есть последовательность из единиц и нулей. Длину этой последовательности в одних книгах называют количеством информации, в других — объе-мом информации. Другой способ оценки количества информации основан на вероятностном подходе. Вводится мера неопределен-ности, мера нашего незнания чего-либо. Устранение неопределен-ности достигается за счет получения информации. Если есть «лапоть» для измерения неопределенности, то он может быть использован для наших целей — определения количества ин-формации. Приведем традиционное рассмотрение этой схемы на примере игры «Бар — Кохба».

Игра «Бар — Кохба». Ее суть заключается в следующем. Один из игроков что-то загадывает, а второй должен отгадать загаданное, задавая вопросы, которые предполагали бы только ответы типа «да», «нет». Предположим, что в классе 16 учени-ков и учитель загадал номер по журналу одного из учеников, они по какой-то причине пронумерованы числами от 0 до 15. В начальный момент времени у нас полное незнание того, какой номер загадан.

Первая серия наших вопросов. 1-й вопрос. Находится ли загаданный номер в интервале от 8 до

15? Ответ — д а (1). 2-й вопрос. Находится ли загаданный номер в интервале от 12

до 15? Ответ — нет (0). 3-й вопрос. Находится ли загаданный номер в интервале от 11

до 12? Ответ — да (1). 4-й вопрос. Загадан номер 11? Ответ — нет (0).

Вторая схема формулировок вопросов. 1-й вопрос. Находится ли загаданный номер в интервале от 0 до

3? Ответ — нет. 2-й вопрос. Находится ли загаданный номер в интервале от 12

до 15? Ответ — нет.

5 - 4 5 2

Page 130: Pascal 8 Класс - Окулов

3-й вопрос. Находится ли загаданный номер в интервале от 4 до 7? Ответ — нет.

4-й вопрос. Находится ли загаданный номер в интервале от 8 до 11? Ответ — да.

5-й вопрос. Находится ли загаданный номер в интервале от 10 до 11? Ответ — да.

6-й вопрос. Загадан номер 11? Ответ — нет.

Чем отличаются серии вопросов? Мера нашего незнания в том и в другом случаях одинакова, т. е. получено одно и то же количество информации. Примем за единицу измерения ин-формации (1 бит) количество информации, содержащееся в от-вете при условии, что ответы равновероятны. В первом случае каждый ответ содержит один бит информации, ибо ответы «да» и «нет» были равновероятны. Во втором случае — нет, в сред-нем один ответ содержал 4 /6 бита информации. Всего получено 4 бита информации. Неопределенность полностью устранена, мы знаем загаданный номер. Пусть есть N равновероятных со-бытий и выделено одно из событий (f). Для того, чтобы опреде-лить t, необходимо получить количество информации I, равное LogyV. Эта формула для вычисления количества информации предложена американским инженером Р. Хартли в 1928 году. Если события не равновероятные, то количество информации вычисляется по формуле американского математика К. Шенно-на: I=-(pJLog2p1 +p2Log2p2+...+pNLog2pN), где р, — вероятности событий. Из формулы К. Шеннона получается формула Р. Хар-тли при p=l/N для всех значений i. Действительно, 1=-(1/ N*Log2(l/N)+ ...+l/N*Log2(l/N))=Log2N.

В скобках первой серии вопросов указаны цифры 0 или 1. Если выписать ответы в виде двоичного числа 10102 и перевес-ти его в десятичную систему счисления, то получим число 10 — номер загаданного ученика! Итак, каждая двоичная циф-ра «несет» в себе один бит информации, отсюда и название еди-ницы измерения — БИТ (binary digit — двоичная цифра). В игре «Бар — Кохба» вопросы задаются последовательно, на основании ответов учителя. А можно ли задать сразу четыре вопроса, а затем получить четыре ответа учителя и определить номер загаданного ученика. Оказывается, да. Вопросы форму-лируются так: «Вы загадали номер, двоичная запись которого содержит единицу в первом разряде». Обратите внимание на то, что и в этом случае ответы «да» и «нет» равновероятны.

Page 131: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 131

Предположим, что учитель одновременно загадал номера двух учеников (tt и t2) в разных классах. Количество учеников в классах — Nt и N2. Необходимо отгадать пару (xt,x2), принад-лежащую множеству всех пар — Согласно формуле Хартли необходимо задать Log2(N +N2) вопросов, или полу-чить Log2(N1*N2) бит информации. С другой стороны, номера учеников можно отгадывать независимо, для отгадывания t t — LoggNj вопросов, для отгадывания второго — LogzN2 вопросов. Следовательно, общее число вопросов равно Log^N 1+Log2N2. Итак, мы получили Log2(N 2)= Log^j+Log^2, что совпада-ет с хорошо известным свойством логарифмической функции. Это закон аддитивности информации.

Примеры. Имеется 27 монет, 26 настоящих и одна фальши-вая, она легче настоящих. Сколько взвешиваний необходимо произвести, чтобы определить фальшивую монету?

Фальшивой может оказаться любая из 27 монет, следовате-льно, по формуле Хартли количество недостающей информа-ции равно Log227 битов. Любое взвешивание имеет три исхода и может дать нам только Log,3 битов информации. Если мы производим X взвешиваний, то они дадут X*Log23 битов ин-формации. Итак, X*Log23>Log227=Log233=3-*Log23. Следовате-льно, Х>3. На самом деле достаточно ровно 3 взвешивания: первое по 9 монет, второе по 3 монеты из найденной группы и, наконец, по одной монете из найденной группы по 3 монеты.

Чтобы найти элемент множества, состоящего из 7 элемен-тов, необходимо задать три вопроса, а чтобы найти элемент множества, состоящего из 9 элементов, — 4 вопроса, т. е. по формуле Хартли получить Log27+Log29 битов информации. Но если необходимо отгадать пару элементов из этих множеств, то требуется получить Log2( 7*9 ) битов информации, а это меньше 6 вопросов. Противоречия нет — Log2(7*9) = Log27+Log29= =5,97728<6. Как понимать формулу Хартли, если Log2N не яв-ляется целым числом? Предположим, что мы выполняем отга-дывание не один, a k раз, т. е. последовательность из k неизве-стных элементов — (хгх2, ...,хк). Таких последовательностей Nk. Очевидно, что для числа вопросов (sk) в этом случае выполня-ются следующие неравенства:

Log2Nk<sk<Log2Nk+l или Log2N<sk/k<Log2N+l/k. Число sjk показывает, сколько вопросов в среднем необхо-

димо для того, чтобы отгадать один элемент множества, содер-жащего N элементов. Выбирая значение k достаточно большим, величину 1/k можно сделать сколь угодно малой. Итак, в том

Page 132: Pascal 8 Класс - Окулов

132 Часть вторая

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

Page 133: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 133

З а н я т и е № 12. Ф у н к ц и и

План занятия • описание функции; • несколько фактов из комбинаторики; • экспериментальная работа с программами вычисления чис-

ла сочетаний из n по т ; получения палиндрома из числа путем последовательного его «перевертывания» и сложе-ний; нахождений общей части п отрезков;

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

процедура. Почти единственное отличие в том, что заголовок функции начинается с ключевого слова Function и кончается типом возвращаемого данной функцией значения.

Function <имя функции> [(СПИСОК параметров)]: <тип результата>;

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

Несколько фактов из комбинаторики. Перестановкой из п элементов назовём упорядоченный набор из п различных нату-ральных чисел, принадлежащих интервалу от 1 до п. Пусть п равно 3. Перечислим все перестановки из 3 элементов: (1 2 3), (1 3 2), (2 1 3), (2 3 1), (3 1 2), (3 2 1). Количество перестановок из п элементов (обозначается как Рп) равно п'=1*2*...*п (факто-риалу числа п). Действительно, есть п способов для выбора эле-мента на первое место, ( п - 1 ) способ для выбора элемента на второе место и т. д. Общее количество способов — это произве-дение п*(п-1)*(п-2)*..*2*1.

Перечислите все перестановки из 4 элементов. Количество перестановок — 24. Подсчитайте вручную значения п\ для п из диапазона от 5 до 12.

Размещения с повторениями. Элементы относятся к п раз-личным видам. Из них составляются всевозможные расстанов-ки по m элементов в каждой. При этом в расстановки могут входить и элементы одного вида. Две расстановки считаются различными, если они отличаются друг от друга или входящи-ми в них элементами, или порядком элементов. Пусть п равно 3, а т — 2 (как обычно, рассматриваем натуральные числа). Размещения с повторениями: (1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3). Число таких расстановок R(n,m)=nm . Доказывается с помощью математической индукции.

Page 134: Pascal 8 Класс - Окулов

134 Часть вторал

Перечислите все размещения с повторениями при п=4 и т = 2 . Подсчитайте вручную значение R(n,m) для нескольких значений п и т .

Размещением без повторений из п элементов по т называет-ся упорядоченный набор из m различных чисел, принадлежа-щих интервалу от 1 до п. Два размещения считаются различ-ными, если они либо отличаются друг от друга хотя бы одним элементом, либо состоят из одних и тех же элементов, но распо-ложенных в разном порядке. Пусть п равно 4, a m — 2. Пере-числим все размещения: (1, 2), (2, 1), (1, 3), (3, 1), (1, 4), (4, 1), (2, 3), (3, 2), (2, 4), (4, 2), (3, 4), (4, 3). Количество размещений (A(n,m)) вычисляется по формуле: A(n,m)=n A (n- l )* . . .*(n-m+l) или A(n,m)=n!/(n-m)!

Перечислите все размещения из 5 элементов по 2. Подсчи-тайте вручную значения A(n,m) для фиксированного значения т , например 4, и п из интервала от 8 до 13.

Сочетанием из п элементов по m называется множество из m различных чисел, выбранных из диапазона от 1 до п. С поняти-ем мчожества мы еще не знакомы. Пусть множества составля-ются из натуральных чисел от 1 до 5. Наборы (1, 2, 5) и (2, 5, 1) — это одно и то же множество. Множества (1, 1, 2, 5) не су-ществует, элемент 1 повторяется два раза. Пусть п равно 5, а m — 3. Перечислим все сочетания: (1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5), (2, 4, 5), (3, 4, 5). Каждому сочетанию соответствует т ! размещений. Обозначим число сочетаний как C(n,m). Таким образом, A(n,m)=C(n,m)*m! или C(n,m)=A(n,m)/m!= n!/(m!*(n-m)!).

Перечислите все сочетания из 7 элементов по 5. Подсчитайте число сочетаний для различных значений п и т (не очень боль-ших). При подсчёте как числа сочетаний, так и числа размеще-ний, используется операция деления. Обоснуйте, почему в ре-зультате получаются только целые числа.

Экспериментальный раздел работы

1. Составить программу подсчета числа сочетаний — C(n,m). Пусть у нас есть функция подсчета факториала числа — Fact(n). Написание основной программы сводится к про-граммированию формулы C(n,m)= n!/(m!*(n-m)!). Обратите внимание на то, что вызывать функцию можно прямо при вычислении выражения, а не в отдельной строке програм-мы, как мы делали при вызове процедур.

Page 135: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 135

Program Му12_1; Var п,т: Integer; с: Long mt; Function Fact (n: Integer) : Longint; Begin End;

Begin WriteLn С Введите пит : ' ) ; ReadLn (n,m) ; c:= Fact (л) / (Fact (m) * Fact (n-m)) ; WriteLn(c); ReadLn;

End. В этот момент времени у Вас только то, что Вы видите. Тела

функции нет, но программа компилируется , и Вы ее сохранили на внешнем носителе. А компилируется ли? Да нет. Ошибка в строке вычисления с — Error 26: Type mismatch. — несоответ-ствие типов. Вспомним, типы данных определяют не только диапазон значений переменных, но и допустимые над этим ти-пом операции. Обычное деление «/» недопустимо для перемен-ных целого типа. Необходимо использовать операцию Div — с:= Fact(n) Div (Fact(m) * Fact(n-m));. Дополняем програм-му.

Function Fact(n:Integer):Longint; Var i: Integer;

rez: Longint; Begin

rez:=1; For l:=1 To n Do rez:=rez*i; Fact:=rez ;

End; Запускаем её для различных значений. При ге=10, т=5, ре-

зультат 252, пока не вызывает сомнений. При га=13, т = 5 , резу-льтат 399, что-то не так , должен быть больше. При п=15, т=7, результат 9, программа не работает. Объясните причину. Под-сказка содержится в материале этого занятия .

А можно л и упростить программу? Переменная с л и ш н я я . Попробуйте убрать её.

Сколько л и ш н и х вычислений (умножений) делает эта про-грамма? Подсчитаем С(13,5) = (1* 2* 3* 4* 5* 6* 7* 8* 9* 10* 11* 12* 13) / (1* 2* 3* 4* 5) * (1* 2* 3* 4* 5* 6* 7* 8). Вряд ли разумный человек будет выполнять все умножения. Он выпол-

Page 136: Pascal 8 Класс - Окулов

136 Часть вторал

нит: 9*11*13 и получит 1287. Почему ж е м ы заставляем компь-ютер быть г л у п ы м ? Н а п и ш и т е более э ф ф е к т и в н у ю ф у н к ц и ю в ы ч и с л е н и я ч и с л а сочетаний. Н а п р и м е р , д а ж е нижеприведен-н ы й вариант п р о г р а м м ы даст более о б н а д е ж и в а ю щ и е результа-ты . П р а в и л ь н ы й результат при С(13,5) , а именно , 1287 , у ж е шаг вперед, но это не предел у л у ч ш е н и й п р о г р а м м ы .

Program Му12_1т; Var п,т:Integer; Function S (п,т:Integer) :LongInt;

Var l:Integer; rez,cht:Longlnt;

Begin rez:=1;cht:=1; For i:=l To m Do Begin rez:=rez*i;cht:~cht*(n-i+1); end; S:=cht Div rez;

End; Begin

WriteLn ('Введите два числа: ' ) ; ReadLn (n,m) ; WriteLn (S (n,m) ) ; ReadLn;

End. Продолжим тему, хотя ф у н к ц и и при р е ш е н и и модифициро-

ванной задачи и не используются . На восьмом з а н я т и и м ы по-знакомились с основной теоремой а р и ф м е т и к и . Пусть нам не требуется вычислять С ( п , т ) , а необходимо представить это значение в виде p1

alp2a2...pq

aq, где а >0, a pt — простые числа . Ограничения 1<га<100, 1 < т < л . Вычислять , если так м о ж н о вы-разиться , «в лоб» — безнадежное занятие . Ч и с л а огромные, а пока м ы не умеем работать с ними . Рассмотрим идею р е ш е н и я на примере . Вычисляем С(8,5)=8!/(5!*3!). Представим 8! к а к единицы в соответствующем массиве, например с именем Sn, Sn[ i ]= l говорит о том, что число i есть в произведении. Просмат-риваем массив Sn, выбираем очередной элемент. Если число i со-ставное, то находим его разложение на простые сомножители. К элементам Sn, соответствующим сомножителям, прибавляем ко-личество единиц, равное числу их вхождений в i, a Sra[i] обну-ляем . Выполним эти действия д л я п.', т! и (п-т)!. После этого останется только провести вычитание степеней одного и того же простого числа .

Page 137: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 137

Итак, С(8,5)=2Э*7' . Program Му12_2; Const Q=100 ; Type MyArray-Array[1..Q] Of Integer; Var n,m:Integer;

P -.MyArray ; Procedure Soli e (sn, sm: Integer; Var Sp:MyArray) ; Begin Ends-Procedure Print (pn : Integer ; Pp: MyArray) ;

Var i: Integers-Begin For i:=2 To pn Do

I f Pp[i]0 Then WriteLn (l: 5, Pp [i] : 5) ; End;

Begin WriteLn('Ввод чисел n и m') ; ReadLn (n,m) ; Solve (n,m,P) ; Print (n,P) ; ReadLn;

End. Итак, есть только основная программа с вызовами проце-

дур. Процедура Print приведена в силу ясности её написания. Программа компилируется. Программируем так, как говорим: ввели данные; выполнили обработку; вывели результат. При-держивайтесь этого принципа написания. Язык программиро-вания для Вас должен стать естественным языком для выраже-ния мыслей, так же как , например, русский язык в общении. Уточнение логики.

Page 138: Pascal 8 Класс - Окулов

138 Часть вторал

Procedure Solve(sn,sm:Integer;'Var Sp:MyArray); Var i:Integer;

Sni,Smi,Snmi:MyArray; Begin

Calc(sn,Sni);{*Сформировали массив, соответствующий n1 *} Calc(sm,Smi);(* Сформировали массив, соответствующий m!*) Calc (sn-sm,Snmi);(*Сформировали массив, соответствующий (n-m)!*) For i:=2 To sn Do Sp[1]:=Sni[1]-Smi[1]-Snmi[1];

End; Сделав заголовок процедуры Calc с операторами Begin и

End, Вы получите опять к о м п и л и р у е м у ю программу . Вариант процедуры Calc, приведенный н и ж е , х о т я и работает, но не так хорошо читабелен, к а к п р е д ы д у щ а я логика . В ы п о л н и т е трасси-ровку этой процедуры в режиме отладки, определите назначение каждого оператора. Ваша задача улучшить процедуру. Вспомни-те о том, что в материале з а н я т и я № 8 перечислены все простые числа до 100.

Procedure Calc(п:Integer; Var X:MyArray); Var l,j,t:Integer;• Begin

FillChar(X,SizeOf ( X ) , 0) ;{* SizeOf - вычисляет размер области памяти, выделенной для массива X; FillChar - записывает 0 значения в зту область памяти, инициализация X. *) For i:=l То n Do X [i] :=1 ; { * признаки чисел, задействованных в вычислении факториала числа. *) For 1:=4 То n Do Begin

t: = i / j:=2; While (3<=(t Div 2)) Do

I f t Mod j =0 Then Begin {*j делит t без остатка. *}

I n c ( X [ j ] ) ; t:=t Div j ; I f t=j Then I n c ( X [ j ] ) ;

End Else I n c ( j ) ;

I f t o i Then Begin (*Найдены делители числа i . *) X[iJ:=0;If ( t o j ) Then Inc(X[t]);

End; End;

End;

Page 139: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 139

Вы решили исследовать задачу для большего диапазона зна-чений п. Пусть п<10000. Запуск программы принесет только разочарование, даже если изменить тип Integer на Longlnt. По-явится ошибка Error 202: Stack of erf low error. He занимаясь де-тальным выяснением ее сути, считаем, что просто не хватает оперативной памяти компьютера для хранения данных про-граммы. Действительно, массивов введено много. Достаточно одного массива Р при условии, что он объявляется как глобаль-ный и не используется при вызове процедур. Вариант програм-мы.

($R+) Program Му12_2т; Const Q=10000; Type MyArray=Array[1..Q] Of Integer; Var n,m:Longlnt;

P: MyArray; Procedure lnc_0 (n : Longlnt) ;

Var i , j , t:Integer; Begin For l:=2 To n Do Begin

t - i ; j:=2; While (j<=(t Div 2)) Do

I f t Mod j =0 Then Begin Inc (P[j ]); t: =t Div j; I f t=j Then Inc (P[j]) ; End Else Inc(j) ; I f t o i Then Begin I f ( t o j ) Then Inc(P[ t]) ; End Else P[i) :=1;

End; Ends-Procedure Dec_0 (n: Longlnt) ; Var i ,j ,t: Integer ; Begin

For l:=2 To n Do Begin t:=i; j:=2;

While (j<= (t Div 2)) Do If t Mod j=0 Then Begin Dec (P[j ]) ; t : =t Div j; I f t=j Then Dec (P[j ] ) ;End Else Inc(j) ;

I f t o i Then Begin I f ( t o j ) Then Dec(P[t]); End Else Dec (P[i]);

End; End;

Page 140: Pascal 8 Класс - Окулов

140 Часть вторал

Begin WriteLn ('Ввод чисел п и т'); ReadLn (п, т) ; 1пс_0(п) ; Dec_0 (т) ; Dec_0 (п-т) ;

End. Программа работает, но она опять не л у ч ш а я (вывод резуль-

тата не приводится) . Код процедур 1пс_0 и Dec O почти повто-ряется . Это плохо. Избавьтесь от этого недостатка , а может быть, В а м и у ж е сделан более достойный вариант обработки. В этой программе значение константы Q м о ж н о увеличить , на-пример до 30000 . Возникает другая проблема — время работы программы, приходится ждать результат .

2. Задание 11 з а н я т и я № 7 требовало определить, я в л я е т с я ли введенное число палиндромом.

Оформим «перевертывание» числа в виде ф у н к ц и и .

Function Pal (п -.Longlnt) :Longlnt; Var x:Longlnt; Begin

x:=0; While n<>0 Do Begin x:=x*10+n Mod 10; n : = n Div 10; End; Pa 1: =x ;

End; Пусть исходное число введено, например 59. Оно не палинд-

ром. «Перевернем» его, получим 95. Найдем сумму чисел 59 и 95 — 154. «Перевернем» это число — 451. Находим сумму — 605. Еще раз: 506 и 1111. Получили палиндром. Н а й т и для всех натуральных чисел из интервала от 50 до 80 количество шагов, необходимых д л я сведения их к палиндромам, с помо-щ ь ю описанной схемы.

Program Му12_3; Const а=50;Ъ=80; Var п, t .-Longlnt;

cnt,i:Integer; Function Pal (n.-Longln t ) : Longlnt; Begin

End;

Page 141: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 141

Begin For i:=a То Ь Do Begin cnt:=0;п:=1; While Pal (n) on Do Begin

t:=Pal m); n:=n+t; Write f t , ' ' ,n,' ' ) ; Inc (cnt);

End; WriteLn (i,' ',cnt) ; End;

End. Измените г р а н и ц ы интервала a, b на 89 и 89. Число 89, так

же к а к и 98, очень неприятно . На 16-м шаге результат по-прежнему не я в л я е т с я палиндромом, а сумма выходит за пре-делы типа Longlnt. И з м е н и м программу, используя четвертый пример из раздела экспериментальной работы десятого заня-т и я . Повторите материал о работе с большими числами. В этой задаче нам потребуются у м е н и я их складывать и «переверты-вать».

($R+) Program Му12_3т;

Uses Crt; Const MaxN=lООО; Type MyArray=Array[0..MaxN+1] Of Integer;{*Числа храним в массиве, они большие и типа Longlnt не хватает. *} Var A,Z:MyArray; Var cnt,n:Integer; Function Eq:Boolean;(*Является ли число палиндромом ?*}

Var i:Integer; Begin

i : =1 ; While (l <=A [0] Div 2) And (A [l} =A [A [ 0]-l+l ]) Do Inc (i) ; Eq:=(i>A[0] Div 2) ;

End; Procedure Swap(Var a,b:Integer);

Var с-.Integer; Begin c:=a;a:=b;b:=c; End;

Procedure Rev;{* «Перевертываем» число.*} Var i:Integer; Begin

Page 142: Pascal 8 Класс - Окулов

142 Часть вторал

F o r i:=l То А[0] Div 2 Do Swap (A[i] ,A[A[0]-i+l]) ;

End; Procedure Change(n:Integer);{*Переводим число в его представление в виде массива. *}

Var 1:Integer; Begin

i : = 1 / While n<>0 Do Begin A [ l ] :=n Mod 10; n:=n Div 10; Inc (A[0J ) ;Inc ( l ) ;

End; End;

Procedure Add;(*Складываем два больших числа. *} Var i,г,w:Integer; Begin

l : = 1 ; r : = 0 ; While (l<-A[0]) Or (r<>0) Do Begin

w:=A[i]+Z[i]+r; A[i] :=w Mod 10; r : = w Div 10; I f A[A[0] +1] <>0 Then Inc(A[0]); Inc ( l ) ;

End; End;

Begin ClrScr; n:=100; While (n<1000) Do Begin ( *Исследуем диапазон от 100 до 999. *}

FillChar (A, SizeOf (А) , 0) ; Change (n) ; {*Преобразуем число. *) cnt:=0;(*Счетчик количества шагов -преобразований числа. *} While (Not Eg) And (A[0] <=MaxN) Do Begin f *Пока не палиндром и нет выхода за пределы массива А считаем. *}

Z: =А;Inc (cnt);Rev;Add;(* Вызов процедур, они описаны и очевидны.*)

End; WriteLn (n, ' ' , cnt) ; ( *Вывод числа и количества шагов. *) I f cnt>=MaxN Then ReadLn;(*Фиксируем факт выхода за пределы массива А. *) Inc(n) ;

End; End.

Page 143: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 142

Д л я чисел 196, 295, 394, 493, 592, 689, 691, 788, 790, 879, 887 , 978 , 986 массива А из 1000 элементов недостаточно для х р а н е н и я результата . А конечен ли процесс, всегда ли можно получить палиндром? Д л я доказательства или опровержения этого ф а к т а требуются математические усилия . Н а ш и м и сред-ствами установить его вряд ли возможно, они могут только «натолкнуть» на схему доказательства . Однако рекомендуем Вам п р о д о л ж и т ь эксперименты с программой, с этой целью массивы в ней определены к а к глобальные переменные и не пе-редаются в процедуры и ф у н к ц и и . Может оказаться , что опера-тивной п а м я т и , выделяемой для задачи на Турбо Паскале , не будет хватать при этом исследовании.

3. К а к находить наибольший общий делитель двух чисел, мы знаем из восьмого занятия . Оформим эти действия в виде фун-кции .

Function Nod(a,b:LongInt):Longlnt; Begin Repeat

I f a>b Then a:=a Mod b Else b:=b Mod a; Until (a=0) Or (b=0) ; Nod:=a+b;

End; Рассмотрим следующую задачу. Дано п отрезков, длины ко-

торых целые числа . Берем два отрезка и из большего вычитаем меньший . Разность — новый отрезок. Если ж е длины отрезков совпадают, то один из н и х исключаем из дальнейшего рассмот-рения . Продолжаем процесс. Ответом задачи является одно число, равное длине оставшегося в результате этих действий отрезка . Пример: п=4 и длины 6, 15, 3, 9.

Ответ задачи 3. Решение задачи «в лоб» по приведенной схеме возможно, хотя оно достаточно трудоемко и для больших

Page 144: Pascal 8 Класс - Окулов

144 Часть вторал

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

Задания для самостоятельной работы

1. Дано п целых чисел. Найти среди них число, у которого при-веденная н и ж е характеристика имеет максимальное значе-ние: • сумма цифр; • первая цифра; • количество делителей; • сумма всех делителей.

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

ние; • наименьшее общее кратное имеет наименьшее значение; • есть ли среди заданных чисел «близнецы», т. е. простые

числа, разность между которыми равна двум. 3. Дано п целых чисел. Написать программу подсчета количе-

ства чисел, в записи которых нет цифры 8. При этом требу-ется использовать функцию вида: Function Yes8(x:LongInt): Boolean. Без использования компьютера решите эту задачу для всех трехзначных чисел. Ответ — 729.

4. Даны два числа пи k (k< п). При этом п означает основание системы счисления, a k — количество знаков в записи чис-ла . Написать программу подсчета количества натуральных чисел в л-ичной системе счисления, записываемых ровно k знаками. Без использования компьютера решите эту задачу для всех трехзначных чисел. Ответ — 900.

5. Объявлен конкурс на получение грантов. В конкурсе прини-мают участие п человек. Гранты выдаются первым пяти уча-стникам, набравшим наибольшее количество баллов. Один участник не может одновременно получить два и более гран-тов. Написать программу определения количества способов, которыми могут быть распределены гранты. Исследовать её область применимости.

Page 145: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 145

6. Дана шахматная доска размером п*п и п ладей. Определить количество способов расположения ладей на шахматной до-ске так, чтобы они не могли бить друг друга. Исследовать, для каких значений п работоспособны типы данных Integer, Word, Longlnt.

7. Дано п различных бусин. Сколько различных ожерелий мож-но составить из них? Исследовать область применимости Ва-шей программы.

При п=7 число ожерелий равно (5040'7):2=360. Делением на двойку учитывается не только поворот (деление на 7), но и пере-ворот ожерелий.

8. Имеются предметы k различных типов. Количество предме-тов первого типа л, , второго — п2, ..., k-ro — пк. Написать про-грамму подсчета числа перестановок, которые можно сделать из этих предметов, и исследовать ее область применимости. Предварительно попытайтесь обосновать формулу: Р,=л ' / (п,1 *п„ ' * *Пу 1 ) . Есть текст. Его шифруют (кодируют) с помощью перестановки букв. В результате получается новый текст, называемый ана-граммой (слова лунка и кулан — анаграммы). Для механиче-ской расшифровки анаграммы требуется выполнить количе-ство перестановок, вычисляемых по выше — приведенной формуле, где nt — количество конкретных букв в анаграмме. Предположим, что у Вас есть компьютер с миллиардным быст-родействием (1 миллиард операций в секунду). Он выполняет миллиард умножений в секунду. Задайте значения величинам п, п}, п2,.... пк и подсчитайте, какое время Ваш компьютер бу-дет решать задачу.

9. Черепашка перемещается по кле- |Г" точному полю размером n*m. Ей разрешено двигаться вправо и вверх. В начальный момент вре-мени она находится в нижней, ле-вой клетке (А), и ей необходимо попасть в верхнюю, правую клет- 1— ку (В). Сколько различных путей L есть у Черепашки. Подсчитать ко-личество путей для различных значений п и т . Выяснить, для к а к и х значений п и т работоспособны типы данных Integer, Word, Longlnt, т. е. для хранения результата можно исполь-

Page 146: Pascal 8 Класс - Окулов

146 Часть вторал

зовать переменные этих типов. Предположим, что для ана-лиза одного пути Черепашки компьютеру с миллиардным бы-стродействием требуется 100 операций. За какое время он проанализирует все пути для конкретных значений п и т ?

10. Последовательности длины п составляются из t нулей и k еди-ниц, причем таким образом, что никакие две единицы не на-ходятся рядом. Подсчитать количество таких последователь-ностей (ответ — С(t+l,k)). Найти способ перечисления всех таких последовательностей для небольших значений t и k. Пример: t=3, k=2. Последовательности: 00101, 01001, 01010, 10001,10010,10100.

Материал для чтения

1. Историческая справка о термине «алгоритм». Термин «ал-горитм» обязан своим происхождением выдающемуся ученому средневековья Мухамеду ибн Муса ал Хорезми ( в переводе с арабского означает «Мухамед сын Мусы из Хорезма»), сокра-щенно Ал-Хорезми. Он жил приблизительно с 783 по 850 гг. В одном из своих трудов Ал-Хорезми описал десятичную систему счисления и впервые сформулировал правила выполнения ариф-метических действий над целыми числами и простыми дробя-ми. В латинском переводе труда Ал-Хорезми правила начина-лись словами Dixit Algorizmi (Алгоризми сказал). В других переводах автор именовался Algori thmus (Алгоритмус). Посте-пенно люди забыли, что Алгоризми — это автор правил, и ста-ли сами эти правила называть алгоритмами.

2. В настоящее время слово «алгоритм» стало научным тер-мином, обозначающим одно из фундаментальных понятий со-временной математики и информатики. Мы уже использовали этот термин, не обсуждая его, но не судите за это строго. Тер-мин веками использовался в математике без точного определе-ния, и для этого есть причины. Вспомним алгоритм Евклида нахождения наибольшего общего делителя двух натуральных чисел. Независимо от того, кем (чем) он выполняется, наиболь-ший общий делитель будет найден, ибо все действия однознач-но определены и требуется только записать их на понятном для исполнителя языке. Мы записываем их на языке Турбо Пас-каль. По этому алгоритму мы решаем задачу для всех целых чисел, подчеркнем еще раз — для всех, т. е. у этого алгоритма то, что называют массовостью алгоритма и его конечностью, ибо результат будет найден за конечное число действий. Однако

Page 147: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 147

вспомним задачу о преобразовании числа в палиндром. Алго-ритм есть, но для некоторых целых чисел результат мы не по-лучили. Пусть даже мы доказали средствами математики, что для этих чисел процесс бесконечный. Но то, что мы написали, является алгоритмом или нет? Или «гипотеза Сиракуз», приве-денная в седьмом занятии — является она алгоритмом или нет?

В школьной учебной литературе приводятся различные не-формальные определения понятия алгоритм и обсуждаются его свойства: определенности, понятности, массовости, диск-ретности, конечности. Например, «под алгоритмом понимается предписание, точное и понятное, определяющее, к а к и е дейст-вия и в к а к о м порядке необходимо выполнить для решения лю-бой задачи из данного класса однотипных задач». Позволим себе не вдаваться в обсуждение определений и свойств, ибо ре-зультат вряд ли окупит затраты. Отметим только, что опреде-ление А. Г. Кушниренко и Г. В. Лебедева «Алгоритм — план будущей деятельности, записанный в заранее выбранной фор-мальной системе. Составляет алгоритм человек, а выполняет ЭВМ» нам кажется наиболее удачным и больше других соответ-ствует нашему пониманию этого термина. Формальной систе-мой в нашем случае является система программирования Турбо Паскаль. Почему формальная? Вспомните, все ошибки устра-ненные в рассмотренных программах.

Остановимся коротко на способах записи алгоритмов. Их много, м ы будем использовать только одну формальную сис-тему — я з ы к программирования Турбо Паскаль . Различные приемы словесного описания, всевозможные диаграммы, блок-схемы не используются в данном учебнике. Причина проста. Поясним ее примером. Стандарты на документацию по про-граммным проектам 70-х годов двадцатого столетия требова-ли кроме текста самой программы оформления блок-схем. Автор помнит огромные тома этих блок-схем и ту титаниче-скую работу, которая стояла за этими томами. А затем про-граммные проекты ж и л и «своей жизнью», и эти тома ж и л и тоже своей ж и з н ь ю — они покрывались пылью в хранили-щах . Отследить все изменения программ в этих блок-схемах было не под силу любому коллективу, и через несколько лет работы с программным проектом эти тома уже ничему не со-ответствовали. Приходилось выпускать новый комплект блок-схем (они назывались документацией, блок -схемы требова-лось еще описать) для хранилища . Программирование благо-получно прошло эту стадию развития . Б л о к - с х е м ы бывают

Page 148: Pascal 8 Класс - Окулов

148 Часть вторая

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

Page 149: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 149

З а н я т и е № 13. Р е к у р с и я

План занятия • понятие рекурсии; О короткие примеры для иллюстрации рекурсии; • экспериментальная работа с программами перечисления

всех последовательностей длины л из чисел от 1 до k\ ге-нерации перестановок чисел от 1 до л; перечисления всех разбиений числа п на сумму слагаемых п=а1+а2+ ... +ак, где k, а3, а2 ак>0;

• выполнение самостоятельной работы. Понятие рекурсии. Рекурсией называется ситуация, когда

процедура или функция вызывает сама себя (образно выража-ясь, «этот глист страдал глистами, что мучились глистами сами»). Типичная конструкция рекурсивной процедуры имеет вид:

Procedure Rec (t: Integer) ; Begin

<действия на входе в рекурсию>; I f <проверка услович> Then Rec (t+1) ; <действия на выходе из рекурсии>;

End; Необходимо осознать два ключевых момента при написании

рекурсивной логики: ход вычислительного процесса по управ-лению и связи в вычислительном процессе по данным. Проил-люстрируем рисунком для конкретной задачи вычисления фак-ториала числа.

Function Factorial ( n: Integer): Longint; Begin I f n=l Then Factorial :=1 Else Factorial: =n * Factorlal (n-1) ; End; Найдем 51. Как же вычисляется факториал числа? Первый

вызов функции осуществляется из основной программы, на-пример, a:=Factonal(5), переменной а присваиваем значение 51. Действий на входе в рекурсию нет, этап вхождения выделен на рисунке жирными линиями. Он продолжается до тех пор, пока значение локальной переменной не становится равным 1. По-сле этого начинается выход из рекурсии (тонкие линии на ри-сунке). Итак, ход вычислительного процесса по управлению яв-ляется нелинейным! В рекурсивной логике обязательно должно быть условие завершения процесса вхождения в рекурсию (на

Page 150: Pascal 8 Класс - Окулов

150 Часть вторал

жаргоне — «заглушка»). Естественный вопрос — что необходи-мо знать для реализации этого процесса. С входом в рекурсию более понятно осуществляется вызов процедуры или функции, а для реализации выхода из рекурсии требуется помнить, отку-да мы пришли, т. е. помнить точки возврата в вызывающую ло-гику. Чтобы помнить, необходимо хранить. Это место, место хранения точек возврата, называется «стеком вызовов», и для него нашей системой программирования выделяется опреде-ленная область оперативной памяти. В этом стеке запоминают-ся не только адреса точек возврата, но и значения всех локаль-ных переменных, образно выражаясь, запоминается «слепок» процедуры или функции. По «слепку» восстанавливается вы-зывающая логика (процедура или функция), как только мы осознаем это утверждение, становятся понятными связи по данным в рекурсивном вычислительном процессе.

1 вызов (п=5)

1. Вы знакомы с развитием технологий программирования по мате-риалам для чтения из предыдущих занятий. Найдите для себя от-вет на вопрос «С какого момента времени развития технологий в системах программирования появилась возможность для реализа-ции рекурсивной логики»? 2. Из вышеприведенного примера. Создаются ли копии програм-много кода функции Factorial в процессе её работы? 3. Написание рекурсивных процедур по образцу, приведенному ниже, вряд ли можно считать приемлемым.

Page 151: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 151

Procedure Generate; Begin(*t - глобальная переменная. *}

I f t=n Then Exit Else Begin

<действия 1>; t:=t+l ; Generate; t : = t - 1 ; <действия 2>;

End; End;

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

Приведем несколько коротких примеров, иллюстрирующих технику написания рекурсивной логики. 1. Сложение двух чисел (а+b). Рекурсивное определение опера-

ц и и сложения двух чисел:

Function Sum С а, Ь: Integer): Integer; B e g i n

I f b=0 Then Sum:=a Else I f b>0 Then Sum:=Sum( a+1, b-1) Else Sum:=Sum ( a-1, b+1 ) ;

End; 2. Перевод натурального числа из десятичной системы счисле-

ния в двоичную. Procedure Rec ( п: Integer ) ; Begin

I f п>1 Then Rec (n Div 2) ; Wri te (n Mod 2) ;

End; Как изменится процедура при переводе чисел в восьмерич-

ную систему счисления? 3. Определить, является ли заданное натуральное число про-

стым. Сформулируем по-другому. Верно ли, что заданное на-туральное число п не делится ни на одно число, большее или

Page 152: Pascal 8 Класс - Окулов

152 Часть вторал

равное т , но меньшее п. П р и этом з н а ч е н и я числа m нахо-дятся в п р о м е ж у т к е от 2 до п. Утверждение истинно в двух случаях : • если т = п ; • п не делится на т , и истинно утверждение д л я чисел от

т + 1 до п - 1 . Function Simple (т,п: Integer): Boolean; Begin

I f m=n Then Simple :=True Else Simple: = (n Mod m <> 0) And Simple (m+1, n) ;

End; Первый вызов ф у н к ц и и — Simple(2,n) , где n — проверяемое

число. 4. Дано натуральное число п>1. Определить число а, д л я кото-

рого выполняется неравенство: 2 а - 1 <п<2 а . Зависимость :

[а(п div 2) + 1, п>1 Function а ( п: Integer): Integer; Begin

I f n=l Then a: =1 Else a:=a(n Div 2) +1; End;

5. Д л я вычисления наибольшего общего делителя двух чисел можно использовать рекурсивную ф у н к ц и ю вида:

Function Nod(a,b:Integer) -.Integer; Begin

I f (a=0) Or (b=0) Then Nod:=a+b Else I f a>b Then Nod:=Nod (a-b,b)

Else Nod:=Nod(a,b-a); End;

6. Нахождение максимального элемента в глобальном массиве А реализуется с помощью следующей рекурсивной логики .

Procedure Search__Max (п : Integer; Var х: Integer) ; Begin

I f n=l Then x:=A[1] Else Begin

Search_Max (n-1, x) ; I f A[n]>x Then x:=A[n];

End; End;

Page 153: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 153

7. Возведение целого числа а в целую неотрицательную степень п рекурсивно реализуется так . Function Pow (a ,п : Integer) . - I n t e g e r ; Begin

I f n=0 Then Pow:=1 Else I f n Mod 2=0 Then Pow: =Pow (a*a, n Div 2)

Else Pow:=Pow(a,n-l) *a; End;

8. Процедура ввода с клавиатуры последовательности чисел (окончание ввода — 0) и вывода ее на экран в обратном по-р я д к е имеет вид: Procedure Solve;

Var п:Integer; Begin

ReadLn (n) ; I f n<>0 Then Solve; Write (n: 5) ;

End;

9. Каждое число Фибоначчи равно сумме двух предыдущих чи-сел при условии, что первые два равны 1 (1, 1, 2, 3, 5, 8, 13, 21, . . . ) . В общем виде п-е число Фибоначчи можно опреде-лить так:

{1, е с л и п = 1 или п = 2 Ф(п - 1) + Ф(п - 2) , е с л и л > 2

Function Fib (п : Integer) :Integer; Begin

I f n<=2 Then Fib: =1 Else Fib: =Fib (n-1) +Fib (n-2) ;

End;

10. Найти сумму первых n членов арифметической (геометри-ческой) прогрессии. Арифметическая прогрессия определя-ется тремя параметрами: первым элементом (а), разностью между соседними элементами (d) и количеством членов про-грессии (п). Очередной элемент прогрессии вычисляется из предыдущего прибавлением разности — aB0Boe:=aCTapoe+d. Function Sa (n,a -.Integer) Integers-

Begin I f n>0 Then Sa :=a+Sa (n-1, a+d) Else Sa:=0;

End;

Page 154: Pascal 8 Класс - Окулов

154 Часть вторал

П р и м е ч а н и е Попробуйте убрать ветвь Else и объяснить полученный результат.

Д л я п р и в е д е н н ы х п р и м е р о в п р о р и с у й т е с х е м ы работы про-цедур и ф у н к ц и й д л я к о н к р е т н ы х в х о д н ы х д а н н ы х (не очень б о л ь ш и х , естественно) .

Экспериментальный раздел работы

1. Д а н ы н а т у р а л ь н ы е ч и с л а п и ft. П е р е ч и с л и т ь все последова-т е л ь н о с т и д л и н ы п из ч и с е л от 1 до k. Н а п р и м е р , п=3, k=2. К о л и ч е с т в о п о с л е д о в а т е л ь н о с т е й — k". Д е й с т в и т е л ь н о , на к а ж д о е из п мест разрешается записывать числа от 1 до k, пе-р е м н о ж а е м и п о л у ч а е м р е з у л ь т а т . Строгое д о к а з а т е л ь с т в о осуществляется т р а д и ц и о н н о , с п о м о щ ь ю простой и н д у к т и в -ной с х е м ы . П у с т ь последовательности в ы в о д я т с я в следую-щ е м п о р я д к е : 1 1 1 , 1 1 2 , 1 2 1 , 1 2 2 , 2 1 1 , 2 1 2 , 2 2 1 , 2 2 2. Ч е м он (порядок) характеризуется? Возьмем две соседние по-следовательности, н а п р и м е р 1 1 2 и 1 2 1 . Д о к а к о й - т о пози-ц и и они совпадают, в данном случае до второй (несовпадение возможно и в первой позиции) , а в этой позиции число во вто-рой последовательности больше, чем число в первой последо-вательности. Такой порядок называется лексикографическим. К а к из очередной последовательности получать с л е д у ю щ у ю в л е к с и к о г р а ф и ч е с к о м п о р я д к е ? И д е м справа . Н а х о д и м пер-в ы й э л е м е н т , не р а в н ы й k. У в е л и ч и в а е м его на е д и н и ц у , а «хвост» последовательности м а к с и м а л ь н о ухудшаем , т. е. за-писываем 1. В последней последовательности сделать такое преобразование, естественно, не представляется в о з м о ж н ы м . П р и м е ч а н и е

Разумеется, сейчас самое время потренироваться на примерах. Как Вы поняли постановку задачи?

Одномерный массив из п элементов — очевидная структура д а н н ы х д л я х р а н е н и я последовательности . Т и п элементов в массиве м о ж н о определить к а к обычно Integer, но давайте ч у т ь - ч у т ь и з м е н и м : Туре МуАггау = Array[l..n] Of 0..k; М ы ввели ограничение на т и п Integer. Д о п у с т и м ы м и з н а ч е н и я м и элементов массива А я в л я ю т с я ц е л ы е ч и с л а в интервале от 1 до k, и компьютер контролирует выход и х за д о п у с т и м ы й диапа-зон (использовался о г р а н и ч е н н ы й тип д а н н ы х л и на предыду-щ и х з а н я т и я х ? ) . Определим «костяк» п р о г р а м м ы и очевидную процедуру вывода элементов массива .

Page 155: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 155

($R+) Program Му13_1; Const п=3;

к=2 ; Type MyArray=Array[1. .п] Of 0 . .к; Var A -.MyArray; Procedure Print;

Var l: Integer; Begin

For l: =1 To n Do Write (A [i ] : 3) ; WriteLn ;

End; Procedure Solve (t: Integer) ; Begin End;

Begin FillCnar (A, SizeOf (A) ,0) ( *Очистка . *) ; Solve (1) ;

End. Из первого вызова рекурсивной процедуры Solve возникает

вопрос — а что определяет параметр рекурсии Позицию в по-следовательности (номер элемента в массиве А). Следующий вызов — Solve(t+l), при значении t, большем п, необходимо выводить очередную последовательность, а иначе — записать очередной элемент, он определяется значением управляющей переменной г, в позицию t.

Procedure Solve (t:Integer); Var l: Integer; Begin

I f t>n Then Print Else For i:=l To k Do Begin

Aft] :=i; Solve (t+1) ;

End; End;

Измените программу так, чтобы последовательности выво-дились в следующей очередности: 2 2 2, 2 2 1, 2 1 2, 2 1 1, 1 2 2, 1 2 1, 1 1 2, 1 1 1.

Предположим, что значение k больше значения п. Изменить программу. В последовательности чисел i-й элемент не должен превосходить г. Это простое изменение программы. А сейчас, при этом же предположении (k>n), требуется сгенерировать

Page 156: Pascal 8 Класс - Окулов

156 Часть вторал

в о з р а с т а ю щ и е последовательности чисел. Н а месте t в последо-вательности допускается запись чисел из интервала от A[t-1 ] + 1 до k-(n-t). Действительно , t=n, м а к с и м а л ь н о е число в пози-ц и и п равно, естественно, k; t=n-l, м а к с и м а л ь н о е число в пози-ц и и п-1 есть k-1 и т. д. Это м ы с л ь приводит к с л е д у ю щ е м у из-менению п р о ц е д у р ы Solve.

Procedure Solve(t:Integer) ; Var l:Integer; Begin

I f t>n Then Print Else For i:=A[t-l]+l To к-n + t Do Begin

A[t]:=i; Solve(t + 1) ;

End; End;

О д н а к о т о л ь к о э т и м и з м е н е н и е м в п р о г р а м м е о г р а н и ч и т ь -ся н е л ь з я . П е р в ы й вызов п р о ц е д у р ы Solve( 1) и о п и с а н и е т и п а Type MyАггау= Array [ 1 ..п] Of l..k; п р и в о д и т к з н а к о м о й о ш и б к е Error 201: Range check error, в строке For i:=A[t-l]+1 To k-n+t Do. П р и ч и н а очевидна — в ы х о д и н д е к с а за п р е д е л ы массива А. И з м е н и м о п и с а н и е т и п а на Type MyArray=Ar-ray[0..n] Of l..k;. П р о г р а м м а в ы д а е т р е з у л ь т а т , но с о д е р ж и т л о г и ч е с к у ю неточность . П р и п е р в о м в ы з о в е Solve использует -ся неопределенное з н а ч е н и е А [ t - 1 ] , т. е. 1 п р и б а в л я е м неиз-вестно к чему . П р и и з м е н е н и и первого в ы з о в а на А[0]:=0; Solvef 1); п р и х о д и м к новой о ш и б к е — Error 76: Constant out of range ( значение к о н с т а н т ы в ы х о д и т за д о п у с т и м ы й диапа -зон). Е щ е одно и з м е н е н и е — Type MyArray=Array[0..п] Of 0..k; и м о ж н о , в первом п р и б л и ж е н и и , з а к о н ч и т ь экспери-м е н т ы с п р о г р а м м о й .

2. Что такое перестановки чисел от 1 до л, определено на пре-д ы д у щ е м з а н я т и и . Необходимо написать рекурсивную про-цедуру генерации перестановок чисел от 1 до л . В чем здесь суть рекурсии? Ф и к с и р у е м на первом месте очередное число и генерируем все перестановки этого вида. П р и этом посту-паем точно по такой ж е схеме. Ф и к с и р у е м число на втором месте и генерируем все перестановки у ж е с фиксированны-м и элементами на первом и втором местах. Пусть п равно 3. Перестановки д о л ж н ы генерироваться в следующей после-довательности: 1 2 3, 1 3 2, 2 1 3, 2 3 1, 3 2 1, 3 1 2. Если не очень понятно, то рассмотрим при п=4 . Вместо многоточия запишите генерируемые перестановки 1 2 3 4 , 1 2 4 3 , 1 3 2 4, 1 3 4 2 , 1 4 3 2 , 1 4 2 3, 2 1 3 4, 2 1 4 3, 2 3 1 4 , 2 3 4 1 4 1 2 3.

Page 157: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 157

Необходимо осознать , что после генерации всех перестано-вок с ф и к с и р о в а н н ы м э л е м е н т о м в п о з и ц и и t перестановка возвращается в исходное состояние и осуществляется новый обмен з н а ч е н и я м и элемента из позиции с номером t+1 и i + l .

Program Му13_2;(*Проиедура Print совпадает с соответствующей процедурой из предыдущей программы. *}

Const п=4; Type MyArray=Array [1. .n] Of Integer; Var А:MyArray;

i:Integer; Procedure Swap(Var a,b:Integer);

Var с:Integer; Begin с: =a ; a : =b;b: =c; Ends-

Procedure Solve(t:Integer); Var l: Integers-Begin

I f t>=n Then Print Else For l:=t+l To n Do Begin

Swap (A [ t+1],A [ l ] ) ; Solve (t + 1); Swap (A [ t+1 ] ,A[i]) ;

Ends-End;

Begin For i:=l To n Do A[i]:=i;(*Первая перестановка -12 3 п.*} Solve(0);{*Первый вызов. *)

End. Д л я простоты будем считать , что к а ж д ы й р е к у р с и в н ы й

вызов приводит к созданию «копии» процедуры. Это не т а к , но нам легче п р и этом п р е д п о л о ж е н и и формулировать вопро-сы. С к о л ь к о «копий» процедуры Solve создается при п=3? С к о л ь к о раз создаются к о п и и с t=2? Сколько в ы п о л н я е т с я л и ш н и х обменов т и п а A[j]<r>A[j]? Н а рисунке приводится часть с х е м ы вызовов (обозначено стрелками) процедуры Sol-ие(при п=3) . Д л я ответа на сформулированные вопросы за-кончите р и с у н о к .

Page 158: Pascal 8 Класс - Окулов

157 Часть вторал

<123> <132>

-*• <213> <231 >

<321 > <312>

Измените описание типа элементов у массива А на Туре MyArray=Array[l..n] Of 1..п; (ограниченный тип). Возникает ошибка (Error 26: Type mismatch) при вызове процедуры Swap. Устраните ее. Не исключено, что Вам придется ввести еще один тип данных.

3. Дано натуральное число п. Необходимо получить все разбие-ния числа п на сумму слагаемых п=а1+а2+ ... +ак , где к, ах, а2, ..., ак>0. Будем считать разбиения эквивалентными, если суммы отличаются только порядком слагаемых. Множество эквивалентных сумм представляем последовательностью ах, а2, ..., ак, где ах >а2 > ... >ак. При п=4 разбиения 1+1+1 + 1, 2+ +1+1, 2+2, 3+1, 4 перечислены в лексикографическом поряд-ке. Для каждой пары соседних разбиений находится номер позиции, в которой число из второго разбиения больше чис-ла из первого разбиения, а до этой позиции последователь-ности совпадают. Порядок 4, 3+1, 2+2, 2+1+1, 1+1+1+1 опре-деляют как обратный лексикографическому. Наша задача — генерировать разбиения в последнем порядке. Рекурсивная схема реализации просматривается достаточно легко. Мы за-писали в позицию t число аг Сумма чисел a1+a2+...+at=s, тог-да с позиции t+1 мы генерируем все разбиения числа n-s, причем для элемента (и остальных) должно выполняться не-равенство А[ t+1 ]<A[t]. Переход к следующему разбиению в позиции t сводится, если это возможно, к вычитанию 1.

Уточним некоторые технические детали. Разбиение хранит-ся в глобальном массиве А. Количество элементов в разбиении различно. Значение переменной t определяет текущий размер

Page 159: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 159

выводимой части массива А. Процедура Print претерпит небо-льшое изменение.

Procedure Print (t: Integer) ; Var l: Integers-Begin

For l: =1 To t Do Write (A[i] : 3) ; WriteLn;

End; Рекурсивная процедура Solve имеет два параметра: число п

и позицию t, начиная с которой необходимо получить все раз-биения числа п. Первым разбиением является разбиение, соот-ветствующее записи числа п в позицию t, а затем ... Последова-тельно записываем в позицию t числа п-1, п-2 ..., и так до 1, а с позиции t+1 генерируем все разбиения чисел 1, 2, ..., п-1 со-ответственно. Приведем текст решения.

($R+} Program Му13_3; Const п=4; Type MyArray=Array[1..п] Of Integer; Var A:MyArray;

Procedure Solve (n, t -.Integer j ; Var l: Integers-Begin

I f n=l Then Begin A [t] : =1; Print (t) ;End Else Begin A[ t] : =n ; Print (t) ;

For i:=l To n-1 Do Begin A[t] :=n-i; Solve (i,t+l) ;

Ends-End;

End; Begin

Solve(n,l); ReadLn;

End. Однако после запуска программы получается результат, не-

сколько отличный от предполагаемого: 4, 3 1, 2 2, 2 1 1, 1 3, 1 2 1, 1 1 2, 1 1 1 1.

Строки 5, 6, 7 лишние. Изменим процедуру. Procedure Solve (п, t: Integer) ;

Var i-.Integers-Begin

I f n—1 Then Begin A[t] :=1;Print (t) ,-End

Page 160: Pascal 8 Класс - Окулов

160 Часть вторал

Else Begin I f n<=A[t~l] Then Begin A[t] :=n;Print f t ) ;End;

For l : =1 To n-1 Do Begin A[t]:=n-i; Solve<i,t + 1) ; End;

End; End;

П о я в л е н и е индекса t-1 требует изменить описание типа мас-сива на Type MyArray=Array[0..n] Of Integer; и осуществлять начальное присвоение А[0]:=п перед первым вызовом Solve ( п , 1 ) . Н е внесение этих и з м е н е н и й приводит к известной ошиб-ке Error 201: Range check error. Что м ы п ы т а л и с ь сделать? От-сечь л и ш н и е разбиения при их выводе, но не генерации! Запуск п р о г р а м м ы нас очередной раз разочарует . Р е з у л ь т а т работы: 4, 3 1, 2 2, 2 1 1, 1 2 1, 1 1 1 1. И з м е н и м процедуру.

Procedure Solve(п,t:Integer); Var l,q:Integer; Begin

I f n=l Then Begin A[t]:=1;Print (t) ;End Else Begin I f n<=A[t-l] Then Begin Aft]:=n;Print (t);End;

q:=Min (n~l,A[t-l]) ; For l: =q DownTo 1 Do Begin Aft]:=!/ Solve(n-i,t + 1) ; End;

End; End;

Она потребует ф у н к ц и ю Min, но ничего страшного, вставьте ее в текст р е ш е н и я .

Function Min(a,Ь:Integer):Integer; Begin

I f a<b Then Min:=a Else Min:=b; End;

Получаем п р а в и л ь н ы й результат: 4, 3 1 , 2 2 , 2 1 1 , 1 1 1 1 , но часть решений м ы по-прежнему «отсекаем» только на выхо-де. Продолжите исследование. Научитесь выводить разбиения в следующих порядках :

• 1 1 1 1, 2 1 1, 2 2, 3 1, 4; • 1 1 1 1, 1 1 2, 1 3, 2 2, 4; • 4, 2 2, 1 3, 1 1 2, 1 1 1 1.

Page 161: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 161

П р и м е ч а н и е При отладке программы и достижении понимания ее работы Вам очень поможет отладчик системы программирования.

Задания для самостоятельной работы

1. Написать рекурсивную программу вывода на экран следую-щей картинки:

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 (16 раз) 2 2 2 2 2 2 2 2 2 2 2 2 (12 раз)

3 3 3 3 3 3 3 3 (8 раз) 4 4 4 4 (4 раза)

3 3 3 3 3 3 3 3 (8 раз) 2 2 2 2 2 2 2 2 2 2 2 2 (12 раз)

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 (16 раз)

2. В конце XIX века в Европе появилась игра под названием «Ханойские башни». Реквизит игры состоит из 3 игл, на ко-торых размещается башня из колец. Цель игры — перенести башню с левой иглы (1) на правую (3), причем за один раз мож-но переносить только одно кольцо, кроме того, запрещается помещать большее кольцо над меньшим.

1 2 3

Выполните трассировку приведенного ниже решения с испо-льзованием отладчика системы программирования. Убедитесь в правильности решения. Попробуйте написать не рекурсивную программу решения задачи.

Program Myl3_zl; Var п: Integer; Procedure MoveTown (High,FromI, Toi, WorkI: Integer); Begin

6-452

Page 162: Pascal 8 Класс - Окулов

162 Часть вторал

I f High>0 Then Begin MoveTown(High-1,FromI,WorkI,Tol); Writeln('Перенести кольцо №',High,' с иглы ',FromI,' на иглу ' , Tol); MoveTown(High-1,WorkI,Tol,FromI);

End; End;

Begin Write('Количество колец - ') ; ReadLn (n) ; MoveTown(n,l,3,2) ;

End.

Д а н н а я задача своим происхождением обязана индусской легенде, которая рассказывает , что в большом храме Бенареса бронзовая плита поддерживает три а л м а з н ы х с т е р ж н я , на один из которых бог нанизал во время сотворения м и р а 64 золотых диска . С тех пор день и ночь монахи , с м е н я я друг друга , к а ж -дую секунду перекладывают по одному диску согласно описан-н ы м в ы ш е правилам. Конец мира наступит тогда, когда все 64 диска будут перемещены, на что потребуется чуть больше 58 млрд. лет.

3. Составить рекурсивную функцию вычисления значения фун-кции А к к е р м а н а для неотрицательных чисел п и т , вводи-мых с клавиатуры.

Найдите одну пару значений п и т , при которых возникнет переполнение стека адресов возврата.

4. Ф у н к ц и я F(n) определена для целых положительных чисел следующим образом:

m + 1, п = О Л(л - 1,1) , п * 0, m = О

Л(л - 1, Л(л, m - l ) ) , n > 0 , т > 0

F(n) 1, е с л и л = 1

^ F(n div i) , е с л и л > 2

Page 163: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 163

Верно ли следующее решение?

Function F (п : Integer) -.Integer; Var i , s : Integer; Begin

I f n—1 Then s:=1 Else For i:=2 To n Do

s : =s +F (n Div 2) ; F:=s;

End;

Можно ли вычислить с помощью этой реализации функции значения F при п=5, 6, 7, ..., 20.

5. Число сочетаний С(п,т) вычисляется по формуле С(п,т)= =С(п-1,т. 1 )+С(п-1,т.), при этом С(п,0)=1 кС(п,п)=1. На-пишите рекурсивную функцию для подсчета числа сочетаний. Определите область ее применения (диапазон допустимых зна-чений п и т ) .

Материал для чтения

1. Продолжим рассмотрение понятия алгоритма. В матема-тике это строгое понятие и теория алгоритмов является по сути одним из ее разделов. Почему возникла эта теория, ведь веками пользовались неформальным определением алгоритма? Дело в том, что появились задачи, для которых не могли найти алго-ритмов решения. После безуспешных попыток естественно воз-ник вопрос «Существуют ли вообще алгоритмы решения таких задач»? Опираясь на нестрогое понятие алгоритма, доказать этот факт средствами математики, а других нет, не представля-ется возможным. Примеры задач. А. Тьюринг доказал, что невозможно найти алгоритм, который по произвольной про-грамме определял бы, остановится эта программа на произволь-но заданном входе или нет. Для десятой проблемы Гильберта о разрешимости в целых числах полиномиальных уравнений также не найдено решения.

2. Алфавит — это непустое конечное множество символов. Всякую конечную последовательность символов алфавита назо-вем словом. Допускается существование пустого слова (обозна-чим Л).

Примеры. Натуральные числа можно записать в виде слова в алфавите {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}. Целые — {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -}. Рациональные — {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, —, /}.

Page 164: Pascal 8 Класс - Окулов

164 Часть вторал

Однако для записи натуральных чисел достаточно алфавита, состоящего из одного символа {I}. Слова в этом алфавите вы-глядят: I, I I , I I I , I N I , M i l l , . . . Дополнение этого алфа-вита, обеспечивающее запись целых и рациональных чисел, не вызывает трудностей. Рассмотрим квадратные уравнения с це-лыми коэффициентами, например 7х 2 -Зх+5=0. В алфавите {О, 1, 2, 3, 4, 5, 6, 7, 8, 9, - , +, *, =} они представимы в виде слов типа 7*х*х-3*х+5=0. Примеры можно продолжить. Главное: входные, промежуточные и конечные результаты любой прак-тически решаемой задачи можно интерпретировать как слова в некотором конечном алфавите. Если это не так, то вопрос о по-лучении результата решения задачи остается открытым. Как обрабатываются слова? Заменой, часть слова или все слово за-меняется другим. Эту замену называют подстановкой. Напри-мер, есть алфавит { | , #} и подстановка: I # I-» I | . Слово I I # I I I # II М в результате последовательного применения

подстановки преобразуется в слово I I I II I II I. 3. Нормальные алгоритмы А. А. Маркова. Дадим определе-

ние понятия алгоритма по А. А. Маркову. Пусть А — некото-рый алфавит, не содержащий символов «->» и «.». Обычной формулой подстановки в алфавите А называется ориентиро-ванная подстановка P-»Q, где Р и Q — слова в алфавите А. За-ключительной подстановкой в алфавите А называется подста-новка P.Q, где Р и Q — слова в алфавите А. При этом Р называется левой частью формулы подстановки, a Q — ее пра-вой частью. Обыкновенную и заключительную подстановки записывают единым образом P-»yQ, где у есть или символ «.» или пустое слово Л.

Определение.Конечная непустая упорядоченная система под-становок:

Pi->YIQI P2-+Y2Q2

Pn-»YnQn называется нормальной схемой в алфавите А, все Р,, Qt — слова в алфавите А для всех i от 1 до п.

Нормальный алгоритм задается алфавитом А и нормальной схемой в этом алфавите.

Page 165: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 165

Схема работы нормального алгоритма. Дано слово Р в алфа-вите А. Выясняется, есть ли среди подстановок нормальной схемы хотя бы одна с левой частью входящей в Р. Если та-ких подстановок нет, то алгоритм заканчивает работу и резуль-тат — само слово Р. Если же в нормальной схеме есть Р , то берется первая подстановка, удовлетворяющая этому условию, и применяется к слову Р. В результате получается новое слово Р ' . В том случае, когда выбранная подстановка была заключи-тельная, алгоритм заканчивает работу, в противном случае — процесс продолжается со словом Р' . Слово Q, получаемое в ре-зультате работы алгоритма (к нему не применима ни одна под-становка), есть результат работы алгоритма.

Итак, А. А. Марковым дано строгое с математической точки зрения определение алгоритма, нормального алгоритма. Что это дает? Тезис А. А. Маркова: «Если для решения задачи есть алгоритм, то этот алгоритм может быть представлен в виде нор-мального алгоритма». Тезис не доказывается, ибо, с одной сто-роны, есть нечеткое, нестрогое понятие алгоритма, а с другой стороны, строгое определение нормального алгоритма. Тезис может быть опровергнут только конкретным примером, кото-рого пока не найдено и, видимо, не будет найдено. Далее. Есть задача. Для этой задачи доказывается (строгое математическое доказательство), что для нее не может быть построена, создана нормальная схема. Вывод — данная задача не имеет алгоритма решения.

4. Проблема слов. Дан алфавит А, нормальная схема и два слова — Р и Q. Вопрос. Можно ли из слова Р получить слово Q, применяя подстановки из нормальной схемы? Если да, то слова считаются эквивалентными. Как установить факт эквивалент-ности для любых двух слов в алфавите А? Пример. Дан алфа-вит А={а,Ь} и система подстановок: ba-oab, аа-оЛ и bb-оЛ (обра-тите внимание на то, что подстановки неориентированные). Решите задачу для нескольких слов. Оказывается, что любое слово сводится к одному из следующих слов: a, b, ab, Л. Схема решения: среди всего множества слов W выделяется подмноже-ство ScW, такое, что любое слово из W преобразуется нормаль-ной схемой к одному из слов, принадлежащих S. Тогда, если слова Р и Q преобразуются в одно слово, то они эквивалентны, иначе — нет. Как находить подмножество S — открытый во-прос. Еще пример. Дан алфавит А={а,Ь} и система подстано-вок: ba<->aab, ааа<->Л и ЬЬ<->Л. Определить, эквивалентны ли два

Page 166: Pascal 8 Класс - Окулов

Часть вторая

произвольных слова — Р и Q. Проверьте, что множество S со-стоит из слов: Л, a, b, аа, ab, aab.

В з аключение отметим, что н о р м а л ь н ы е а л г о р и т м ы А. А. Маркова не единственная схема уточнения п о н я т и я алго-ритма, придания ему статуса строгого математического понятия со всеми вытекающими из этого факта последствиями. Известны алгоритмическая система Э. Поста (1935 г.), алгоритмическая система А. М. Тьюринга (1937 г.), аппарат частично рекурсив-ные функции и т. д.

Page 167: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 167

З а н я т и е № 14. С и м в о л ь н ы й и с т р о к о в ы й т и п ы д а н н ы х

План занятия • символьный тип данных; • короткие программы иллюстрации работы с символьным

типом данных; • строковый тип данных; • процедуры и функции работы со строковым типом дан-

ных; • экспериментальная работа с программами: выделения слов

из текста; вставки символа пробела после запятой; нахож-дения расстояния между строками; генерации всех под-строк строки; определения эквивалентности двух слов;

• выполнение самостоятельной работы. Символьный тип данных. Идентификатором типа является

зарезервированное слово Char. Значениями типа Char являют-ся элементы конечного и упорядоченного множества символов, т. е. на множестве значение типа Char есть отношение порядка. Американский стандартный код для обмена информацией (AS-CII — American Standard Code for Information Interchange) есть система кодирования, в которой алфавитные, цифровые и управляющие символы представлены в виде 8-разрядного дво-ичного кода. Символы с кодами от 0 до 127 составляют так на-зываемую основную таблицу кодов ASCII. Эта часть идентична на всех IBM-совместимых компьютерах. Так, цифрам от 0 до 9 соответствуют коды от 4810 (001100 002) до 5710 (001110012); буквам латинского алфавита от А до Z — коды от 6510 (010000012) до 9010 (010110102); буквам от а до z — коды от 9710 (011000012) до 12210 (011110102); буквам русского алфави-та от А до Я — коды от 12810 (1000 0 0 002) до 15910 (100111112). Константы этого типа обрамляются в апострофы, например: '*' '3' 'G'. Для отображения множества символов на их порядко-вые номера и обратно существуют две функции: Ord и Chr. Функция Ord(w) дает порядковый номер символа w, Chr(i) определяет символ с порядковым номером i. Функции Ord и Chr обратные по отношению друг к другу: Chr(Ord(w))=w и Ord(Chr(i))<=i. Отношение порядка на множестве символов по-зволяет выполнять операции сравнения. Из двух символов ме-ньше тот, который встречается раньше в кодировке ASCII. Для величин типа Char функции Pred и Succ работают следующим образом: Pred(q)=Chr(Ord(q)-l) и Succ(q)=Chr(Ord(q)+l).

Page 168: Pascal 8 Класс - Окулов

167 Часть вторал

Короткие программы. В первом примере в ы в о д я т с я симво-л ы и соответствующие и м коды. П е р е м е н н а я к используется к а к счетчик д л я о р г а н и з а ц и и последовательного вывода — по 5 символов .

Program Myl4_1; Var г, k:Integer; Begin к :=0

WriteLn('Вывод порядковых номеров (кодов) символов — значение переменной i и самих символов.'); For i:=l То 255 Do Begin

Write (i:4, ' Символ ' ,Chr(i)); Inc (k); I f k=5 Then Begin WriteLn;k:=0; End;

End; End;

Во втором примере показано , к а к п е р е м е н н а я символьного типа используется в качестве у п р а в л я ю щ е й переменной в опе-раторе For. Символы выводятся так :

А ВВ с с с

WWW,.WWW (23 раза)

Program Му14_2; Var i:Char;

j .-Integer; Begin

For i: = 'A' To 'W' Do Begin For j:=l To Ord (i) -Ord (' A') +1 Do Write ( l ) ; WriteLn;

End; End.

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

Program Му14_2т; Var 1:Char; Begin For i :='a' To 'z' Do For j:='a' To i Do Write ( j ) ; ReadLn; End.

Page 169: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 169

В следующем примере подсчитывается количество симво-лов, введенных с клавиатуры. Ввод заканчивается символом ' . ' Вы вводите несколько символов, затем точку и нажимаете кла-вишу Enter. Программа выдает правильный результат. А если нажимать клавишу Enter после ввода каждого символа? Резу-льтат неверный, он как будто бы в три раза превышает истин-ный результат. На самом деле все верно. Нажатие клавиши Еп ter генерирует ввод еще двух символов (управляющих) — возврата каретки (код 13) и перевода строки (код 10).

Program Му14_3; Var 1: Char;

j:Integer ; Begin Read(i) ; j:=0; While K>'.' Do Begin Inc ( j ) ;Read (l) ;End; Wri teLn ( j ) ;

End. Другая версия этой простой программы позволяет отказать-

ся от символа точка как признака конца ввода данных. Символ # перед целым числом говорит о том, что это число следует рассматривать как ASCII код символа.

Program Му14_3т; Var 1 : Char;

j:Integer; Begin Read(i) ; j:=0; While i<>tl3 Do Begin Inc (J ) ;Read (i) ;End; WriteLn ( j ) ; ReadLn;

End. А если изменить строку на While к>#10 Do Begin Inc(j);

Read(i);End;, то количество подсчитанных символов на едини-цу больше. Объясните результат.

В следующем примере подсчитывается количество цифр во вводимых с клавиатуры данных.

Program Му14_4; Var ch :Char; к:Integer; Begin

Read(ch) ;

Page 170: Pascal 8 Класс - Окулов

170 Часть вторал

к:=0; While ch<>#13 Do Begin

IF (ch>=' 0') And (ch<='9') Then Inc(k); Read (ch) ;

End; WriteLn С Количество цифр равно ' ,к);

End.

И з м е н и т е п р о г р а м м у т а к , чтобы о н а • о п р е д е л я л а , я в л я ю т с я л и в в е д е н н ы е д а н н ы е п р а в и л ь н о й

з а п и с ь ю целого ч и с л а ; • в ы ч и с л я л а с у м м у ц и ф р введенного ч и с л а . Ф у н к ц и я Upcase(ch) преобразует з н а ч е н и е п е р е м е н н о й ch

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

Program Myl4_4m; Var ch:Char; Begin

Read(ch); While ch<>#13 Do Begin

IF (ch>='a') And (ch<='z') Then Write(Upcase (ch)) Else Write(ch); Read(ch);

End; End.

Строковый тип данных. Строкой н а з ы в а е т с я последовате-льность символов определенной д л и н ы . И д е н т и ф и к а т о р т и п а — слово String. П р и м е р ы о п и с а н и я п е р е м е н н ы х т и п а S t r i n g : Var Strl: String[10]; Str2: String; Str3:String[13], В к в а д р а т н ы х с к о б к а х у к а з ы в а е т с я м а к с и м а л ь н ы й р а з м е р (длина) строки . Если он не у к а з а н , то д л и н а строки с ч и т а е т с я р а в н о й 2 5 5 сим-волам . З а м е т и м , что строку м о ж н о р а с с м а т р и в а т ь к а к одномер-н ы й массив из символов — к к а ж д о м у символу строки допусти-мо о б р а щ е н и е по его номеру (Strl[i] — это о б р а щ е н и е к i-му элементу с т р о к и Strl). П е р в ы й символ строки (с и н д е к с о м 0) с о д е р ж и т ф а к т и ч е с к у ю д л и н у строки . П е р е м е н н ы е т и п а String в ы в о д я т с я на э к р а н м о н и т о р а посредством с т а н д а р т н ы х проце-дур Write и WriteLn и вводятся с п о м о щ ь ю ReadLn и Read. То есть вводятся и в ы в о д я т с я не поэлементно , к а к массивы , а сра-зу ц е л и к о м . С л е д у ю щ и е простой п р и м е р иллюстрирует сказан-ное.

Page 171: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 171

Program Му14_5; Var s:Stnng; w: String [10] ; v:Stnng[5];

i,j:Integer; Begin

ReadLn (v); WriteLn(v); WriteLn(Integer(v[0])); ReadLn(w); WriteLn(wj ; WriteLn(Ord(w[0]) ) ; ReadLn (s); WriteLn (s); WriteLn(Integer(s[0])); For i:=1 To 0rd(s[0]) Do Begin

For j:=l To l - l Do Write (' ' ) ; W r i t e L n ( s [ i ] ) ;

End; ReadLn ;

End. В ы вводите с т р о к и v и w б о л ь ш е й д л и н ы , чем у к а з а н о в опи-

с а н и и . Вывод н а экран показывает , что при этом они «обрезают-с я » . Операторы WriteLn(Integer(v[0])) и Wntebn(0rd(w[0])) обеспечивают вывод з н а ч е н и я д л и н ы строки . Если Вы измени-те п е р в ы й оператор на WriteLn(v[0]), то вместо цифрового з н а ч е н и я н а э к р а н в ы в о д и т с я «непонятный» символ . Попро-буйте о б ъ я с н и т ь этот результат и п о н я т ь с м ы с л преобразования Integer(v[0] ). С л е д у ю щ и е о п е р а т о р ы п р и м е р а демонстрируют п о с и м в о л ь н о е о б р а щ е н и е к строке — строка рассматривается к а к s : A r r a y [ 0 . . 2 5 5 ] Of Char;. Вывод символов с т р о к и s на э к р а н о с у щ е с т в л я е т с я «лесенкой» .

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

Примеры: • ' B a l k o n ' c ' b a l k o n ' ( O r d ( ' B - ) < O r d ( V ) ) ; • ' b a l k o n ' > ' b a l k e n ' (Ord( 'o ' )>Ord( ' e ' ) ) ; • ' b a l k o n ' > ' b a l k ' (длина первой строки больше); • ' к о ш к а ' > ' к о ш к а ' (так к а к д л и н а первой строки больше); • ' К о т ' = ' К о т ' ( р а в н ы по длине и совпадают посимвольно) .

П р и с в а и в а е м о е значение строки (строковая константа) , так ж е к а к и о т д е л ь н ы й символ т и п а Char, з аключается в апостро-ф ы . Н а п р и м е р , Strl:='y Егорки; Str2:= всегда отговорки;

Стандартные процедуры и функции работы со строковым типом данных приведены в таблице .

Page 172: Pascal 8 Класс - Окулов

172 Часть вторал

Тип Вызов Параметры Действие

Процедура Delete(s,p,n) Var s. String, р,п-Integer

Удаляются n символов из строки S, начиная с позиции р

Процедура lnsert(w,s,p) Var s String, p Integer,

В строку s, начиная с позиции р, вставляется строка w Если результат превысит 255 символов, строка обрывается ||

Процедура Str( v, s) v Integer ИЛИ v Real,

Var s'String,

Число V преобразуется в строку, результат в s

Процедура Val(s,v,w) s String, Var v Integer

или Var v Real,

Var w Integer,

Если строка s состоит из цифр, то они преобразуются в числовое значение переменной v, значение wравно 0 В противном случае, строка состоит нв только из цифр, — преобразование не выполняется, wO— I признак ошибки

Функция Concat(s1,s2, ,sm) илиs/+s2+ +sm

Значение функции типа String

s1,s2, , sm String,

Строки s/+s2+ +sm II записываются одна за другой Если I результат превысит I 255 символов, строка обрывается

Функция Copy(s,p,n) Значение функции

типа String

s. String, p.n- Integer;

Из строки s, начиная с позиции р, выбирается п символов

Функция Length(s) Значение функции

типа Integer

s String; Определяется длина s, т. е. число символов из которых она состоит

Функция Pos(w,s) Значение функции

типа Integer

w,s String; В строке s отыскивается первое вхождение строки w (номер позиции) Если вхождения нет, то выдается 0

Примеры. В приведенных примерах переменные si, s2, s3 имеют тип String, p,q — тип Integer. 1. sl:='y Егорки всегда отговорки';

Delete( si,7,8);

Page 173: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 173

Результат — sl = 'Y Егора отговорки' . 2. sl:= 'У Егорки всегда отговорки' ;

«2 := 'Матрены и '; Insert(s2,sl,3); Результат — sl='У Матрёны и Егорки всегда отговорки' .

3. р: = 1234;д:=34.5 ; Str(p.sl); Str(q,s2); Результат — sl = ' 1234 ' , s 2 = ' 3 4 . 5 ' .

4. s .Z := '555 ' ; s2 := '23 .345 ' ; s3 := '34 r r2 ' ; Val(sl.p.w); Val(s2,q,w); Val(s3,p,w); Результат : в первом случае — р=555 , w=0; во втором слу-чае — q=23 .345 , w=0; в третьем случае — w<>0, р=??? .

5. sl:='Y Егорки всегда отговорки, '; s2:='у Миладки всегда ш о к о л а д к и ' ; s3:=Concat(sl,s2); (или s3 :=s i+s2 ; ) Результат — вЗ= 'У Егорки всегда отговорки, у Миладки все-гда ш о к о л а д к и ' ;

6. s l : = ' y Егорки всегда отговорки, у Миладки всегда шоколад-к и ' s2:=Copy( s,28,26); Результат — s 2 = ' y Миладки всегда шоколадки ' .

7. s / : - ' y Егорки всегда отговорки' ; p :=Length(s) ; Результат — р—25.

8. sl:='У Егорки всегда отговорки'; p:=Pos('o',s ); Результат — р=5. Продолжите примеры. Напишите программу для исследова-

ния работы перечисленных процедур и функций. Обратите особое внимание на граничные условия, т. е. когда какой-либо параметр выходит за пределы строки. В этой работе можно использовать приведенные н и ж е короткие примеры. 1. Подсчет количества символов (параметр q) в строке (пара-

метр st) . Function QChar(q:Char;st: String): Byte;

Var i , k: Bytes-Begin

к: =0 / For i : =1 To Length (st) Do I f st[i]=q Then Inc (k) ; QChar:=k;

End;

Page 174: Pascal 8 Класс - Окулов

174 Часть вторал

2. У д а л и т ь среднюю букву п р и нечетной д л и н е с т р о к и и две средние б у к в ы п р и четной д л и н е строки . Procedure Mi Del ( Var st: String); Var k: Byte; Begin к:=Length ( s t ) ; I f к Mod 2=1 Then Delete(st, к Div 2+1,1) Else Delete(st,k Div 2,2); End;

3. З а м е н и т ь все в х о ж д е н и я подстроки w в строке s t на подстро-к у V. Procedure Ins(w, v:String; Var st: String);

Var k: Byte; Begin

While Pos(w,st)<>0 Do Begin k:= Pos (w,st); Delete(st,k,Length(w)); Insert(v,st,k);

End; End;

4. Подсчитать сумму ц и ф р , в с т р е ч а ю щ и х с я в строке . Function Sum (st: String): Integer;

Var l , k, d, s: Integer; Begin

s: =0 ; For l;=1 To Length ( s t ) Do Begin

Val (st [ i ] , d, k) ; I f k=0 Then s:=s+d;

End; Sum:=s;

End;

Экспериментальный раздел работы

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

Page 175: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 175

Program Му14_6; Const п=20;т=10;(*Количество слов в тексте и количество букв в слове. Естественно, что эти параметры программы допускается изменять. *} Type TString=String[m]; Var А:Array[1..n] Of TString;

s:String; k, l .-Integer ;

Procedure DelPr(Var s:String);{*Удаляем пробелы в начале текста. *} Begin

While (s[l] = ' ' ) And ( s o " ) Do Delete (s, 1,1) ; Ends-

Function GetWord (Var s:Strmg) : TString; { *Выделяем слово, при этом оно удаляется из текста, и убираем пробелы после слова. *) Begin

GetWord:=Сору(S,1,Pos (' ' ,s) -1) ; Delete(s,1,Pos(' ',s)>; DelPr ( s ) ;

End; Begin

WriteLn('Введите текст'); ReadLn (s) ; s:=s + ' ';(* Добавляем символ пробела в конец текста. Зачем?*) DelPr(s);(*Удаляем пробелы в начале текста. *) к: =0 ; While s<>" Do Begin (*Пока текст не пустой. *}

Inc (k) ; A[k]:=GetWord(s);{*Берем слово из текста.*)

End; For-i: =1 To k Do Wri teLn (A [l ] ); ReadLn;

End. Удалите вызов процедуры DelPr(s) из основной программы,

а в ф у н к ц и и GetWord. переставьте этот вызов в ее начало. Что изменится в работе программы? Н а к а к и х исходных данных она не будет правильно работать? Что произойдет, если в конец текста не добавлять символ пробела? В процедуре DelPr изме-ните While (s[l]=' ') And (so") Do Delete(s,l,l) на While (s[l]=' •) Do Deletefs,l,l). Найдите пример исходного текста,

Page 176: Pascal 8 Класс - Окулов

176 Часть вторал

при котором результат работы программы о к а ж е т с я неверным или его вообще не будет. П р о д о л ж и т е эксперименты с програм-мой.

2. По п р а в и л а м м а ш и н о п и с и после з а п я т о й в тексте всегда ста-вится пробел. Программа исправляет такой тип ошибки в тек-сте.

Program Му14_7; Var 1:Integer;

s:String; Begin WriteLn('Введите текст'); ReadLn (s) ; i: =1 ; While KLength (s) Do Begin If (s[i]=',') And Notts [1+1] = ' ') Then Insert Г ' ,s,i+l); Inc (i) ; End; WriteLn (s) ; ReadLn; End.

Почему выбран оператор While, а не Fori Почему в теле цик-ла нельзя использовать функцию Pos(' ' ,s)'> Обратите внимание, если запятая — последний символ текста, то м ы не добавляем пробела. Измените программу для исправления ошибки типа «после символов '!', ' ? ' , ' . ' должен стоять пробел, а затем текст начинается с заглавной буквы». Вспомните еще ряд традицион-ных ошибок и модифицируйте программу для их исправления.

3. Д а н ы две строки X и У. Назовем расстоянием (г) м е ж д у X и У количество символов, которыми X и У различаются меж-ду собой. Например : X='abcd', Y='dxxc', r=4; Х='111 1111', Y= 111222', r=7. Написать программы вычисления расстояния между строка-ми.

Решение . Program Му14_8;

Var i ,j,г:Integer; S,X,Y:String;

Begin WriteLn ('Первая строка') ; ReadLn (X) ; WriteLn('Вторая строка');ReadLn(Y);

Page 177: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 177

I f Length(X)>Length (Y) Then Begin S:=X;X:=Y;Y:=S; End;(*Y строка большей длины. *} r:=Length (Y); For l:=1 To Length (X) Do Begin J : =1 ; While (j<=Length (Y) ) And (Y[j]<>X[i]) Do I n c ( j ) ; I f j>Length (Y) Then Inc(r) Else Begin Dec(r);Delete(Y,j,1);End;f*Есть совпадение. Уменьшаем расстояние и удаляем символ из Y. *}

End; WriteLn('Расстояние ' , г) ; ReadLn;

End. К к а к и м последствиям приведет исключение из решения

строки со сравнением длин строк и записи в Y строки наиболь-ш е й длины? Проверьте. Исключим оператор Delete(Y,],l). Что за результат у нас будет получаться? Измените программу так, чтобы в тексте находилось два слова, расстояние между кото-р ы м и имеет максимальное (минимальное) значение.

4. Д а н а строка не более чем из шести произвольных различных символов. Разработать программу вывода всех возможных подмножеств (подстрок), составленных из символов данной строки. Количество подстрок 2 " - 1 , где п — количество сим-волов в строке. Естественно, что каждый символ входит в под-строку не более одного раза. Будем генерировать шестираз-рядные двоичные числа, так, 010101 соответствует подстрока ' b d f ' строки ' abcde fh ' . Принцип генерации: просматриваем число слева направо до первого нуля , заменяя при этом все 1 на 0, например после 110011 получаем 001011. Завершение генерации — число 0000001. Решение . Program Му14_9;

Const MaxN=6;

Type MyArray=Array[l..MaxN+1] Of Byte; Var A,Last:MyArray;

i:Integer; s:String;

Function Eq (X, Y:MyArray;n: Integer) :Boolean; {*Сравнение текущего значения с 0000001, если п=6.*)

Page 178: Pascal 8 Класс - Окулов

178 Часть вторал

Var х:Integer; Begin

i : = 2 ; While (1<=п) And (X [ i ] = Y [l ]) Do Inc(i); Eg:={i>n);

End; Begin

WriteLn('Строка '};ReadLn (s); FillChar(A,SizeOf(A),0); FillChar(Last,SizeOf(Last) ,0) ; Last[Length(s)+1]:=1;{*Первое несуществующее разбиение. *} While Not (Eq(A,Last,(Length(s)+1))) Do Begin (*Пока нет совпадения, выполняем генерацию следующей подстроки. *)

i : =2 ; While (K=Length (s) ) And (A[i]=l) Do Begin A[i] :=0 ; Inc (i) ;End; A[i]:=1; I f K=Length(s) Then

For i:=l To Length(s) Do I f A[i]=l Then Write (S[i]>; {*Вывод подстроки. *}

WriteLn; End; ReadLn;

End. В схеме генерации реализовано обычное прибавление едини-

ц ы к двоичному числу , только число з а п и с ы в а е т с я слева напра-во: 000000 , 100000 , 010000 , 110000 , 0 0 1 0 0 0 и т. д. К а ж д о м у такому числу ставится в соответствие подстрока . И з м е н и т е схе-му генерации так , чтобы е д и н и ц а п р и б а в л я л а с ь т р а д и ц и о н н ы м способом: 000000 , 000001 , 000010 , 000011 , 0 0 0 1 0 0 и т. д. А можно ли и с к л ю ч и т ь генерации последовательности двоичных чисел из р е ш е н и я задачи?

5. Р а с с м о т р и м с л е д у ю щ у ю задачу (по м а т е р и а л у д л я ч т е н и я п р е д ы д у щ е г о з а н я т и я ) . З а д а н ы две с т р о к и А и В д л и н ы N (1<W<100), состоящие из символов 0 и 1 (двоичные слова) . Д о п у с к а е т с я следующее преобразование строк . Любую под-строку А и л и В, с о д е р ж а щ у ю четное число единиц , м о ж н о перевернуть, т. е. записать в обратном порядке . Н а п р и м е р , в строке 11010100 м о ж н о перевернуть подстроку , составлен-ную из с и м в о л о в с 3 -й по 6-ю п о з и ц и и . П о л у ч и т с я с т р о к а

Page 179: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 178

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

Пример. Д а н ы строки 100011100, 001011001. Решить дл них задачу. Ответ — «да» и преобразования: 6 9, 3 8, 1 5.

Основная идея решения задачи формулируется следующи образом. Н а множестве всех слов W следует выделить подмн< жество S (ScW), такое, что для любого слова teW можно ук< зать эквивалентное ему слово r e S . Алфавит и слова у нас зад; ны, преобразования тоже. Осталось определить множество £ свести каждое слово к представителю из множества S, и есл представители совпадают, то слова эквивалентны. Из к а к и слов состоит S? Слов вида 11 . . .100 . . .0100 . . .0 , причем к а к n e j вая группа единиц, так и первая группа нулей, а т а к ж е следук щ а я единица (слово из одних 0) могут отсутствовать. Основны фрагменты решения .

Const Мах=100; Type Tstr=StrmglMax) ;

Procedure Reverse(Var S:Tstr;l,j;Integer); (*Достаточно очевидная процедура - перевертываем подстроку S с позиции i до позиции J . *} Var ch:Char; Begin

While K j Do Begin ch : =S [l] ;S[i] :=S(J] ;S[j] :=ch;Inc(i) ;Dec ( j ) ;

End; Ends-Procedure Normalize (Var S:Tstr) ; (*Сводим слово к

его представителю из S. *) Var i , j , cnt-.Integer ; Begin

cnt:=0; For i:=l To Length (S) Do I f S[i]='l' Then

Inc (cnt) ; {*Подсчитываем количество 1 в

For i:=1 To cnt-1 Do I f S[i] = '0' Then Begin (*Находим «левый» 0 в

слове. *)

Page 180: Pascal 8 Класс - Окулов

180 Часть вторал

Repeat Inc(j) Until S[jJ='1';{*Поиск первой 1.*) Repeat Inc(j) Until S [ J ] = ' 1';(*Поиск второй 1, подстрока содержит четное число 1, ее можно «перевернуть».*}

Reverse(S,l,j); End;

End; Допишите программу. Заметим, что вывод преобразований,

в случае эквивалентности слов, требует дополнительной струк-туры данных (массива) для запоминания значений i и j при каждом перевертывании.

Если исключить из условия задачи вывод преобразований, то её можно решить гораздо проще. Рассмотрим таблицу. Столб-цы с заголовком «?» оставлены осознанно.

Внимательно изучив таблицу, догадываемся, что в послед-них столбцах указано число нулей. Но каких? В четвертом столбце количество нулей после четного числа единиц, в пя-том — после нечетного. И вторая часть нашего вывода — эти числа не изменяются в процессе преобразования слов (строки таблицы с номерами 4 - 6 и 7 -11) . После этого можно напи-сать небольшой фрагмент подсчета этих чисел (сразу после их ввода)

Page 181: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 181

cnt:=0; chl:=0;ch2:=0 ; For l:=1 To Length(S) Do

I f S[i]='0' Then I f cnt Mod 2=0 Then Inc(chl) Else Inc(ch2)

Else Inc (cnt);

И т а к , если з н а ч е н и я chl и ch2 д л я слов совпадают, то они э к в и в а л е н т н ы в в ы ш е п р и в е д е н н о м значении . Н у л е в а я п о з и ц и я в слове считается при этом четной, н а п р и м е р числа 1, 5, 4 од-нозначно определяют слово 0 0 0 0 0 1 0 0 0 0 .

Обоснуем результат . И н в а р и а н т о м преобразования я в л я е т с я з н а к о ч е р е д у ю щ а я сумма вида: !.(-1 )'*at, где i — номер едини-цы, а а, — номер п о з и ц и и i е д и н и ц ы . Схема доказательства . Обозначим через s номер п о з и ц и и в подстроке, соответствую-щ и й центру переворачиваемой подстроки. Пусть х — номер по-з и ц и и до «переворота», у — номер этой п о з и ц и и после опера-ц и и «переворота». Очевидно, что (x+y)/2=s, то есть y=2s-x или al-^2s-al. Количество элементов в сумме четно, з н а к и чере-дуются , и Ъ(-1 )l*al =£(~1 )'*(2s-aJ. Поэтому после ввода слов следует подсчитать количество 1 в к а ж д о м из них и суммы ука-занного типа . Если они совпадают, то слова А и В эквивалент-ны. Н а п р и м е р , д л я строки 0101 сумма равна (-1 )'*2 +(-1 )2* *4=2, после п е р е в о р а ч и в а н и я — 1010 с у м м а — (-1 )'*1 + (-1 )2* * 3 = 2 . Они совпадают , с т р о к и э к в и в а л е н т н ы . Д л я п р и м е р а ( 1 1 0 1 0 0 и 110001) эти с у м м ы равны - 3 и - 5 , строки неэквива-л е н т н ы .

Задания для самостоятельной работы

1. Н а п и с а т ь программу вывода последовательности символов: • Z Y Y X X X . . . A A . . A A ; • ABC.. .ZZBC.. .ZZZC.. .ZZ. .ZZ на экран .

2. Составить программу, которая выводит True, если в строке буква А встречается ч а щ е , чем буква В, и False в противном случае.

3. Проверить , п р а в и л ь н о л и в з аданном тексте расставлены круглые скобки (т. е. находится ли справа от каждой откры-вающей скобки соответствующая ей з акрывающая скобка, а слева от каждой закрывающей — соответствующая ей откры-вающая) .

4. Подсчитать количество гласных латинских букв в строке.

Page 182: Pascal 8 Класс - Окулов

182 Часть вторал

5. Удвоить вхождение некоторой буквы в текст. Например, текст «мама папа» должен иметь вид — «маамаа паапаа».

6. Даны две строки. Вывести буквы, встречающиеся и в той и в другой строках.

7. Дан текст. Вывести все слова, начинающиеся с согласных букв латинского алфавита.

8. Дан текст. Определите • длину самого короткого и самого длинного слов; • количество слов, начинающихся и оканчивающихся од-

ной и той же буквой; • количество слов, в которых содержится хотя бы одна за-

данная буква; • количество слов, которые содержат определенное коли-

чество заданной буквы; • количество слов, являющихся палиндромами.

9. Дан текст. Вывести слова, встречающиеся в тексте по одно-му разу.

10. Дан текст. Вывести различные слова. 11. Дан текст. Вывести все слова, предварительно преобразовав

каждое из них по следующему правилу: • удалить из слова все предыдущие вхождения последней

буквы; • оставить в слове только первые вхождения каждой бук-

вы. 12. Дан текст. Вывести слова, которые отличны от последнего сло-

ва и удовлетворяют следующему условию: • в слове нет повторяющихся букв; • буквы слова упорядочены по алфавиту; • слово совпадает с начальным отрезком латинского алфа-

вита (a, ab, abc, abed,...). 13. Дан текст. Вывести все слова, предварительно выполнив пре-

образования их по правилу: заменить во всех словах первую букву на заглавную.

14. Написать программы решения ребусов: ОДИН+ОДИН+ +ОДИН+ОДИН+ОДИН=ПЯТЬ; КУБ=(К+У+Б)3; ТРИ+ДВА= =ПЯТЬ;

VOLVO+FIAT=MOTOR. При решении ребусов одинаковым буквам соответствуют одинаковые цифры. Например, приве-дем часть ответов последнего ребуса: 15615+9743=25358, 15715+9643=25358, ..., 72672+9451=82123, их всего 10.

Page 183: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 183

15. Дан текст. Написать программу проверки правильности на-писания сочетаний «жи», «ши», «ча», «ща», «чу» и «щу». Исправить ошибки.

16. Даны три строки. Определить, можно ли из символов первых двух строк получить третью строку.

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

Материал для чтения

1. О кодировании. Мы уже неоднократно использовали этот термин. Попробуем разобраться с ним. Из истории. Коды поя-вились в глубокой древности в виде криптограмм, зашифрован-ных (засекреченных) сообщений. В шифре, получившем назва-ние — шифр Юлия Цезаря , алфавит размещается к а к бы на круге по часовой стрелке. После последней буквы, например Я в русском алфавите, идет первая буква алфавита (буква А в русском алфавите). Для зашифровки буквы текста заменяются буквами, отстоящими по кругу на заданное число букв (сдвиг) дальше по часовой стрелке. Пусть сдвиг равен 2 и сообщение составлено в русском алфавите. Буква А заменяется на букву В, буква Я на букву Б. Принцип расшифровки (декодирования) понятен, если известно значение сдвига. Шифр Цезаря рас-шифровывается достаточно легко даже при неизвестном значе-нии сдвига. Известны вероятности букв pt, i=l, .... л (л — число букв в алфавите). Подсчитаем частоты букв fl в сообщении (считаем, что оно достаточно длинное). Вычисляем значение Sum(sh)=Abs(p-ft(sh)). Сумма берется по г от 1 до л. Значение sh — это значение сдвига. Найдем минимальное значение Sum, подсчитывая ее для различных значений sh от 1 до п. Значение sh, на котором достигается минимум значения Sum, считаем сдвигом в шифре Цезаря.

С течением времени появились достаточно сложные шифры. Клодд Шеннон показал, что возможно построение криптограм-мы, которая не поддается никакой расшифровке, при условии, что не известен способ её составления. Итак, создание шиф-ров — объект криптографии, взлом шифров — криптоанализ. В целом эту область деятельности называют криптологией.

Кодирование, как мы его понимаем в настоящее время, BO3J никло позднее, и оно связано с проблемой передачи сообщений

Page 184: Pascal 8 Класс - Окулов

184 Часть вторал

по линиям связи (телеграф, телефон, радио, телевидение и т. до-исторически первый код, предназначенный для передачи сооб-щений, предложен изобретателем телеграфного аппарата Сэмю-элем Морзе. В этом коде каждому символу соответствует своя последовательность из кратковременных (точка) и длительных (тире) импульсов тока, разделяемых паузами (точка, тире, пау-за — три элемента для кодировки символов, код Морзе троич-ный). Другим широко используемым кодом в телеграфии явля-ется код Бодо. Для кодирования используются два элементарных сигнала — импульс и пауза. Символам сопоставляются кодо-вые слова одинаковой длины (из пяти сигналов). В коде Бодо, в отличие от кода Морзе, используется равномерное кодирова-ние. В коде Морзе наиболее часто используемым символам со-ответствуют кодовые слова меньшей длины (основополагающая идея в теории оптимального кодирования), поэтому для одно-значного распознавания (декодирования) и потребовался третий символ — пауза. Коды, использующие два различных элемен-тарных сигнала, называют двоичными. Договоримся обозначать эти сигналы символами 0 и 1. Фрэнсис Бэкон был первым, кто использовал для кодирования два символа.

2. Общая постановка рассматривае-мой задачи поясняется на рисунке. Бук- / ' \ вами Aj , А2 обозначены два различных / —V алфавита. Сообщение записывается с I j использованием алфавита А г Перевод (преобразование) этого сообщения (W) \ ^ / в сообщение, записанное в алфавите А2, V J у есть кодирование. Обратный перевод (ТО — декодирование. Основные требования к решению задачи W: оптимальность, помехоустойчивость и однозначность декоди-рования (преобразование W такое, что преобразование V одно-значно решает задачу перевода сообщения в исходный алфа-вит). Под оптимальностью понимается то, что закодированное сообщение должно быть как можно меньше (короче). Передача сообщения связана с затратами, при оптимальном кодировании затраты минимизируются. В любом канале передачи данных возможны искажения. Они приводят к ошибкам. Решение за-дачи, при котором ошибки и находятся, и, по возможности, устраняются, называется помехоустойчивым кодированием. И наконец, однозначность декодирования. В коде Морзе она до-стигается за счет введения символа «пауза», в коде Бодо — все символы кодируются одинаковыми по длине последовательно-

Page 185: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 185

стями. В последующих материалах для чтения рассматривают-ся схемы кодирования с А2={0, 1}, двоичное кодирование.

3. Ответим еще на один вопрос (повторим). Сколько различ-ных сообщений можно закодировать в m двоичных разрядах? Если ш=1, то два — 0 и 1. В общем случае — 2Ш. Строкой из 0 и 1 мы кодировали целые числа, символы (кодировка ASCII). С помощью 0 и 1 кодируются все данные, обрабатываемые ЭВМ. В Computer Science способы кодировки и записи различной ин-формации в двоичном виде называют форматами. Они настоль-ко многочисленны, что их сложно перечислить: форматы графи-ческого вывода, форматы сжатия текстов, форматы для звуковых файлов, кинофайлов и т. д.

Page 186: Pascal 8 Класс - Окулов

186 Часть вторал

З а н я т и е № 15. В е щ е с т в е н н ы й т и п д а н н ы х

План занятия • вещественный тип данных; • короткие программы иллюстрации работы с веществен-

ным типом данных; • экспериментальная работа с программами: решения урав-

нения f(x)=0; вычисления площади фигуры; вычисления элементарных функций с использованием представления этих функций в виде некоторых бесконечных сумм;

• выполнение самостоятельной работы. Вещественный тип данных. Числа записываются в виде

мантиссы (дробной части числа), умноженной на порядок (сте-пень десяти, обозначается буквой Е). Существуют следующие типы вещественных чисел ( наиболее используемый — Real).

Тип Диапазон возможных значений

Точность Формат

J Real 2 9Е-39 1 7£38 11-12 знаков 6 байт

Single 1 5Е-45 3 4Е38 7 -8 знаков 4 байта |

Double 5.0Е-324 .1 7Е308 15-16 знаков 8 байт

Extended 3 4Е-4932 1 1Е4932 19-20 знаков 10 байт

| Сотр -9.2Е18 9 2Е18 19-20 знаков 8 байт

П р и м е ч а н и е Величины типа данных Сотр принимают целочисленные значе-ния, в известной степени этот тип является расширением типа Lon glnt до 19 разрядов. Допустимые арифметические операции: '+ ' сложение, '- '

вычитание, '*' умножение, ' / ' деление и операции сравнения. На вещественном типе данных нет отношения порядка, поэто-му операцией сравнения '= ' следует пользоваться с большой осторожностью.

Вещественные числа записываются • путем отделения дробной части от целой с помощью точ-

ки, например 127.3, 25.0, -16 .003, 200.59, 0.54; • с использованием символа Е, например 0.000009 — 9Е-6,

0 .62П 04 - 0 .62£+4, - 10 .8 *1012 10.8Я12, 20*10-3 -20Я-3.

Page 187: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 187

П е р е ч и с л и м стандартные ф у н к ц и и работы с вещественными в е л и ч и н а м и :

Sqr(X) Квадрат X

SqrtfX) Квадратный корень из X

Sm(X) Синус X, X в радианах

Cos(X) Косинус X, X в радианах

Arctan(X) Арктангенс X, результат а радианах

Exp(X) Число е возводится в степень X

Ln(X) Вычисление натурального логарифма X

Pi Возвращает значение числа Р/

Int(X) Возвращает целую часть аргумента X

Frac(X) Возвращает дробную часть аргумента X

Trunc(X) X имеет тип Real, результат — целая часть X Тгипс(6 8)=6, Тгипс(-7 9)=7

Round(X) X имеет тип Real, результат — ближайшее к X целое число Round(5 8)=6, Round(5 5,1=6

Короткие программы. Первый пример связан со сравнени-ем двух вещественных чисел. Запустите эту простую програм-му.

Program Му15_1; Var x:Real;

у: Reals-Begin

х:=0.3;у:=0.3; WriteLn(х=у); ReadLn;

End.

Получен о ж и д а е м ы й результат — значение True. Изменим описание переменной у на y.Single. Запуск программы не прой-дет, есть ошибка Error 116: Must be in 8087 mode to compile this.1 Суть в том, что выполнение арифметических операций с вещественными числами осуществляется по-другому, чем с це-л ы м и числами. И х реализация требует больших затрат процес-сорного времени. С этой целью созданы специальные быстро-действующие сопроцессоры для выполнения операций с числами вещественного типа.

Только в случае, если на компьютере не установлен арифметический со-процессор и не включен эмулятор сопроцессора.

Page 188: Pascal 8 Класс - Окулов

187 Часть вторал

Д и р е к т и в а {$N+}. Д а н н а я д и р е к т и в а к о м п и л я т о р а осуществляет переключение м е ж д у д в у м я р а з л и ч н ы м и р е ж и м а м и генерации кода д л я вы-полнения операций с вещественными числами. В режиме {$N-}, а по у м о л ч а н и ю в к л ю ч е н он, к о м п и л я т о р использует библиоте-к у подпрограмм д л я работы с 6 -байтовыми вещественными ч и с л а м и , т. е. типом Real. В р е ж и м е {$N+} к о м п и л я т о р генери-рует программный код, обеспечивающий повышенную точность в ы ч и с л е н и й и доступ к четырем дополнительным веществен-н ы м типам д а н н ы х .

Итак , при использовании дополнительных вещественных ти-пов д а н н ы х требуется записывать д и р е к т и в у {$N+} в начало программы. Вставим ее и добавим еще два оператора. Текст программы имеет вид:

{$N+} Program Myl5_lm;

Var x:Real; у:Single;

Begin x:=0.3;у:=0.3; WriteLn (x=y) ; WriteLn(x); WriteLn(y); ReadLn;

End. Результат сравнения есть False. Причина очевидна — для хра-

нения значений х н у выделяется разное количество разрядов. Отметим еще один момент. П р и работе в р е ж и м е {$N+} стан-

дартная процедура Write осуществляет вывод в формате Exten-ded, так ж е к а к и значения , возвращаемые стандартными фун-к ц и я м и , перечисленными в ы ш е по тексту. Сравните работу следующих фрагментов программ (s in(30°)=sin(0.5236. . . )=0.5) и проведите еще одно небольшое исследование. Задайте в разделе Const константы а и Ъ. Затем, вместо оператора WriteLn(Pi) на-п и ш и т е WriteLn(Pi:a:b). Найдите зависимость между измене-нием значений а, Ь и результатами вывода числа Pi на экран .

{$N-} Program Му15_2; Begin

WriteLn (Sm (0.5236) ) ; WriteLn (Pi);

End.

Page 189: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 188

l$N+} Program Му15_2; Begin

WriteLn(Sin(0.523 6)); WriteLn(Pi);

End. П р о д о л ж и м н а ш и примеры. Известно, что (х/а)*а=х. Про-

верим это утверждение с помощью следующей простой про-г р а м м ы .

($N+> Program Му15_3; Var х:Extended;

i , cnt: Integers-Begin

cnt:=0; For l:=20000 To 30000 Do Begin

x:=i/10000; x:=x*10000;If x=i Then Inc(cnt); End; WriteLn С Число совпадений ',cnt); WriteLn('Число несовпадений ',10001-cnt); ReadLn;

End. Результат. Число совпадений — 8209, число несовпаде-

ний — 1792. П о п ы т к а вывести значения х и i при несовпадени-я х ничего не дает. Числа равные, а есть несовпадение, т. е. разли-ч и я в разрядах, которые не улавливаются при выводе значений переменных на экран . Они не улавливаются и другими способа-ми. Однако ограничимся этим, главное в том, что постараемся не использовать «лобовое» сравнение величин вещественного типа , а будем использовать для этих целей короткую функцию. Она приведена в следующей модификации программы.

($N-f } Program Му15_3;

Const eps=l. 0Е-10; Var x:Extended;

i,cnt:Integer; Function Eq (x,y:Real) -.Boolean;

Begin Eq: -Abs (x-y) <eps;

End; Begin

cnt:=0;

Page 190: Pascal 8 Класс - Окулов

190 Часть вторал

For i:=20000 То 30000 Do Begin х:=i/l0000; x:=x*10000; I f Eq ( x f l ) Then Inc(cnt);

End; WriteLn (' 'Число совпадений ' , c n t ) ; WriteLn С Число несовпадений ',10001-cnt); ReadLn;

End. Р е з у л ь т а т работы п р о г р а м м ы — 10001 совпадение .

Экспериментальный раздел работы

1. Приближенное решение уравнения f(x)=0, где f ( x ) — задан-н а я ф у н к ц и я . Р е ш и т ь уравнение — значит найти такое значе-ние х*, при котором f(х*)=0. Поиск решения осуществляется на интервале [а,Ъ], причем f(a)<0, &f(b)>0. Поиск такого ин-тервала — отдельный вопрос, м ы его не рассматриваем . Д л я р е ш е н и я используем одну из ф у н д а м е н т а л ь н ы х идей инфор-м а т и к и — «разделяй и властвуй», в данном случае «метод де-ления пополам». Находим точку с — половину отрезка [а,Ь]. Е с л и f(c)>0, то г р а н и ц у Ь и з м е н я е м на з н а ч е н и е с, а если f(с)<0, то и з м е н я е м а. Процесс продолжаем , пока д л и н а ин-тервала не будет меньше заданной точности.

Пусть f(Х)=Х2-2, Т. е. м ы попытаемся н а й т и значение квад-ратного к о р н я из 2. Решение .

($N+1 Program Му15_5;

Const eps=l.ОЕ-3; Var a,b,c .-Real; Function Eq(x,y:Real) -.Boolean; {*Функция рассмотрена выше. *}

Page 191: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 191

Function F(x:Real) :Reals-Begin

F : = S g r ( x ) - 2 ; Ends-

Begin WriteLn (' Введите интервал. ' ) ReadLn(a,b); I f F(a) *F(b)>0 Then WriteLn ('На этом интервале мы не можем найти решение уравнения.')

Else Begin While Not Eq(a,b) Do Begin

с: = (a+b) /2; I f F(c}>0 Then b:=c Else a:=c;

End; WriteLn('Результат ' ,a);

End; ReadLn;

End. Обратите в н и м а н и е на то, что вычисление f ( x ) оформлено

к а к отдельная ф у н к ц и я . Использование программы д л я других д а н н ы х требует л и ш ь ее изменения . Запуск программы при р а з л и ч н ы х з н а ч е н и я х а и Ъ Вас не очень обрадует. Получаются з н а ч е н и я 1 .4140625 и л и 1 .41357421875 (для р а з л и ч н ы х интер-валов а и Ь), а фактическое значение равно 1.414213.. . Измени-те значение eps на 1.0Е-6. Точность вычислений имеет большое значение при работе с типом данных Real. Исследуйте еще ряд ф у н к ц и й с помощью приведенной программы:

• х 2 - 6 х + 5 = 0 • x - c o s ( x ) = 0 • х - 1 п ( х ) - 2 = 0 • 2х 3 —9х 2 -60х+1=0 Возникает вопрос, к а к определять интервал [а,Ь], удовлетво-

р я ю щ и й условиям задачи? А начните с таблицы, вначале со-ставляйте её вручную (например, для первой функции) :

Г " х 0 2 3 6 1 1 Знак Цх) + - II

После ряда попыток Вы вспомните, что разумная лень — двигатель прогресса, так это называется, и автоматизируете процесс составления таблицы, т. е. добавите к программе еще одну процедуру.

Page 192: Pascal 8 Класс - Окулов

192 Часть вторал

2. Пусть Ц х ) — н е п р е р ы в н а я п о л о ж и т е л ь н а я ф у н к ц и я на отрез-ке [а,Ъ] (а<Ъ). В ы ч и с л и м п л о щ а д ь фигуры, ограниченную гра-ф и к о м ф у н к ц и и f(x), осью Л: И п р я м ы м и х=а и х=Ь. Д л я этого разобъём отрезок [а,Ъ] на п р а в н ы х отрезков одинаковой дли-н ы Ax=(b-a)/n\ a=x(0)<x(l)<...<x(i)<x(i+l )< <x(n)=b. Н а к а ж д о м из отрезков [x(i),x(i+l)] к а к на основании, построим п р я м о у г о л ь н и к с высотой f(x(i)). П л о щ а д ь прямоугольников м ы у м е е м с ч и т а т ь . С у м м а п л о щ а д е й всех п р я м о у г о л ь н и к о в даст п р и б л и ж е н н о е значение п л о щ а д и ф и г у р ы . Общая форму-ла s

npu6,=(b~a)/n* (f(x(0)+f(x(l))+...+f(x(n-l)). Естест-венно , что чем «мельче» будет разбиение, тем точнее м ы под-считаем п л о щ а д ь ф и г у р ы . А сейчас вспомните один из тезисов н а ш е й работы — «все подвергай сомнению, все проверяй» . Где неточность в в ы ш е и з л о ж е н н о м материале?

ц и ю — f(x)=x2 на и н т е р в а л е от 1 до 2. П л о щ а д ь ф и г у р ы равна 2 .333. . .

($N+;) Program Му15_6;

Const eps=l.ОЕ-3; Var a,b,s,dx:Real; i,n:Integer; Function Eg(x,y:Real):Boolean;{*Функция рассмотрена выше. *}

Function F(x:Real):Real; Begin

F:=Sqr(x); End;

Begin WriteLn('Введите интервал и число частей, на которые он разбивается'); ReadLn (a,b,n);

Page 193: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 193

х:=а; dx;=(b-a)/п; s:=0; For i: =1 То п-1 Do Begin s:=s-bF(x) ; x:=x+dx; End; WriteLn('Результат ' , (b-a)/n*s); ReadLn;

End. Запускаем программу при фиксированных границах интерва-

ла и различных значениях п. Результаты удручают. При п=6 он равен 1.5277..., при п=50 — 2.224992... , при п=100 — 2.78749..., при п = 3 0 0 — 2.315046. . . Давайте изменим решение . Будем сравнивать два соседних п р и б л и ж е н н ы х значения площади д л я р а з л и ч н ы х п. Если их разность больше заданной точности вы-числения , то увеличим значение п и снова подсчитаем пло-щадь . Начальное значение п в программе взято 10, шаг измене-н и я 100. Естественно, эти параметры программы вынесены в раздел констант , так ж е к а к и точность вычисления . Предыду-щ а я логика оформлена к а к ф у н к ц и я вычисления площади . Мы не смешиваем задачи. Эту часть нам известно, к а к решать .

{$N+;I

Program Myl5_6m; Const eps=l.0E-3;

nb=l0;ns=l00; Var a,b,wn,ws:Real; I,n:Integer; Function Eq(x,y:Real) -.Boolean; { *Функция рассмотрена выше. *}

Function F(x:Real):Real; Function Sq (n : Integer) .-Real ;

Var x,dx,s:Reals-Begin

x:=a; dx: = (b-a) /n ;s : =0; For i:=l To n-1 Do Begin s :=s+F (x) ; x:=x+dx; End; Sq:= (b-a)/n*s;

End; Begin

WriteLn('Введите границы интервала'); ReadLn (a,b) ; n : =nb ; un : =Sq (n) ; Repeat

ws: =un ; n : =n +ns; wn:=Sq(n) ; Until Eq (ws, wn) ; WriteLn('Результат: количество частей и значение площади',n,wn); ReadLn;

End.

Page 194: Pascal 8 Класс - Окулов

194 Часть вторал

После з а п у с к а получаем, что п р и л = 8 1 0 достигается требуе-м а я точность вычислений , результат — 2 .32654955 . . . . Однако в уме м ы подсчитали точнее. И з м е н и м точность на 1.0Е-6. В а ш компьютер задумается на некоторое время, а результат л = 2 3 3 1 0 и п л о щ а д ь — 2 .33309739. . . Д л я самоконтроля Вам предлагает-ся в ы ч и с л и т ь площадь д л я следующих ф у н к ц и й : f(x)=1/(1+х), [0,1]; f(:с;=1/х, [1,3]; f(x)=sin(x), [0,Pi/2].

3. Д л я в ы ч и с л е н и я элементарных ф у н к ц и й в м а т е м а т и к е ши-роко распространено представление этих ф у н к ц и й в виде не-которых бесконечных сумм. Не вдаваясь в обоснование т а к и х представлений, приведем некоторые из них : е*=1 +х+х2/2 1 +Х1 /3.' + +х"/п' + sin (х)=х-х3/3 '+х5/5!-х7/7\ + + (-1) "х2"*1/(2п+1) ' + . cos (х) =1 -Х2/2 ! +х"/4 ! -х6/6 '+ +(-1) "х2"/ (2п) ! + 1п (1+х) =х-х2/2+х3/3-х"/4+ +(-1)"*1х"/п+ (~1<х<1)

В к а ж д о м из р а з л о ж е н и й точность представления ф у н к ц и и будет, вообще говоря, тем в ы ш е , чем больше взято с л а г а е м ы х в сумме. П р и ч е м з н а ч е н и я самих слагаемых с ростом п стремят-ся к нулю. Д л я в ы ч и с л е н и я значений ф у н к ц и и с некоторой за-данной точностью eps поступают следующим образом. Вычисля-ют и суммируют слагаемые до тех пор, пока очередное слагаемое не станет по абсолютной величине меньше eps и л и абсолютное значение разности м е ж д у соседними слагаемыми не станет ме-ньше eps (что лучше?) . Полученную сумму и п р и н и м а ю т за приближенное значение ф у н к ц и и . Продемонстрируем этот ме-тод на примере в ы ч и с л е н и я ф у н к ц и и sin(x).

{$N+) Program Му15_7;

Const eps=l.ОЕ-3; Var x,sn,ss:Real;

p,n:Integer; Function Eq(x,у:Real):Boolean;{*Функция рассмотрена выше.*} Function F(n:Integer;Var x:Real):Real;

Var i-.Integer; stLonglnt;

Begin s:=l; For i:=2 To n Do s:=s*i; x:=x*Sqr (x); F:=x/s;

End; Begin

Page 195: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 195

WriteLn('Введите значение х ' ) ; ReadLn (х) ; ss : =0 ; sn :=х ; п: =1 ;р: =1 ; ( *р — для реализации чередования знака слагаемого. *) Repeat

ss:=sn;(*Предыдущее значение слагаемого. *} n:=n+2;р:=-1 *р; sn : =ss+p*F (n, х) ; ( *Новое значение слагаемого.*}

Until Eq (ss, sn) Or (n>=l2);(*Второе условие требуется по простой причине — факториал числа мы умеем вычислять только при этих значениях п. *} WriteLn('Результат ',sn); ReadLn;

End. Известно, что sm(30°)=l /2 , sm(45°) = Sqrt(2)/2, 30°=0,5236...

радиан, 45°=0,7854... радиан. Проверьте работоспособность про-граммы. При какой точности вычисления нам не будет хватать наших знаний по вычислению факториала числа.

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

Задания для самостоятельной работы

1. Квадратное уравнение ax 2 +bx+c=0 задается своими коэффи-циентами. Его корни находятся по формуле X j = ( - b + S q r t (b2-4*a*c))/(2*a) и x 2 ==(-b-Sqr t (b 2 -4*a*c)) / (2*a) . Написать программу нахождения корней квадратного уравнения.

2. Написать программы вычисления следующих выражений:

• ! / = ( l - I ) ( l - I ) . . . ( l - I ) , n > 2

• у = cos(l + cos(2+...+ cos(39 + cos40)...)) • у = sin х + sin sin x+...+ sin s in . . . sin x(rt раз) • у = sin x + sin x2 +...+ sin x" • у = s i n * + sin2 *+. . .+sin" x „ c o s l cos l + cos2 „ cos 1 + К + cosn • у = К

s i n l s i n l + s in2 s i n l + Л + s inn • У = \х\ + х' • y = |x | + 4 - * 3 - 1 - х 2

• y = \x-2\ + 3-xB

• y = 6 b2 + | b - 3 f - 1 5 • y =(3 • *3 + 18 • x2) • x + 12 • x2 - 5 • y = (d + с + b) • e - б • k - 1 A

Page 196: Pascal 8 Класс - Окулов

196 Часть вторал

• у = 3 с3 + | с 2 - 4 с + 7|3 - 5 с

• i/ = ]* + 4 | - | * 2 - 3 • * + б|

3. Вычислить приближенное значение бесконечной суммы:

• J - + J _ + - L + . . . 1 - 2 2 3 3 - 4

г-, 1 1 1 • + + + ... 1 - 3 2 - 4 3 5

1 1 1 +

1 2 3 2 3 - 4 3 4 5 4. Написать программы вычисления

i - I + ! _ . . . + _ i L _ 2 3 9999 10000

следующими четырьмя способами: • последовательно слева направо; • последовательно слева направо вычисляются

+ 3 + " ' + 9999 И 2 + 4 + " ' + 10000' затем второе значение вычитается из первого;

• последовательно справа налево; • последовательно справа налево вычисляются суммы (как

во втором случае), а затем выполняется вычитание. Сравните результаты и найдите их разумное объяснение.

5. Рассмотрим бесконечную последовательность чисел у1, у2, у3, ... , образованную по следующему закону:

х + т. - 1 = 2

1 х yt = —((т. - l)yi.x + ——), (i=2, 3, ...), где х — данное дейст-вительное число, т — натуральное число. Эта последовате-льность позволяет получить сколь угодно точные приближе-ния числа %[х(х > 0). Составить программу для вычисления значения %Гх с заданной точностью eps.

6. Дано п вещественных чисел. Написать программы: • вычисления разности между максимальным и минима-

льным элементами; • определения количества элементов, которые больше сво-

их «соседей», то есть предыдущего и последующего чи-сел;

Page 197: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 197

• определения минимального значения разности двух про-извольных чисел.

7. Представим обыкновенную дробь — (т < п) в виде цепной

« т 1 тт П 5 1 дроби — = - . Например, — = . п „ , 1 7 , 1

Процесс преобразования заключается в следующем: 5 /7=1/ (7/5)=1/(1+2/5)=1/(1+1/(5/2))=1/(1 +1/(2+1/2)). Даны числа а2 , а2 а к , описывающие цепную дробь. Написать про-грамму вычисления значения цепной дроби. Написать программу вычисления выражения (цепной дроби)

1 4 ' 1 0 1 1 —

103 9. Перевод десятичных дробей в двоичное представление вы-

полняется несколько иначе, чем целых чисел. Пусть есть дробь 0.37510. Схема перевода: 0.375*2 = 0.75, 0.75*2 1.5, 0.5*2 = 1.0, двоичное представление дроби 0.37510 имеет вид: 0.0112. Жирным шрифтом выделены те цифры, которые идут в двоичное представление дроби. После каждого умножения на 2 мы получаем очередную цифру в двоичной записи дроби (почему?). Всегда ли процесс умножения конечен? На какой итерации следует заканчивать перевод, если Вам дано m раз-рядов для представления дроби?

Написать программу перевода действительного числа а (0<а<1) в двоичную систему счисления.

Материал для чтения

1. Продолжим знакомство с кодированием. Рассмотрим не-сколько схем построения двоичных кодов для заданного мно-жества сообщений. Пусть имеется пять сообщений а, Ь, с, d, е, они передаются по каналу связи с вероятностями 0.3, 0.24, 0.21, 0.15 и 0.1. Закодируем их по методу американского мате-матика Р. Фано.

Page 198: Pascal 8 Класс - Окулов

198 Часть вторал

Сообщение Вероятность 1-й шаг

2 -й шаг

3-й шаг

Код

а 0 3 0 00 - 00

b 0 24 0 01 - 01

с 0 21 1 10 - 10

1 d 0 15 1 11 110 110

е 0 1 1 11 111 111

Что мы сделали? На первом шаге произвели разбивку всего множества сообщений на две группы так, чтобы суммарные ве-роятности сообщений в группах были как можно ближе друг к другу. Первая группа — сообщения а и Ь, вторая группа — со-общения с, d, е. Сообщениям первой группы приписали 0, вто-рой — 1. Процесс продолжается в каждой группе до тех пор, пока в очередной группе не останется одно сообщение. Почему именно такой принцип разбивки на группы? Вспомните игру «Бар — Кохба». Разряд, получаемый на первом шаге, «несет» в себе максимальное количество информации. Точно так же на втором и третьем шагах. Итак, очередной раз мы встречаемся с фундаментальной идеей информатики — деления пополам («раз-деляй и властвуй»).

При равномерном кодировании для пяти сообщений требует-ся три разряда, т. е. длина кода равна трем. Подсчитаем сред-нюю длину кода Р. Фано — L =1,*р1+12*р2+13*р3+14*р4+15*р5= 2*0.3 + 2*0.24 +2*0.21 + 3*0. i5 +3*0.1=2.25. Итак, получен выигрыш.

Структура данных, изображенная на рисунке, называется двоичным деревом. Черными кружками выделены узлы дере-ва. Отрезки, соединяющие узлы, называют ребрами. Ребрам приписаны цифры 0 и 1 так, как сделано в схеме кодирова-ния. Узлы, из которых не выходят ребра (вниз), называют ли-

Page 199: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 199

стьями. Итак , каждому листу соответствует сообщение, а путь к нему от корня (верхнего узла), при выписывании соответству-ющих цифр, дает код этого сообщения. Обратим внимание на одну особенность кодирования. Кодам соответствуют листья дере-ва, т.е. ни один код не является началом другого. Такие коды на-зываются префиксными. Код Р. Фано — префиксный код. Про-верьте. Если бы некоторому коду соответствовала внутренняя вершина дерева, то это условие не выполнялось бы.

Пусть есть последовательность кодовых слов, записанных подряд 010111110111111101011000. Попробуем декодировать это послание. Просматриваем последовательность слева напра-во и пытаемся выделить кодовое слово. 0 не является кодом, 01 — является , выделяем и отбрасываем. Продолжаем процесс: 0 1 I 0 1 I 111 I10 I 111 I 111 1 1 0 I 1 0 I 1 1 0 I 0 0 (символ I используем к а к разделитель). Сообщение декодировано — bbeceeccda.

2. Рассмотрим схему кодирования по К. Шеннону. Пусть есть п сообщения а : , а2 , ... ад, которые появляются с вероятно-стями p j , р2, ... рп. Вероятности сообщений даются в порядке возрастания — p t < р2 < р3 < ... < рп. Находятся частичные сум-мы вида S ^ Pj + р2 + ...+ р1 для всех i = l , 2, ...п. Затем опреде-ляются значения такие, что 2~ш < pt < 2"nl+1 . Частичные суммы S t представляют в виде двоичной дроби 0.d1d2d3d4 . . . Пер-вые цифр дроби дают кодовое слово для сообщения а ;. Значе-ние Sn равно 1 (полная группа сообщений, какое-нибудь да при-сутствует в передаче). Удобнее взять в схеме Шеннона Sn=0.999...

Прежде чем рассмотрим пример, напомним некоторые фак-ты из ранее рассмотренного материала. Значения отрицатель-ных степеней двойки равны: 2~1=0.5, 2"2=0.25, 2"3=0.125, 2"4= =0.0625, 2"5= 0.03125. Тогда для дроби 0.1 выполняются нера-венства 2"4 < 0 . 1 < 2~3 и значение равно 4. Правило перевода дробей в двоичное представление сводится к последовательно-му умножению на 2 и выделению целых частей результатов ум-ножения. Переведем 0.11 0 : 0.1*2=0.2, 0.2*2=0.4, 0.4*2=0.8, 0.8*2=1.6, 0.6*2=1.2, 0.2*2=0.4. Двоичное представление — 0.000110...2 .

Постройте двоичное дерево для полученного кода (код пре-фиксный). Отбрасывание лишних ребер приводит к усеченному коду.

Подсчитаем среднюю длину — L c p .=2*0.1 +3*0.15 +3*0.21 +2*0.24 + 2*0.3=2.36. Этот результат лучше, чем при равно-мерном кодировании сообщений.

Page 200: Pascal 8 Класс - Окулов

200 Часть вторал

Сооб

щени

е

Ве

ро

ятн

ость

S, п,

0 S 1

£ 0

1 а.

Ко

д

Усе

че

нн

ый

- !

ООО

а 0 1 4 0001 00

0 10 0 00011

Ь 0 15 3 010 010

0 25 0 01000

II С 0 21 3 011 011

0.46 00111

II d 0 24 3 101 10

0 70 0 1010

е 0 3 2 11 11

1 00(0 99) 0 1111

3. Рассмотрим еще одну схему построения кода. Ее предло-жил американский математик Д. Хаффмен (1953 г.). Получае-мый в результате построения код является оптимальным, т. е. не существует кодов с меньшим значением средней длины.

Пусть сообщения расположены в порядке убывания вероят-ностей их появления. Построение кода состоит из двух этапов. На первом сообщения с наименьшими вероятностями объеди-няются в одно. Получается новое множество сообщений. Осу-ществляется его перегруппировка так, чтобы опять их вероятно-сти находились в порядке убывания. Процесс, который называют сжатием, продолжается до тех пор, пока не останется два сооб-щения. Первому из оставшихся сообщений приписываем 0, второму — 1, и начинается процесс расщепления. Его суть. Если сообщение получено объединением двух в одно, то оно расщепляется на два, к текущему коду первого приписывается 0, второго — 1. Процесс продолжается до тех пор, пока мы не дойдем до первоначального множества сообщений. В результа-те будет получен код исходного множества сообщений. Для примера, описанного в предыдущих пунктах материала для чтения, схема построения кода Хаффмена приведена в таблице. Проверьте её. Убедитесь, что построенный код является пре-фиксным, а его средняя длина равна 2.25.

Page 201: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 201

Сооб

щени

е

Ве

ро

ятн

ост

ь

Пр

оц

есс

сж

ати

я

Пр

оц

есс

р

асщ

еп

ле

ни

я

а 0 3 0 3 0 45 0 55 0 1 00 00

Ь 0 24 0 25 0 3 0 45 1 00 01 10

с 0 21 0 24 0 25 - - 01 10 11

d 0 15 021 - - - - 11 010

е 0 1 - - - - - - 011

Page 202: Pascal 8 Класс - Окулов

202 Часть вторал

З а н я т и е № 16. Т е к с т о в ы е ф а й л ы

План занятия: • файловый тип данных; • операторы работы с файловым типом данных; • короткие программы иллюстрации работы с текстовыми

файлами; • экспериментальная работа с программами: подсчета час-

тоты записи целых чисел в файле; правильности расста-новки скобок в тексте программы; подсчета количества строк в тексте;

• выполнение самостоятельной работы. Файловый тип данных. Данные, размещаемые программой

в памяти компьютера, исчезают при выключении питания. Для долговременного хранения данных используются внешние но-сители, в частности магнитные диски. Единицей хранения ин-формации на диске служит файл. Файл имеет имя, но это имя не Турбо Паскаля, а операционной системы, под управлением которой работает Турбо Паскаль. С другой стороны, этот файл с точки зрения Турбо Паскаля является обычной переменной, иначе бы с ним нельзя было работать, и как любая переменная, он имеет имя. Это другое имя, это имя Турбо Паскаля, рассмат-ривающего файл как переменную. Естественно, чтобы не было «мешанины», должны быть средства установки соответствия между именами. Еще раз. В Турбо Паскале мы работаем с од-ним именем. Операционная система, фактически осуществляю-щая чтение и запись на диск, работает с другим именем. Следу-ющий основополагающий момент заключается в том, что с каждым файлом в конкретный момент времени можно рабо-тать или в режиме чтения, или в режиме записи. Одновременно нельзя, т. е. мы или читаем, или записываем — другого не дано. Для того чтобы, например, записать в файл, открытый для чтения, необходимо закончить работу с файлом в режиме чтения, а затем начать работу с этим файлом в режиме записи. В рамках данного занятия мы ограничимся работой с текстовы-ми или последовательными файлами. Текстовые файлы хранят информацию в виде последовательности символов. Символы со-ставляют строки произвольной длины. В конце каждой строки записываются два особых символа: # 1 3 # 1 0 (возврат каретки, перевод строки), которые отделяют строку от следующей. С текстовыми файлами, однако, мы работали, начиная с первого занятия, правда, не говоря об этом. Есть стандартные файлы ввода и вывода. Они связаны с клавиатурой и дисплеем. Имена

Page 203: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 202

этих ф а й л о в в системе Турбо П а с к а л ь (файловые переменные) Input и Output, и х и м я в операционной системе Dos —- Con (консоль) . Т а к и м образом, п р и записи операторов Read или Write ф а к т и ч е с к и осуществлялось чтение из стандартного фай-л а ввода с к л а в и а т у р ы и л и запись в стандартный файл вывода, с в я з а н н ы й с дисплеем. Естественный вопрос. Почти все в р е м я о с у щ е с т в л я л с я ввод и вывод цифровой и н ф о р м а ц и и . Оказыва-ется , при ч т е н и и и з аписи чисел их преобразование в строко-в ы й т и п и наоборот в ы п о л н я е т с я автоматически при работе с т е к с т о в ы м и ф а й л а м и , a Input и Output — текстовые ф а й л ы .

Операторы работы с файловым типом данных. В про-грамме н а Турбо П а с к а л е текстовый файл представлен фай-ловой переменной типа Text: Var <имя файловой переменной>: Text;.

Связь файловой переменной с дисковым именем ф а й л а осу-щ е с т в л я е т с я с помощью оператора Assign(имя файловой пере-менной, дисковое имя файла). Второе и м я больше нигде в про-грамме не появляется . Открытие файла для чтения выполняется о п е р а т о р о м Reset(<файловая переменная>), д л я з аписи — ReWrite(<файловая переменная>), д л я пополнения — Append (файловая переменная). Окончание работы с файлом фиксиру-ется с п о м о щ ь ю оператора Close(<файловая переменная>).

П р и м е р о ф о р м л е н и я работы с текстовым файлом: Var f:Text;

s: Strings-Begin Assign(f,'a:\data.txt');{*Файл с именем data, txt находится на диске с логическим именем а. *} Reset(f);{*Файл открывается для чтения. *} ReadLn(f,s);{*Читаем одну строку из файла. *)

Close ( f ) ; End. Текстовый файл д л я оператора ReadLn такой ж е источник

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

Page 204: Pascal 8 Класс - Окулов

204 Часть вторал

ние из ф а й л а . М ы не м о ж е м в начале прочитать 100-ю строку, а затем 10-ю. Последовательный файл , к а к лента магнитофона . П р и этом, конечно , м е ж д у элементами д а н н ы х ф а й л а обязаны быть разделители , ибо д а н н ы е ф а й л а могут быть разной д л и н ы и ф а й л обязан з а к а н ч и в а т ь с я к а к и м - т о п р и з н а к о м к о н ц а фай-ла . А к а к и н а ч е з а к о н ч и т ь чтение? В Турбо П а с к а л е есть специ-а л ь н а я ф у н к ц и я Eof (<файловая переменная>), которая воз-вращает значение True, если достигнут конец ф а й л а , и False в противном случае .

Короткие примеры. Н а ч н е м со сверхпростого примера . Вводим д а н н ы е (строки) из ф а й л а и выводим их н а экран .

Program Му16_1; Var f:Text;

s:String; Begin

Assign(f,'Input.Txt'); R e s e t ( f ) ; While Not E o f ( f ) Do Begin ReadLn ( f , s) ; Wri t eLn (s) ;

End; Close ( f ) ;

End. Запуск п р о г р а м м ы даст ошибку Error 2: File not found. Фай-

ла с именем Input.Txt нет на диске , его необходимо создать. От-крываем новый файл , записываем строки

А ВЬ Ссс Dddd Е е е е е F f f f f f G g g g g g g

и сохраняем ф а й л с именем Input.Txt. После запуска м ы видим на экране п р а в и л ь н ы й результат , но только при одном условии. В а ш а программа и файл Input.Txt находятся на одном и том ж е диске и в одной директории (каталоге) . Измените т е к у щ и й ка-талог — р е ж и м File/Change Dir — и выберите другой каталог . Запустите программу. Опять знакомая ошибка — Error 2: File not found. Ваша программа находится в одном каталоге, а файл Input.Txt в другом. Перепишите файл Input.Txt в тот каталог, где находится Ваша программа. Запустите. Результат нормальный.

Page 205: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 205

Измените программу. Вместо ReadLn(f,s) напишите Read(f.s). Результат плачевный. Приходится использовать Ctrl+Break, что-бы остановить программу, она «зациклилась» . П р и ч и н а — не может найти признак конца файла . Откройте окно Debug /Watch, введите и м я переменной s и выполните программу в пошаговом р е ж и м е . П е р в у ю строку м ы благополучно вводим и выводим, а затем идут пустые строки . Попробуйте дать объяснение. Обра-тите в н и м а н и е на неудобство работы. П р и х о д и т с я все время ис-пользовать F6 д л я того, чтобы перейти от одного окна к друго-му . Вы забыли материал занятия №3, работу с окнами. Измените р а з м е р ы окон и переместите их так , чтобы они не накладыва-лись друг на друга . Структура вашего экрана д о л ж н а иметь вид:

о к н о M y 1 6 _ 1 . P a s

о к н о I n p u t . T x t

о к н о M y 1 6 _ 1 . P a s о к н о M y 1 6 _ 1 . P a s

о к н о W a t c h e s

А м о ж н о л и читать посимвольно? Изменим программу. Program Му16_1т,

Var f-.Text; ch-.Chars-

Begin Assign(f,'Input.Txt'); Reset ( f ) ; While Not E o f ( f ) Do Begin Read ( f , c h ) ; Write(ch);

Ends-Close ( f ) ,-

End.

Page 206: Pascal 8 Класс - Окулов

206 Часть вторал

Р е з у л ь т а т п р а в и л ь н ы й . Н о если В ы и з м е н и т е Read(f,ch); Write(ch); на Read(f,s); Write(s); где s имеет т и п String, то ре-зультат о п я т ь о т р и ц а т е л ь н ы й — « з а ц и к л и в а н и е » . Осознайте р а з н и ц у . Н а й д и т е еще в а р и а н т ы , п р и к о т о р ы х В а ш а п р о г р а м м а будет « з а ц и к л и в а т ь с я » .

Проведите очередное и з м е н е н и е п р о г р а м м ы . Ее вид: Program Myl6_1тт;

Var f:Text; ch:Char;

Begin Assign (Input,'Input.Txt'); Reset (Input); While Not Eof Do Begin Read (ch); Write (ch) ;

End; Close (Input);

End. З а п у с к п р о г р а м м ы дает п о л о ж и т е л ь н ы й результат . Ч т о м ы

сделали? Переопределили с т а н д а р т н ы й ввод с к л а в и а т у р ы на ввод из н а ш е г о ф а й л а на диске . Ф а й л о в у ю переменную в этом случае м о ж н о не з а п и с ы в а т ь в операторах Read и Write. Доба-вьте после оператора Close(Input) операторы ReadLn(s); Wri-teLn(s), где s имеет строковый тип д а н н ы х . З а п у с к п р о г р а м м ы приводит к о ш и б к е Error 104: File not open for input (файл не открыт д л я ввода). Вставим после Close(Input) оператор Re-set (Input). П р о г р а м м а заработала . Однако вместо ввода с кла-в и а т у р ы строки s м ы имеем ввод первой строки из ф а й л а In-put.Txt. К а к вернуться к вводу с к л а в и а т у р ы ?

П р о д о л ж и м рассмотрение примеров . Запустите программу . Program Му16__2;

Uses Crt; Const MaxN=100; Type MyArray=Array[1.. MaxN] Of Integer; Var A:MyArray;

i,j:Integer; Begin

ClrScr; Assign (Input,'Input.Txt'); Reset (Input); i: =0 ; While Not Eof Do Begin

Page 207: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 207

Inc (i) ; Read (A[i ] ) ;

End; Close(Input); For j:=l To l Do Write (A [ j ] : 2) ;

End. Результат традиционный — ошибка Error 106: Invalid nume

ric format (неправильный числовой формат). Мы вводим в циф-ровой массив А, система преобразует символ в число, но символ не соответствует числу и к а к следствие — ошибка . Изменим файл Input.Txt на

1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 П р о г р а м м а работает правильно. В массиве А эти самые чис-

ла . И з м е н и м файл Input.Txt. 1 2 22 3 3 333 4 4 4 4444 5 5 5 5 55555 Все почти правильно. И последние записи в строках прави-

льно преобразовались в числа, правда, кроме 55555. Вместо этого числа выводится нечто странное - 9 9 8 1 . Если Вы хорошо усвоили материал предыдущих занятий, то суть ошибки оче-видна. Если нет, то перед программой вставьте {$R+} и проана-лизируйте ошибку . Вернемся к посимвольному вводу из файла.

Program Му1б_2т; Uses Crt; Const MaxN=100; Type MyArray=Array[1..MaxN] Of Integer; Var Л:MyArray;

l,j,k:Integer; ch: Chars-

Begin ClrScr; Assign(Input,'Input.Txt'); R e s e t ( Inpu t , ! ; l: =0 ;

Page 208: Pascal 8 Класс - Окулов

208 Часть вторал

While Not Eof Do Begin Read(ch); {Val (ch,j ,k) ;) ( I f k=0 Then Begin) Inc(i);

A [ l ] : =Ord (ch) -0rd('0') ; (End;)

End; Close(Input); For j:=l To l Do Write (A[j] : 6) ;

End. Р е з у л ь т а т — с т р а н н а я последовательность : 1 - 3 5 - 3 8 2 - 1 6

2 2 - 3 5 - 3 8 3 но с определенной закономерностью. Объяс-ните ее. Уберите ф и г у р н ы е скобки и вновь запустите програм-му. Результат : 1 2 2 2 3 3 3 3 3 . . . И з м е н и т е программу так , что-бы результат все ж е был п е р в о н а ч а л ь н ы м 1 2 22 3 3 333 ...., но ввод о с у щ е с т в л я л с я посимвольно. А м о ж н о ли т а к и м ж е обра-зом вводить вещественные ч и с л а ? В программе Му16_2 изме-ните Type MyArray=Array[l..MaxN] Of Real; и проведите ана-логичные э к с п е р и м е н т ы , естественно, и з м е н и в п р и этом и файл Input.Txt.

В следующем примере показано переопределение выходного файла . После запуска п р о г р а м м ы на экране м о н и т о р а ничего нет. Необходимо о т к р ы т ь ф а й л Output.Txt. Ра зместите на экра-не все три ф а й л а Му16_3, Input.Txt, Output.Txt (после первого прогона п р о г р а м м ы на диске в т е к у щ е м каталоге будет создан выходной файл) так , чтобы они б ы л и одновременно обозримы. Измените несколько раз ф а й л Input.Txt, п р и этом з а п у с к а я программу.

Program Myl6_3; Uses Crt; Var ch:Char ; Begin

ClrScr; Assign(Input,'Input.Txt'); Reset (Input); Assign(Output,'Output.Txt'); Rewrite(Output); While Not Eof Do Begin Read(ch); Write(ch)

End; Close(Input); Close(Output);

End. После строки с операторами Close( Input); Close (Output); вста-

вьте следующие операторы.

Page 209: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 209

Assign(Input,'Con'); Assign(Output,'Con'); Reset (Input); Rewrite(Output); ReadLn (s) ; WriteLn (s);

Если п р о г р а м м а будет что-то ждать , то наберите произволь-н ы й текст на клавиатуре и н а ж м и т е к л а в и ш у Enter. Мы научи-лись с В а м и возвращаться к стандартному вводу и выводу дан-н ы х .

Экспериментальный раздел работы

1. Известно , что в файле з а п и с а н ы целые числа , например из д и а п а з о н а от - 1 0 0 до 100. Необходимо подсчитать, сколько раз к а ж д о е число встречается в файле .

Д л я р е ш е н и я задачи требуется создать файл с ц е л ы м и чис-лами . Попробуем это сделать с помощью следующей програм-мы.

Program Му16__4 ; Const MaxN=10000;

а=100; Ъ=2С0; с=15; Var 1,j , к:Integer; Begin

Randomize; A s s i g n ( O u t p u t , ' N u m b e r . T x t ' ) ; Rewrite(Output); l:=0; к:=0; While KMaxN Do Begin j:=Integer(Random(b))-a; Inc ( l ) ;Inc (k) ; Write ( j ) ; Write (' ' ) ; I f k=c Then Begin WriteLn;k:=0;End;

End; Close(Output);

End. Она работает. Ф а й л Number.Txt создается в текущем катало-

ге. Он содержит MaxN ц е л ы х чисел, разбитых на строки по с чисел в к а ж д о й строке. Можно ли записать в файл 100000 чи-сел? Простое изменение константы MaxN приводит к зацикли-ванию. П р и ч и н а традиционная , м ы забыли изменить тип дан-н ы х у переменной i на Longlnt. После каждого числа в файл

Page 210: Pascal 8 Класс - Окулов

210 Часть вторал

з а п и с ы в а е т с я с и м в о л пробела и после с ч и с е л о с у щ е с т в л я е т с я переход на новую строку . Обязательно л и сие? И з м е н и м про-г р а м м у , в о з ь м и т е в ф и г у р н ы е с к о б к и текст {Write(' '); If k=c Then Begin WriteLn;k:=0;End;} п р о г р а м м н о г о кода . П р о г р а м м а радует нас н о в ы м и с о о б щ е н и я м и , они в ы д а ю т с я на с т а д и и вы-п о л н е н и я п р о г р а м м ы и о ф о р м л е н ы н е с к о л ь к о иначе , чем про-стые с и н т а к с и ч е с к и е о ш и б к и (обратите на этот ф а к т внима-ние) . Первое сообщение : Error. Line too long, truncated ( строка очень б о л ь ш а я , она о к р у г л я е т с я ) ; второе сообщение , но у ж е не к а к о ш и б к а , а к а к п р е д у п р е ж д е н и е : Warning. Error encountered reading file NUMBER.TXT ( о ж и д а е т с я о ш и б к а п р и ч т е н и и фай-ла) . В е р н е м с я к и с х о д н о м у тексту п р о г р а м м ы , а в качестве у п р а ж н е н и я попробуйте с ф о р м и р о в а т ь ф а й л , у которого и ко-личество ч и с е л в строке ф о р м и р у е т с я с л у ч а й н ы м образом.

В т о р а я часть з а д а ч и р е а л и з у е т с я с п о м о щ ь ю с л е д у ю щ е й про-г р а м м ы .

Program Му16_5; Uses Crt; Const MaxN=l00;c=20; Type MyArray=Array[-MaxN..MaxN] Of Integer; Var A:MyArray;

l,3:Integer; Begin

ClrScr; FillChar(A,SizeOf(A),0);{*Подсчитываем, сколько раз каждое число встречается в файле. Число используется как индекс к элементу массива А. Обратите на этот факт особое внимание. Это «принцип косвенной адресации» — одна из ключевых идей информатики, реализован как на уровне аппаратуры компьютера, так и в любых системах программирования. *) Assign(Input,'Number.Txt'); Reset (Input); While Not Eof Do Begin Read(i);Inc(A[i]);End; Close (Input); Assign(Output,'Count.Txt');{*3аписываем результат в файл Count.Txt, не разбивая на строки. *) Rewrite(Output); For i:=-MaxN To MaxN Do Write (A [ i ] , ' ' ) ; Close(Output);

Page 211: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 211

Assign(Input,'Count.Txt');{*Открыв а ем файл Count.Txt для чтения.*} Reset(Input); Assign(Output,'Con');{*Результат выводим на экран. *1 Rewrite(Output); j:=0; While Not Eof Do Begin

Read (1} ;Inc ( j ) ; Write (1,' ' ) ; I f j=c Then Begin WriteLn;j:=0;End;{*Разбиваем на строки, иначе трудно просматривать.*}

End; End.

М е т о д и ч е с к о е з а м е ч а н и е На экране должны быть обозримы все файлы: с первой и второй программами; Number.Txt и Count.Txt.

В программе , хотя она и работает, содержится я в н а я неточ-ность. Устраните ее. Вспомните, что В а ш и программы д о л ж н ы работать при всех допустимых исходных данных . Предполо-ж и м , что датчик с л у ч а й н ы х чисел дал сбой и сгенерировал 10000 раз одно число. Что произойдет в этом случае?

Подсчитать , сколько раз встречается к а ж д ы й символ в соот-ветствии с кодировкой ASCII в некотором текстовом файле .

Необходимо изменить решение задачи так , чтобы она рабо-тала и с вещественными числами . Если создание файла Num-ber.Txt не вызывает затруднений, то логика подсчета частоты повторяемости к а ж д о г о числа «расползается». Использовать вещественное число в качестве индекса нельзя , косвенная адре-с а ц и я не работает. Требуется брать б л и ж а й ш е е целое число, но это у ж е другая задача. В этом случае вещественные числа из единичного интервала представлены одним ц е л ы м числом. А если уменьшить интервал? Попробуйте исследовать эту пробле-му.

Создается файл с не очень большим количеством слов. Затем формируется массив из этих слов. Случайным образом из мас-сива выбирается слово и записывается в другой файл, это дей-ствие выполняется 10000 раз. После того, к а к файл сформиро-ван, Вам необходимо решить аналогичную задачу — подсчитать, сколько раз встречается к а ж д о е слово и результат записать в третий файл .

Page 212: Pascal 8 Класс - Окулов

212 Часть вторал

2. Д а н т е к с т о в ы й ф а й л , с о д е р ж а щ и й программу на я з ы к е Турбо П а с к а л ь . П р о в е р и т ь правильность расстановки скобок (круг-л ы х , к в а д р а т н ы х , ф и г у р н ы х ) в тексте этой п р о г р а м м ы . П р и в е д е м п р о с т ы е п р и м е р ы д л я п о я с н е н и я сути з а д а ч и : • (()) — с к о б к и в строке р а с с т а в л е н ы п р а в и л ь н о ; 1=1 {(] } — с к о б к и в строке р а с с т а в л е н ы н е п р а в и л ь н о ; Q ({[}]) — с к о б к и в строке р а с с т а в л е н ы н е п р а в и л ь н о .

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

Program Myl6_sk; Begin

WriteLn (y(*9 ( ( ( ) ) ) [ [ [ [ ] ] ] ] * } ' ) ; End. Д а л е е в ы я с н и м , к а к о т л и ч а ю т с я к о д ы (по ASCII) рассматри-

в а е м ы х в з а д а ч е скобок . Этот ф а к т у с т а н а в л и в а е т с я с п о м о щ ь ю с л е д у ю щ е й простой п р о г р а м м ы .

Program Му16_р; Uses Crt; Begin

ClrScr; WriteLn (OrdC ( ' ) ) ; WriteLn (OrdC ) ' ) ) ; { *Ответ: 40, 41.*} WriteLn (OrdC [ ' ) ) / WriteLn (OrdC ] ' ) ) ; (*Ответ: 91, 93. *} WriteLn (OrdC ( ' ) ) ; WriteLn (OrdC } ' ) ) ; (*Ответ: 123, 125.*} ReadLn;

End.

И т а к , разность кодов д л я соответствующих друг другу ско-бок не п р е в ы ш а е т з н а ч е н и я 2. С л е д у ю щ и й момент . Х р а н и т ь весь текст и з ф а й л а в п а м я т и нецелесообразно , он м о ж е т б ы т ь очень б о л ь ш и м , да это и не требуется в задаче . Необходимо хра-нить только о т к р ы в а ю щ и е скобки . Определим д л я этой цели массив с т и п о м элементов Char и п р е д п о л о ж и м , что уровень в л о ж е н н о с т и скобок не п р е в ы ш а е т 100. Е с л и очередной считан-н ы й из ф а й л а с и м в о л — о т к р ы в а ю щ а я скобка , то она записы-вается в массив . П р и о б н а р у ж е н и и з а к р ы в а ю щ е й с к о б к и срав-

Page 213: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 213

н и в а е т с я её код с кодом последней скобки, записанной в массив. Е с л и з н а ч е н и е разности больше 2, то ф и к с и р у е т с я о ш и б к а в расстановке скобок , и н а ч е скобка у д а л я е т с я из массива , и про-смотр текста п р о д о л ж а е т с я .

($R+) Program Му16_6;

Const MaxN=l00 ; Type MyArray=Array[1. .MaxN] Of Char; Var A:MyArray;

ch:Char; yk:Integer; ff:Boolean;

Begin FillChar(A,SizeOf(A),0);yk:=0;ff:=True; Assign(Input,'MY1 6_SK.PAS'); Reset (Input);

While (Not Eof) And f f Do Begin Read (ch); I f (ch='C) Or (ch='[') Or (ch='{') Then Begin Inc(yk);A[yk]:=ch; End Else I f (ch = ' ) ' ) Or (ch=']') Or (ch='}') Then

I f Ord (ch)- Ord(А[ук])<=2 Then Dec(yk) Else ff:=False;

End; Close (Input); I f f f Then WriteLn ( 'Скобки расставлены правильно') Else WriteLn ( 'В тексте программы есть ошибка в расстановке скобок');

End. На экране обозримы две программы — последняя и Myl6_sk.

М о ж н о п р и с т у п и т ь к экспериментам . Вносите изменение во вторую программу , с о х р а н я я текст после каждого изменения , переходите в другое окно , делайте запуск программу и произво-дите а н а л и з результата . Например , п р и некоторых исходных д а н н ы х возникает о ш и б к а Error 201: Range check error. Про-г р а м м а содержит логическую неточность. Устраните её.

Измените программу так , чтобы, например, проверялась пра-вильность з аписи слова WriteLn в тексте программы. Приду-майте еще ряд м о д и ф и к а ц и й программы.

Page 214: Pascal 8 Класс - Окулов

214 Часть вторал

3. В ы з а п и с а л и с л е д у ю щ у ю п р о г р а м м у на д и с к под и м е н е м MY 16J7.PAS. В программе по дочитывается количество строк в т е к с т о в о м ф а й л е , к а ж д а я с т р о к а с о х р а н я е т с я к а к элемент массива д л я последующего а н а л и з а , с троки в ы в о д я т с я на эк-ран . М ы п о л у ч и л и один и з вариантов п р о г р а м м ы вывода соб-ственного т е к с т а на э к р а н , п р и ч ё м не л у ч ш и й . В а ш а ц е л ь — п р е д л о ж и т ь наибольшее количество м о д и ф и к а ц и й задачи, ес-тественно, с и х р е а л и з а ц и я м и . Program Му16_7;

Const MaxN=l00 ; Type MyArray=Array[l..MaxN] Of S t r i n g ; Var A:MyArray;

cnt,i:Integer; Begin

cnt:=0; Assign(Input,'MY16_7.PAS'); Reset (Input);

While Not Eof Do Begin Inc (cnt); ReadLn (A[ cnt] ) ; End;

Close(Input); WriteLn (cnt) ; For i:=l To cnt Do WriteLn (A [ i ] ) ;

End. П р о г р а м м ы , в ы в о д я щ и е на э к р а н свой собственный текст ,

н а з ы в а ю т и н т р о с п е к т и в н ы м и . П о л ь з о в а т ь с я д л я этой ц е л и хра-н я щ е й с я на д и с к е к о п и е й текста з а п р е щ е н о . Т а к и м образом, н а ш а п р о г р а м м а не я в л я е т с я и н т р о с п е к т и в н о й . Эта тема была одно в р е м я довольно п о п у л я р н а в к о м п ь ю т е р н о й л и т е р а т у р е . Попробуйте свои с и л ы в р е ш е н и и д а н н о й п р о б л е м ы .

Задания для самостоятельной работы

1. Д а н т е к с т о в ы й ф а й л , с о д е р ж а щ и й ц е л ы е ч и с л а . Н а й т и • к о л и ч е с т в о чисел в файле ; • м а к с и м а л ь н ы й элемент в ф а й л е , в к а ж д о й строке ; • с у м м у ч и с е л в ф а й л е , в к а ж д о й строке ; • разность м е ж д у м а к с и м а л ь н ы м и м и н и м а л ь н ы м элемен-

т а м и в ф а й л е , в к а ж д о й строке; D а р и ф м е т и ч е с к о е среднее ч и с е л в ф а й л е , в к а ж д о й строке ; • н о м е р м а к с и м а л ь н о г о элемента в ф а й л е ; • сумму м а к с и м а л ь н ы х элементов в файле ; • с у м м у ч е т н ы х чисел в ф а й л е .

Page 215: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 215

2. Дан текстовый файл, содержащий строки. Найти • количество строк, начинающихся с заглавных латин-

ских букв; • количество строк, начинающихся и заканчивающихся

одинаковыми символами; • самые короткие строки; • симметричные строки (палиндромы).

3. Дан текстовый файл. Вставить в начало каждой строки ее но-мер и записать преобразованные строки в новый файл.

4. Даны два текстовых файла. Записать в третий только те стро-ки, которые есть и в первом и во втором файлах.

5. Дан текстовый файл. Дописать в его конце следующие дан-ные: количество строк, количество символов в каждой стро-ке, количество чисел в каждой строке.

6. Даны два файла А и В (тип элементов одинаковый). Поменять местами содержимое этих файлов. Использовать процедуру Rename не разрешается.

7. Содержимое текстового файла копируется в другой файл, при этом каждая строка циклически сдвигается вправо на п сим-волов. Пример циклического сдвига строки abcdefqwrt на 3 символа, результат — wrtabcdefq. То же самое, но только каж-дое слово сдвигается на половину своей длины.

8. Дано некоторое конечное множество слов. Исключить их из текстового файла. Например, из файла с текстом программы исключить все слова Begin и End.

9. Считаем, что длина строк текстового файла не превышает 80 символов. Преобразовать файл так, чтобы все строки были от-центрированы: более короткие строки дополняются символа-ми пробела, текст строки размещается по ее центру.

10. Дан файл, в котором встречаются даты. Каждая дата — это число, месяц и год (например, 13.05.1949 г.). Найти наимень-шую дату.

Материал для чтения

1. В предыдущем материале для чтения мы рассмотрели раз-личные схемы построения двоичных кодов. Однако все они не являются помехоустойчивыми. Искажение в любом разряде приводит к тому, что декодировать код не представляется воз-можным. Проверьте это утверждение. Замените в произволь-ном разряде последовательности 010111110111111101011000 значение на противоположное, например в третьем. Получаем 011111110111111101011000.

Page 216: Pascal 8 Класс - Окулов

216 Часть вторал

Декодируем — 01 I 111 I 111 I 01 I 111 I 111 I 011 01 110 I 00, что соответствует beebeebbca. Это послание не соответствует истин-ному, а именно — bbeceeccda. Единственный путь решения проблемы построения помехоустойчивых кодов — это введение дополнительных разрядов, другого не дано. Вернемся к равно-мерному кодированию. Пусть для кодирования сообщений ис-пользуется четыре разряда. Очевидно, что кодируются только 16 различных сообщений. Введем один дополнительный раз-ряд — пятый. В этот разряд записываем 1, если в коде нечет-ное число единиц и 0, если четное. В таблице символом I обо-значены столбцы с исходными кодами, символом II — столбцы с соответствующими кодами, но дополненные пятым разрядом. В канал передачи идут не четыре разряда, а пять. II II I II I II I И

0000 00000 0100 10100 1000 11000 1100 01100

0001 10001 0101 00101 1001 01001 1101 11101

0010 10010 0110 00110 1010 01010 1110 11110

рои 00011 p i n Ю111 ю н п о п n i l ~ o i i n I] Что дает введение одного разряда? Пусть передавался код

10010, произошло искажение в одном разряде, например в пер-вом, и принят код 10011. Подсчитываем число 1, их нечетное количество. Делаем вывод о том, что произошла ошибка в пере-даче. Установить, в каком разряде нам не под силу, информа-ции недостаточно. А если искажениям подверглись два разря-да, например первый и четвертый? Принят код 11011. Число единиц четно, по нашим правилам «игры» мы должны считать этот код правильным. Итак, с помощью одного дополнительно-го разряда возможно фиксирование факта ошибки в нечетном количестве разрядов и только.

Рассмотрим другую схему. Каждый разряд сообщения дуб-лируем т раз ( т — нечетно). Пусть m равно пяти. Тогда 10010 передается как 1111100000000001111100000. Ни о какой экономии в этом случае речь не идет, сплошное расточитель-ство. Произошли искажения и принята последовательность 1100100011001001101100011. Число m известно и в приемном устройстве. Наши действия. Считаем в каждой группе из m разрядов количество 1 и 0 и исправляем разряды по принципу «чего больше». Исправленная последовательность имеет вид: 1111100000000001111100000. Сообщение восстановлено. Мы рассмотрели два крайних случая — один дополнительный разряд и много дополнительных разрядов. Оба они неудовлетво-

Page 217: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 217

рительны. В первом случае фиксируется одиночная ошибка, о восстановлении сообщения речи не идет, во втором сообщение восстанавливается за счет сверхизбыточности, сверхрасточитель-ности. Истина, как обычно, находится посередине.

2. Рассмотрим на примере построение помехоустойчивого кода по Р. В. Хэммингу (1950 г.), способного обнаруживать и исправлять одиночные ошибки. Но прежде попытаемся дать со-держательную интерпретацию решаемой задачи. Пусть для ко-дирования используются т разрядов. Как мы знаем, различ-ных значений, представимых m разрядами — 2т (обозначим это множество буквой S). Расстояние по Хеммингу (р(х,у)) между двумя различными элементами S, — это количество раз-рядов, в которых они отличаются друг от друга. Пример: т=4, х=1001, у=0101, р ( х , у ) = 2 ; х=0000, у=1111, р(х,у)=4. Априори известно, что в канале не может произойти больше t ошибок. Другими словами, расстояние по Хеммингу между истинным и искаженным кодами не может превышать значения t. В качест-ве кодов среди 2т элементов множества S следует выбирать та-кое подмножество элементов (обозначим буквой W), что между любыми двумя его элементами расстояние по Хеммингу дол-жно превышать значение 2*t. В этом случае искажение ис-тинного кода не приводит к неоднозначности. Для любого ис-

каженного кода существует только один истинный код, расстояние меж-ду которыми меньше t. Проиллюст-рируем сказанное рисунком, точка-ми обозначены элементы множества S, точками в кружках и соединен-ных «жирными» отрезками — эле-менты множества S. Расстояние по Хеммингу между любыми двумя эле-ментами S больше 2*t. Искажения

оставляют код в t-окрестности, к р у ж к е , и искаженному коду соответствует один истинный.

Продолжим рассмотрение примера. Пусть т=4 и t=1. Сколь-ко дополнительных разрядов (k) необходимо ввести, чтобы уметь устранять одиночную ошибку? Эти разряды обычно называют проверочными, или контрольными. Значение k определяется из неравенства 2k>m+k+l. Ошибка может произойти как в кон-трольных (р), так и в информационных (т) разрядах, плюс 1 учитывает случай отсутствия ошибок. Для нашего примера т = 4 , 2 3 =4+3+1. Принцип формирования контрольных разря-дов — дополнение до четного числа единиц определенной груп-

Page 218: Pascal 8 Класс - Окулов

218 Часть вторал

пы разрядов кода. В примере рассматриваются 3 группы, ибо контрольных разряда три. В результате проверки в приемном устройстве каждой из этих групп на четность числа единиц должен формироваться номер разряда, в котором произошла ошибка. На рисунке в овалах указаны разряды из одной груп-пы. Надписи «да», «нет» говорят о том, есть или нет ошибка в этих разрядах . В прямоугольниках указан номер искаженного разряда, 0 — нет ошибки.

Осталось решить проблему — где в семи разрядах размещать контрольные разряды, а где информационные. В первой группе 1, 3, 5, 7-й разряды. Три их них должны быть информацион-ные, один контрольный. Пусть он седьмой, в нем фиксируется четность числа единиц в 1, 3, 5-м разрядах. Следующий конт-рольный разряд шестой, фиксируется четность числа единиц во 2, 3, 7-м разрядах, и наконец, третий контрольный разряд раз-мещаем на четвертом месте — четность числа единиц в 5, 6, 7-м разрядах. Кодировка цифр от 0 до 9 по Хеммингу приведе-на в таблице. Значения контрольных разрядов формируются по значениям информационных и уже сформированных контроль-ных в следующей очередности рх, р2 , р3 .

Номер разряда 1 при перед, аче сообщения

7 1 6 I 5 I 4 I з I 2 I 1 Размещение контрольных и информационных разрядов 1

Pi Р2 т 4 Рз т 3 т 2 т , 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 1

Page 219: Pascal 8 Класс - Окулов

Процедуры и функции -— элементы структуризации программ 219

1 0 0 1 1 0 0 1 0 1 1 0 1 1 1 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0

° 0 1 1 0 0 1 I

Предположим, что передается цифра 5. В кодировке Хем-минга она записывается как 0101101. Ошибка произошла в третьем разряде, т. е. принят код 0101001. Вычисляем значе-ния контрольных разрядов. Сумма единиц в 1, 3, 5-м и 7-м раз-рядах нечетна — есть ошибка. Проверяем 2, 3, 6, 7-й разряды. Сумма единиц нечетна — есть ошибка. Проверяем сумму еди-ниц в 4, 5, 6-м и 7-м разрядах — нет ошибки. Если слова «есть ошибка» заменить на 1, а слова «нет ошибки» на 0 и записать 011 2

= 3 1 0 , то получим номер разряда, в котором данные искаже-ны. Выполните еще несколько примеров подобного типа, чтобы полностью понять кодирование по Хеммингу.

Page 220: Pascal 8 Класс - Окулов

Часть третья

М а с с и в — фундаментальная структура д а н н ы х

З а н я т и е № 1 7 . М е т о д ы р а б о т ы с э л е м е н т а м и о д н о м е р н о г о м а с с и в а

План занятия • соревнование на написание коротких программ работы с

элементами массива; • экспериментальная работа с программами вставки и уда-

ления элементов массива; • выполнение самостоятельной работы. Короткие примеры. В этих примерах используется следую-

щее описание данных: Const MaxN= ;(*Максимальное количество элементов в

массиве. *) Type MyArray= Array[l..MaxN] Of I n t e g e r ; . А основная программа может иметь вид Begin

Init(m,X);1*Ввод элементов массива и инициализация глобальных переменных. *} SolveProc(<параметры>);{*Вызов основной процедуры решения задачи.*} Print(m,X);(*Вывод результата. *} ReadLn;

End. Если для решения задачи необходимо использовать функ-

цию, то возможен такой вид основной логики. Begin

Init(m,X);{*Ввод элементов массива и инициализация глобальных переменных. *) WriteLn(SolveFun(<параметры>));[*Вызов функции, если результат ее действий выводится с помощью одного оператора WriteLn.*} ReadLn;

End.

Page 221: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 221

1. З а м е н и т ь о т р и ц а т е л ь н ы е элементы массива на противопо-л о ж н ы е по з н а к у .

Procedure NegPos(п:Integer;Var A: MyArray); Var 1: Integer; Begin

For i:=1 To n Do I f A[i]<0 Then A[i]:=-A[i]; End;

2. П р и б а в и т ь к к а ж д о м у элементу массива число 25. Procedure Add25 ( п:Integer;Var A:MyArray); Var i: Integers-Begin

For i:=l To n Do A[i] :=A[i] + 25; End;

3. Е с л и элемент ч е т н ы й , то прибавить к нему первый, если не-ч е т н ы й — последний элементы массива. П е р в ы й и послед-н и й элементы не и з м е н я т ь .

Procedure AddFirstLast(п:Integer/ Var A: MyArray); Var l: Integer; Begin

For l:=2 To n-1 Do I f A[l] Mod 2=0 Then A[i]:=A(i] + A[l] Else A[i] :=A[i]+A[n] ;

End;

4. Д а н ы два одномерных массива одинаковой размерности. По-лучить третий массив такой ж е размерности, к а ж д ы й эле-мент которого равен сумме соответствующих элементов дан-н ы х массивов. Procedure AddArray(п:Integer;A,B:MyArray; Var С: MyArray ); Var i: Integer; Begin

For l: =1 To n Do С[l] : =A[i J +B[l] ; End; Д а н ы два целочисленных массива, состоящие из одинаково-го ч и с л а элементов. Получить третий массив той ж е размер-ности, к а ж д ы й элемент которого равен наибольшему из со-ответствующих элементов данного массива. Procedure AddMaxArray (п:Integer;A,B:MyArray; Var С: MyArray ); Var i: Integer; Begin

Page 222: Pascal 8 Класс - Окулов

222 Часть третья

For i:=l То п Do I f A[i]>B[i] Then С[l]:=A[1] Else C[i]:=B[i];

End; 5. Д а н п е р в ы й элемент а р и ф м е т и ч е с к о й п р о г р е с с и и и р а з н о с т ь

м е ж д у с о с е д н и м и э л е м е н т а м и . С ф о р м и р о в а т ь о д н о м е р н ы й м а с с и в и з п е р в ы х п э л е м е н т о в а р и ф м е т и ч е с к о й п р о г р е с с и и . П у с т ь one — п е р в ы й элемент п р о г р е с с и и , a k — р а з н о с т ь . Е с л и известно з н а ч е н и е э л е м е н т а с н о м е р о м i—l, то э л е м е н т с н о м е р о м i в ы ч и с л я е т с я по п р а в и л у : a=au+k, и л и а=опе+ +k*(i—l). Procedure FormProg (one, к, n: Integer; Var A: MyArray); Var 1: Integer; Begin

A[l]:=one; For l : =2 To n Do A[ l ] : =A [ l - l ] +k ;

End; Д л я геометрической прогрессии очередной элемент вычисля -ется по п р а в и л у a=ail*k, где k — з н а м е н а т е л ь прогрессии . Н а й т и с у м м у п е р в ы х п э л е м е н т о в п р о г р е с с и и . Function FormProgm(one, к, п: Integer):Longlnt; Var i , t : Integer;sum:Longlnt; Begin

sum:=one;t:=one; For l:=2 To n Do Begin t:=t*k; Inc(sum,t);End; FormProgm:=sum;

End; 6. Д а н ы д в а о д н о м е р н ы х м а с с и в а А и В. Н а й т и и х с к а л я р н о е

произведение . С к а л я р н ы м произведением двух массивов оди-наковой размерности называется сумма произведений соответ-ствующих элементов. Она записывается в виде: А[1]*В[1]+ +А[2]*В[2] +... + А[п-1 ]*В[п-1 ] + А[п]*В[п], где п — это к о л и ч е с т в о элементов в м а с с и в а х .

Function Product(л:Integer;А,В: MyArray): Longlnt; Var i: Integer;s: Longlnt; Begin

For i : =1 To n Do s :=s+A[i] *B[i] ; Product:=s/

End;

Page 223: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 223

7. Н а й т и м а к с и м а л ь н ы й по м о д у л ю элемент массива . Function MaxAbs(п:Integer; A:MyArray):Integer;

Var l,max:Integer; Begin

m a x : = A b s (A[1]); For l : =2 To n Do I f Abs (A [l ] ) >max Then max:=Abs (A[i]); MaxAbs:=max;

End;

Н а й т и среднее а р и ф м е т и ч е с к о е з н а ч е н и е элементов . Function Average(п:Integer; A:MyArray):Real;

Var i,sum:Integer; Begin sum:=A[1]; For l:=2 To n Do Inc(sum,A [l] ) ; Aver age:=sum/n;

End;

И з м е н и т ь з н а к у м а к с и м а л ь н о г о по модулю элемента масси-ва .

Procedure SignMaxElem(п:Integer; Var A:MyArray); Var l,j,Max:Integer; Begin

Max:=Abs(A[1]);j:=1; For l: =2 To n Do I f Abs (A [l ] ) >Max Then

Begin Max:=Abs(A[i]);j:=i;End; A[J]:=-A[J] ;

End; 8. Все ч е т н ы е э л е м е н т ы массива возвести в квадрат , а нечет-

н ы е удвоить . Procedure MaxEven(п:Integer; Var A:MyArray);

Var i:Integer; Begin

For i:=1 To n Do I f A[i ] Mod 2 =0 Then A[i] :=A[i] *A[i] Else A[i] :=2*A[i] ;

End; 9 . И з п о л о ж и т е л ь н ы х элементов массива вычесть элемент с но-

мером k j , а о т р и ц а т е л ь н ы е у в е л и ч и т ь на значение элемента с номером к 2 , нулевые элементы оставить без изменения .

Page 224: Pascal 8 Класс - Окулов

224 Часть третья

Procedure ChangeElem(п,kl,к2:Integer; Var A .-MyArray) ;

Var l,w,v:Integer; Begin w:=A[kl];v:=A[k2]; For 1:=1 To n Do

I f A[i] >0 Then A[i]:-A[i]+w Else I f A[i]<0 Then A[i] :=A[i]-v;

End; Операторы w : = A [ k l ] v :=A[k2] обязательны. Использование к о н с т р у к ц и и I f A[i] >0 Then Ah] :=A[i]+A[kl] Else I f A[i]<0 Then All]:=A[i]-A[k2]; приводит к н е ж е л а т е л ь н ы м последствиям. Проверьте .

10. И з элементов массива А сформировать элементы массива В по правилу : B[ i ] :=A[l ]+A[2]+. . .+A[i ] . Procedure SumElem(п:Integer; А:МуАггау;Var В:МуАггау);

Var l:Integer; Begin В [1 ] : =А[1 ] ; For i: =2 То п Do В [i ] : =В [i -1 ] +А [ 1 ] ;

End;

Экспериментальный раздел работы

1. Удалить из массива последний (в том случае, если их неско-лько) м а к с и м а л ь н ы й элемент. Д л я решения задачи необхо-димо: О найти номер максимального элемента — k; • сдвинуть все элементы, н а ч и н а я с fe-ro, на один элемент

влево; • последнему элементу присвоить значение 0. ($R+) Program Myl 7_1 ;

Const MaxN^lO;la=-10;ha=21;{*Размер массива и интервал значений. *) Type MyArray=Array[1..MaxN] Of Integer; Var A .-MyArray; k:Integer; Procedure Init(t,v,w:Integer;Var X:MyArray);l'Напомним процедуру формирования значений элементов массива. *}

Page 225: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 224

Var 1:Integer; Begin

Randomize; For l: =1 To t Do X[i] :=v+Integer(Random(w)) ;

Ends-Procedure Print(t:Integer;X:MyArray);(*Вывод t первых элементов массива X. *)

Var l:Integer; Begin

For l:=1 To t Do Write(X[i] :5) ; WriteLn; Ends-

Function Max(t:Integer;X:MyArray) -.Integer; (*Поиск номера последнего максимального элемента. *}

Var i,Mx: Integers-Begin Mx:=l; For l: =2 To t Do I f X[i]>=X[Mx] Then Mx:=i; Max:=Mx;

Ends-Procedure Sz(t,q:Integer;Var X:MyArray);(*Сдвиг элементов массива влево на одну позицию, начиная с элемента с номером q+1. *}

Var i:Integer; Begin

For i:=q To t-1 Do X[l]:=X[i+1]; X[t]:=0;

Ends-Begin

n : =MaxN; Imt (n, la, ha, A) ; WriteLn( 'Исходный массив.'); Print(n,A); k:=Max(n,A); Sz(n,k,A); WriteLn(Массив после удаления элемента.') ; Print (п-1,А) ; ReadLn;

End. Предположим, что максимальный элемент встречается не-

сколько раз . Во-первых, следует изменить функцию М а х . Тре-буется находить не номер максимального элемента, а его значе-ние.

Page 226: Pascal 8 Класс - Окулов

225 Часть третья

Function Maxft:Integer;X:MyArray) . - I n t e g e r ; Var l,Mx:Integer; Begin

Mx:=X[l]; For i:=2 To t Do I f X[i]>Mx Then Mx:=X[i] ; Max:=Mx;

End; З а т е м повторно просматриваем массив, у д а л я я элементы,

равные м а к с и м а л ь н о м у , и у м е н ь ш а я количество элементов в массиве.

Procedure Solve(Var t: Integer;Var X:MyArray); Var k,l:Integer; Begin

k:=Max(t,X); i:=t+l; While i>l Do Begin Dec (1) ; I f X[i]=k Then Begin Sz ( t , 1, X) ;Dec (t) ; End;

End; End;

Измените программу так , чтобы при удалении массив про-сматривался не с конца , а с начала .

2. Вставка элемента в массив. Необходимо вставить элемент после первого максимального элемента массива (пусть его но-мер равен q). Операция выполняется с л е д у ю щ и м образом: • первые q элементов массива остаются без изменений; • все элементы, н а ч и н а я с (д+2)-го, необходимо сдвинуть

на одну позицию; • на место (q+1) записываем значение константы InsC. ($R+i Program Му17_2;

Const MaxN=l0;la=l;ha=6;InsC=l00; Type MyArray=Array[l..MaxN+1] Of Integer; {*Вставляем один элемент. Резервируем для него место. *} Var А.-МуАггау;

п,k:Integer; Procedure Init (t,v,w:Integer;Var X:MyArray) ; ( * Формируем исходный массив . *}

Procedure Print(t:Integer;X:MyArray);(*Вывод значений элементов массива.*} Function Max(t:Integer;X:MyArray):Integer;

Page 227: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 227

(*Поиск номера первого максимального элемента. *} Var 1,Мх:Integer; Begin Мх:=1; For l : =2 То t Do I f X[i]>X[Mx] Then Mx:=i; Max:=Mx;

Ends-Procedure InsPs (t,q: Integer ;Var X -.MyArray) ;

("Сдвиг на одну позицию вправо и вставка на место q+1 (после заданного элемента) элемента, равного InsC. *)

Var i:Integer; Begin

For i:=t DownTo q+1 Do X[i+1]:=X[i]; X[q+11:=InsC;

End; Begin

n:=MaxN; Imt (n, la ,ha, A} ; WriteLn('Вывод массива до вставки элемента.'); Print (n,A); k:=Max(n,A);("Определяем место первого максимального элемента. *} InsPs(n,k,A); WriteLn( 'Вывод массива после вставки элемента.'); Print(n+1,А) ; ReadLn;

End. Д л я в с т а в к и элемента перед з а д а н н ы м элементом требуется

незначительное изменение процедуры InsPs. Procedure InsPs(t,q:Integer; Var X:• MyArray);

Var i-.Integer; Begin

For l: = t DownTo q Do X[l+l ] : =X[l ] ; X[q]:=InsC;

End; И з м е н и м задачу — требуется вставить InsC после всех мак-

с и м а л ь н ы х элементов массива . Во-первых, следует изменить размер массива . Е с л и массив состоит из одинаковых элементов, то в результате н а ш и х действий он увеличится в два раза . И т а к , Type MyArray=Array[1..2*MaxN] Of Integer. Во-вторых, предыдущее решение в п р и н ц и п е не изменяется . Ф у н к ц и я

Page 228: Pascal 8 Класс - Окулов

227 Часть третья

k:=Max(nA) и п р о ц е д у р а InsPs(n,kA) в к л ю ч а ю т с я в н о в у ю п р о ц е д у р у Solve.

Procedure Solve(Var t: Integer/Var X:MyArray) ; Var к, l:Integer; Begin

к:=Max(t,X);{'Определяем значение максимального элемента. *} i: =1 ; While К= t Do Begin

I f X[i]=k Then Begin InsPs(t.l,X) ;Inc ( t ) ; End; {'Вставляем элемент и увеличиваем размер

массива. *} Inc (1) ;

End; End;

А сейчас вопрос . П р и всех л и и с х о д н ы х д а н н ы х эта п р о г р а м -ма работает п р а в и л ь н о ? О к а з ы в а е т с я , нет . Н е т р о г а я п р о г р а м -м н ы й код , а и з м е н я я т о л ь к о з н а ч е н и я к о н с т а н т в п р о г р а м м е , н а й д и т е о ш и б к у . Э к с п е р и м е н т ы с п р о г р а м м о й п р и в е д у т Вас к з н а к о м о й о ш и б к е : Error 202: Range check error, а она у ж е «на-т о л к н е т » на неточность в п р о г р а м м е .

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

Задания для самостоятельной работы

1. З а м е н и т ь • э л е м е н т ы с krvo по k2-n на п р о т и в о п о л о ж н ы е по з н а к у ; • п е р в ы й о т р и ц а т е л ь н ы й э л е м е н т н у л е м ; • м а к с и м а л ь н ы й элемент на п р о т и в о п о л о ж н ы й по з н а к у ; • п е р в ы й э л е м е н т , к р а т н ы й 5, н у л е м ; • э л е м е н т ы с н е ч е т н ы м и н о м е р а м и н а к в а д р а т и х н о м е р а ; • п о с л е д н и й п о л о ж и т е л ь н ы й э л е м е н т н а второй э л е м е н т

массива ; • н у л я м и э л е м е н т ы м е ж д у м и н и м а л ь н ы м и м а к с и м а л ь н ы м

э л е м е н т а м и массива ; • все э л е м е н т ы , к р а т н ы е 3, на т р е т и й э л е м е н т м а с с и в а ; • все э л е м е н т ы с н е ч е т н ы м и н о м е р а м и р а з д е л и т ь н а ц е л о

на п е р в ы й элемент .

Page 229: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 229

2. И з элементов массива А сформировать массив В той ж е раз-мерности по правилу • первые 10 элементов — B[i]:=A[i]+i, остальные — B[i]:=

• если номер четный, то B[i]:=i*A[i], если нечетный, то B[i]:=A[i];

• элементы с 3-го по 12-й — B[i]~A[i]*A[i], все осталь-ные — B[i]~A[i] -1;

• если номер четный, то B[i]:=A[i]*A[i], если нечетный, то B[i]:=A[i] Div i.

3. Сформировать массив А с помощью датчика случайных чисел ц е л ы м и числами из следующих интервалов: [ - 2 1 , 53], [ - 4 3 , 32], [ - 1 1 , 67], [ - 3 5 , 7 5 ] , [ - 4 5 , 9 5 ] .

4. У д а л и т ь из массива все элементы • с ц и ф р о й 5 в записи; • состоящие из одинаковых ц и ф р (включая однозначные

числа); • последняя ц и ф р а которых четная , и само число делится

на нее; • первая цифра которых четная ; • к р а т н ы е 7 и п р и н а д л е ж а щ и е промежутку [а, Ь] (а и Ь

вводятся с клавиатуры) ; • меньшие нуля ; • большие данного числа А (.А вводится с клавиатуры) ; • четные элементы, стоящие на нечетных местах; • которые повторяются , оставив только их первые вхожде-

н и я , то есть получить массив из р а з л и ч н ы х элементов; • к р а т н ы е 3 и л и 5; • н а ч и н а я с krro по kz-й (k1 и k2 вводятся с клавиатуры) .

5. Вставить число k (все числа , указанные в формулировках за-дач, вводятся с клавиатуры) • после всех элементов, к р а т н ы х своему номеру; • перед всеми элементами, в которых есть цифра 1; • перед и после всех элементов, з аканчивающихся на дан-

ную цифру ; • после всех элементов, больших заданного числа, а число

t — перед всеми элементами, к р а т н ы м и 3; • м е ж д у всеми соседними элементами, которые образуют

пару элементов с одинаковыми знаками; • после первого отрицательного элемента; О перед последним отрицательным элементом;

Page 230: Pascal 8 Класс - Окулов

229 Часть третья

• после максимального элемента, а число t — перед макси-мальным элементом;

• перед всеми элементами, кратными заданному числу; • перед всеми отрицательными элементами; • после всех элементов, больших данного числа Р , а второе

число t — перед всеми элементами, меньшими числа Р; • перед всеми элементами, большими k, а число t — после

всех элементов, меньших его. 6. Переставить в массиве

• первые три и последние три элемента местами, сохраняя их следование;

• первый положительный и последний отрицательный эле-менты;

• первый элемент и максимальный; • второй и минимальный; • первый и последний отрицательный; • элементы следующим образом: А [ 1 ] , А [ 12], А [ 2 ] , А[ 11 ] ,

.... А[5], А[8], А[6], А[7]\ • первые k элементов в конец, то есть: A[k+1 ], A[k+2]

А[п], А[1 ], А[2] A[h]• • в обратном порядке часть массива между элементами с

номерами k t и k2, включая их. • первый элемент с последним, второй с предпоследним и

так далее; • в обратном порядке элементы, расположенные между

минимальным и максимальным элементами; • элементы по следующим правилам (2*п элементов в мас-

сиве): • А[п+1 ], А[п+2] А[2п], А[1 ], А[2],..., А[п]; ш А[п+1], А[п+2],..., А[2п], А[п], А[п-1 ],..., А[1]; • А[1], А[п+1], А[2], А[п+2] А[п], А[2п]; ш А[2п], А[2п-1 ] А[п+1 ], А[1], А[2],..., А[п].

7. Дан одномерный массив из целых чисел. Найти количество различных чисел среди элементов массива.

8. Дан одномерный массив из целых чисел. Известно, что значе-ния элементов массива находятся в интервале от А до В. Най-ти количество различных чисел среди элементов массива.

9. Дан одномерный массив из целых чисел и число д. Переста-вить элементы массива так, чтобы слева от некоторой грани-цы были записаны числа, меньшие q, а справа от границы — числа, большие или равные q. Дополнительные массивы не разрешается использовать.

Page 231: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 231

Модифицировать решения для случая: меньшие q, равные q, большие q.

10. Дан массив из N целых чисел и число М (M<N). Для каждо-го участка из М стоящих рядом элементов массива вычислить значение суммы (количество участков N-M+1).

Материал для чтения

Алгоритмы и их сложность. Оценка сложности алгоритмов (методов) обычно осуществляется по времени решения и затра-там на объем требуемой памяти для хранения исходных дан-ных задачи. Рассмотрим первую характеристику понятия слож-ности алгоритма. С каждой конкретной задачей связывается некое число N, называемое ее размером и выражающее меру количества входных данных. В случае одномерного массива — это размер массива. Время, затрачиваемое на решение задачи, выраженное через значение N, называется временной сложно-стью алгоритма. Поведение этой характеристики (сложности) при увеличении размерности задачи (значения N) называют асимптотической временной сложностью. Эффективность алго-ритма определяется именно асимптотической временной слож-ностью. Если алгоритм обрабатывает задачу размера N за время c*N2, где с — некоторая постоянная, то говорят, что временная сложность алгоритма есть 0(N2) (читается «порядка iV2»). Наи-более часто встречаемые оценки временной сложности приведе-ны в таблице.

I Временная сложность Тип зависимости Значение при N=1024

0(N) Линейная 1024

O(N'LogN) Логарифмическая 10240

OfN 2 ) или OfN3) Полиномиальная ~106 или 10э

1 0(2», Экспоненциальная ~Ю300

Эффективный алгоритм — это тот, который решает задачу за меньшее время. Пусть есть два алгоритма At и А2, решаю-щих одну и ту же задачу. Первый алгоритм решает задачу раз-мерности jV=106aa N2 операций, а второй — за 10*N*LogN. Первый алгоритм выполняется на суперкомпьютере с быстро-действием 108 операций в секунду, а второй — на простом, с быстродействием 106 операций в секунду. Найдем время реше-

Page 232: Pascal 8 Класс - Окулов

232 Часть третья

н и я з а д а ч и в том и в другом с л у ч а я х : f ; = 1 0 1 2 / 1 0 8 - 2 , 8 часа ; f 2 = ( 6 * 1 0 7 * L o g l 0 ) / 1 0 6 - 2 0 0 секунд .

П о и с к м а к с и м а л ь н о г о элемента в о д н о м е р н о м массиве за N - 1 с р а в н е н и е м ы умеем делать .

Mm :=А[1]; For 1 :=2 То N Do I f A[i]<Mm Then Mm:=A[i] ;

А если необходимо н а й т и и м и н и м а л ь н ы й элемент? Р е ш е н и е за t j = 2 * N - 2 о п е р а ц и й с р а в н е н и я очевидно . М о ж н о л и улуч-ш и т ь р е з у л ь т а т (N — четное число)?

I f А[1]>А[2] Then Begin Max:=А[1];Min:=А[2];End Else Begin Mm : =A [ 1 ] ;Max: =A [2] ;End;

i:=3; While i<=N~l Do Begin

I f A[i]>A[i+l] Then Begin I f A[i]>Max Then Max:=A [l] ;

I f A[i+1] <Mm Then Mm : =A[i+l ] ;

End Else Begin

I f A[i+1]>Max Then Max:=A[i+l]; I f A[l] <Min Then Mm:=A[i];

End; Inc (i , 2) ; End;

Н а к а ж д у ю пару чисел, кроме первой, требуется три сравне-ния. Н а первую — одно. Время решения t2 = 3* pV / 2 ] - 2 (в ко-личестве операций сравнения) , где символами обозначено бли-ж а й ш е е ц е л о е ч и с л о , п р е в ы ш а ю щ е е з н а ч е н и е N / 2 . Д л я сравнения методов найдите разность t1~t2, например, при 2V=109.

Одним и з о с н о в н ы х п р и е м о в р е ш е н и я задач я в л я е т с я реали-з а ц и я п р и н ц и п а « р а з д е л я й и властвуй» , т. е. з а д а ч а разбивает-ся на подзадачи , р е ш а е м ы е независимо , а з атем р е з у л ь т а т ы о б ъ е д и н я ю т с я . П у с т ь размерность з а д а ч и о п и с ы в а е т с я значени-ем N. Н а м необходимо оценить в р е м я р е ш е н и я T(N). Слож-ность з а д а ч и обычно определяется числом и размером подзадач и в м е н ь ш е й степени д е й с т в и я м и , н е о б х о д и м ы м и д л я разбие-н и я з а д а ч и на подзадачи . С л е д у ю щ а я теорема (приводится без

Page 233: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 233

доказательства) является основой оценки времени решения за-дач при использовании принципа «разделяй и властвуй».

Пусть а, Ь, с — неотрицательные постоянные. Решение уравнений: О T(N)=b, при N= 1, О T(N)= a*T(N/c)+b*N, при N>1,

где N — степень числа с, имеет вид: О T(N)=0(N), если а<с, О T(N)=0(N*LogN), если а=с, О T(N)=0(Nl°ec

a), если а>с.

Page 234: Pascal 8 Класс - Окулов

234 Часть третья

З а н я т и е № 18. М н о ж е с т в е н н ы й т и п д а н н ы х

План занятия О описание множественного типа д а н н ы х , операции с вели-

ч и н а м и этого типа д а н н ы х ; • экспериментальная работа с программами: вывода цифр,

не в х о д я щ и х в десятичную запись числа п, поиска про-с т ы х чисел с помощью «решета Эратосфена», р е ш е н и я ре-бусов;

О выполнение самостоятельной работы.

Множество в я зыке Турбо Паскаль — это ограниченный на-бор различных элементов одного (базового) типа 1 . Базовый тип — это совокупность значений, из которых могут быть образованы множества . Мощность (количество р а з л и ч н ы х элементов) не превышает 256. Значение переменной множественного типа может содержать любое количество р а з л и ч н ы х элементов базо-вого типа . И н ы м и словами, в о з м о ж н ы м и з н а ч е н и я м и перемен-н ы х множественного типа я в л я ю т с я все подмножества значе-ний базового типа. Вещественный тип д а н н ы х не используется в качестве базового (нет отношения порядка , нельзя ввести ограничения на тип).

Описание Туре <имя типа> = Set Of <тип элементов>; Var <имя переменной множественного типа> : <имя типа>;,

или Var <имя переменной множественного типа> : Set Of <тип элементов>;. Пример: Type Mn_Char= Set Of Char; Var mnl ; Set Of Char;

mn2: Mn_Char; mn3: Set Of 'A'. . 'Z'; si: Set Of Byte; s2: Set Of 100..120;

З н а ч е н и я м и переменных mnl и mn2 я в л я ю т с я множества , составленные из р а з л и ч н ы х символов, т п З — множества из бо-л ь ш и х л а т и н с к и х букв; si — множества из целых чисел от 0 до 255 (тип Byte содержит целые числа от 0 до 255), s2 — множе-

Элементы множества должны быть порядкового типа.

Page 235: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 235

ства из ц е л ы х ч и с е л от 100 до 120. В п р о г р а м м е э л е м е н т ы мно-ж е с т в а з а д а ю т с я в к в а д р а т н ы х с к о б к а х , через з а п я т у ю . Е с л и э л е м е н т ы и д у т п о д р я д д р у г за другом , то и с п о л ь з у е т с я обозна-ч е н и е и н т е р в а л а (две т о ч к и ) .

Пример:

Type Digit = Set Of 1. .5; Var s : Digit;

К о л и ч е с т в о р а з л и ч н ы х з н а ч е н и й п е р е м е н н о й s 32 . Перечис-л и м и х :

• [ ] — пустое м н о ж е с т в о ; • [1], [2], [3] , [4] , [5] — о д н о э л е м е н т н ы е м н о ж е с т в а ; • [1 , 2] , [ 1 , 3 ] , . . . , [ 2 , 4 ] , [ 4 , 5 ] — д в у х э л е м е н т н ы е м н о ж е с т -

ва ; • [1, 2, 3] , [ 1 , 2 , 4 ] , . . . , [ 3 , 4 , 5 ] — т р е х э л е м е н т н ы е м н о ж е с т -

ва ; • [1, 2 , 3, 4] , [ 1 , 2 , 3 , 5 ] , [ 1 , 2 , 4 , 5 ] , [ 1 ,3 ,4 ,5 ] , [ 2 , 3 , 4 , 5 ] — четы-

р е х э л е м е н т н ы е м н о ж е с т в а ; • [1 , 2, 3 , 4 , 5] — м н о ж е с т в о из всех элементов базового

т и п а .

Операции над множествами. Объединением двух д а н н ы х м н о ж е с т в н а з ы в а е т с я м н о ж е с т в о элементов , п р и н а д л е ж а щ и х обоим м н о ж е с т в а м . З н а к о п е р а ц и и — « + ».

Примеры: • [ ' A ' , ' F ' ] + [ ' В ' , ' D ' ] = [ ' A ' , ' F ' , ' В ' , 'D ' ] ; • [ 1 . . 3 , 5 , 7 , 1 1 ] + [ 3 . . 8 , 1 0 , 1 2 , 1 5 . . 2 0 ] = [ 1 . . 8 , 1 0 . . 1 1 , 1 5 . . 2 0 ] ; • S i : = [1 . . 5 , 9 ] , S 2 : = [ 3 . . 7 Д 2 ] . S : = S1 + S 2 = [ 1 . . 7 , 9 , 1 2 ] ; • A j : = [ ' a ' . . ' z ' ] ; A ^ A j + [ 'А ' ] . Результат — A ^ ' A ' , V . . ' z ' ] .

Пересечением д в у х д а н н ы х м н о ж е с т в н а з ы в а е т с я м н о ж е -ство э л е м е н т о в , п р и н а д л е ж а щ и х о д н о в р е м е н н о и п е р в о м у , и в т о р о м у м н о ж е с т в у , то есть это о б щ и е э л е м е н т ы . З н а к опера-ц и и — «*».

Page 236: Pascal 8 Класс - Окулов

236 Часть третья

СЮ А В А* В

Примеры: • [ 'A ' , ' F ' ] * [ 'В ' , 'D '] = [ ] — так как общих элементов нет; • [1 . .3 ,5 ,7 ,11] * [3. .8 ,10,12,15. .20] = [3,5,7]; • S j := [1..5.9], S2 := [3 . .7Д2] . S:= S,, * S2=[3. .5] . В результате операции вычитания формируется множество,

состоящее из тех элементов первого множества, которые не яв-ляются элементами второго множества. Знак операции — «-» .

Примеры: • [ 'A' , 'F ' ] - [ 'В' , 'D '] = [ 'A ' , 'F ' ] — общих элементов нет; • [1. .3,5,7,11] - [3. .8 ,10,12,15. .20] = [1. .2Д1]; • S l : = [1.-5,9]; S2:= [3. .7Д2]; S;= SI - S2 =[1. .2,9]; • A1:=[ 'A ' . . 'Z ' ] ; A1:=A1 — [ 'A ' ]=[ 'B ' . . 'Z ' ] . Операция определения принадлежности элемента множе-

ству. Эта логическая операция обозначается служебным сло-вом In. Результат операции — значение True, если элемент принадлежит множеству, и False — в противном случае.

Примеры: О 5 In [3 .. 7] дает значение True, так как 5 е [3 .. 7]; • 'a" In [ 'А' .. 'Z '] дает значение False, буквы 'а ' нет среди

больших латинских букв. Операцию проверки принадлежности удобно использовать

для исключения более сложных проверок. Например, оператор вида

If (ch = 'a') or (ch='b') or (ch='x') or (ch='y'j Then s;

GO A A-B

Page 237: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 236

переписывается в более к о м п а к т н о й и наглядной форме: I f ch In [ ' а ' , ' b ' , ' х ' , 'у' ] Then s;. Сравнение множеств. Д л я сравнения множеств используют-

ся обычные операции: О = — равенство (совпадение) двух множеств; О о — неравенство двух множеств; • < = , < — проверка на вхождение первого множества во

второе множество; О > = , > — проверка на вхождение второго множества в пер-

вое множество . Первое множество м е н ь ш е или равно второму (А < В):

True False True

Первое множество меньше второго (А < В):

True False False

Экспериментальный раздел работы

1. Дано натуральное число п. Составить программу вывода цифр, не в х о д я щ и х в десятичную запись числа п (в порядке возрас-тания) .

Program Му18_1; Type Mn=Set Of 0. .9; Var s:Mn ;

n:Longlnt; i : Integer;

Begin WriteLn('Введите число ' ) ; ReadLn (n) ; s:=[0..9]; While n<>0 Do Begin

Page 238: Pascal 8 Класс - Окулов

237 Часть третья

s:=s-[п Mod 10];{*Исключаем цифру. *) п:=л Div 10;

End; For i: =0 То 9 Do I f l In s Then Write (i :2) ; WriteLn; ReadLn;

End. И з м е н и т е п р о г р а м м у т а к , чтобы н а х о д и л и с ь общие ц и ф р ы в

з а п и с и т ч и с е л . 2. «Решето Эратосфена» . Н а й т и простые ч и с л а в и н т е р в а л е от

2 до п. Н а п о м н и м , что п р о с т ы м ч и с л о м н а з ы в а е т с я ч и с л о , не име-

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

Program Му18_2; Const п=1ООО;{'Интервал чисел, в котором находятся простые числа. *} Var 1:Integer; Function Simple(m:integer):Boolean;

Var i:Integer; Begin

i: =2 ; While fi<= m Div 2) And (m Mod i<>0) Do Inc(i) ; Simple:=(i>m Div 2);

End; Begin

For i : =2 To n Do I f Simple (i) Then Write ( i , ' ' ) ; ReadLn;

End. И з м е н и т е п р о г р а м м у т а к , чтобы н а х о д и л а с ь п е р в а я 1000

простых чисел . О т к а ж е м с я от этого простого р е ш е н и я . П р и м е н и м идею «ре-

шета Эратосфена» , ибо н а ш а цель — и з у ч е н и е множественного т и п а д а н н ы х . Суть метода — считаем все ч и с л а и н т е р в а л а про-с т ы м и , а з атем «вычеркиваем» те, к о т о р ы е не удовлетворяют требованию простоты. К а к о с у щ е с т в л я е т с я в ы ч е р к и в а н и е ? На-ходим очередное н е в ы ч е р к н у т о е число , оно простое, и у д а л я е м все числа , к р а т н ы е ему. После такого «просеивания» в исход-ном м н о ж е с т в е останутся только простые ч и с л а .

Page 239: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 239

Program Му18_2т; Const п=255; Type Mn=Set Of 0. .п; Var Sim:Мп;

1,3 :Integer; Begin

Sim:=[2..n]; j:=2; While j<=n Div 2 Do Begin

I f j In Sim Then Begin {*Поиск очередного простого числа.*}

i:=j+j; While 1<=п Do Begin Sim:=Sim-[i];Inc <i,j); End;{*Вычеркивание. *}

End; I n c ( j ) ;

End; For l: =2 To n Do I f 1 In Sim Then Write (i:4) ; {*Вывод оставшихся после вычеркивания чисел, они простые. *} ReadLn;

End.

Поиск простых чисел из интервала , большего, чем 0. . 255, «упирается» в ограничение множественного типа д а н н ы х — не более 256 значений базового типа. Уйдем от этого ограничения путем ввода массива , элементами которого я в л я ю т с я множест-ва . Н о прежде , чем рассмотрим решение, небольшой фрагмент:

($R + ) Program Myl8_2mm;

Var Mn: Set Of 1..255; a: Word;

Begin Mn : = [1. .255]; a:=258; I f a In Mn Then WriteLn (' Yes') Else WriteLn ('No') ; ReadLn;

End. После запуска программы видим знакомую до боли ошиб-

ку — Error 202: Range check error. Значение переменной а вы-ходит за допустимый диапазон значений. Учтем этот факт при написании очередной версии программы.

Page 240: Pascal 8 Класс - Окулов

240 Часть третья

($R+1 Program Му18_2ттт;

Uses Crt; Const m=255;n=l000 ; TypeMn=Set Of l..m;

OMyArray=Array [0 . . (n Div m) ] Of Mn; VarSim:Mn;

A:OMyArray; i,j,k:Integer; Begin

ClrScr; к:=(n Div m); For l:=0 To к Do A[i]: = [!. .m] ; j:=2; While j<=n Div 2 Do Begin

I f ( j Mod m) In A[j Div m] Then Begin i:=j+j; While K=n Do Begin A f i Div m] :=A[i Div m] - [l Mod m];Inc(i,j);End;

End; I n c ( j ) ;

End; For i:=2 To n Do I f (l Mod m) In A[i Div m] Then Write ( l , ' ' ) ; ReadLn;

End.

3. Р е ш е н и е ребусов м ы р а с с м а т р и в а л и на п р е д ы д у щ и х заняти-я х . С использованием множественного типа д а н н ы х програм-м н ы й код получается более к о м п а к т н ы м . Подсчитаем коли-чество р е ш е н и й ребуса М У Х А + М У Х А = С Л О Н . Program Му18_3;

Type Mn=Set Of 0. .9; Var i , j , cnt:Integer;

Sm,Se:Mn; Procedure Change(t:Integer; Var S:Mn);{*Из цифр числа формируем множество. *j Begin

S:=[]; While t<>0 Do Begin

S:=S4-[t Mod 10];t:=t Div 10; End;

End;

Page 241: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 241

Function Qw(S:Мп):Integer;{"Подсчитываем количество элементов в множестве. *)

Var 1,cnt:Integer; Begin

cnt:=0; For l: =0 To 9 Do I f i In S Then Inc (cnt); Qw:=cnt;

Ends-Begin

cnt:=0;{*Счетчик числа решений. *) For l:=1000 To 4999 Do Begin{"Результат -четырехзначное число, поэтому слагаемое не превышает 4 999. *}

Change(i,Sm); I f Qw {Sm) =4 Then Begin { *Если все цифры числа различны, то выполняем дальнейшие вычисления. *)

О: =2*i; Change ( j , Se) ; I f (Sm*Se=[]> And (Qw(Se)=4) Then Inc(cnt); {*Числа состоят из различных цифр, и все цифры

результата различны. *) End;

End; WriteLn (cnt);

End.

Задания для самостоятельной работы

1. Дан текст . Н а й т и множества , элементами которых являют-ся встречающиеся в тексте: О ц и ф р ы от ' 0 ' до ' 9 ' и з н а к и арифметических операций; • буквы от 'А' до 'F' и от 'X' до 'Z'; О з н а к и п р е п и н а н и я и буквы от до W .

2. Вывести элементы множества , составленного из произволь-н ы х букв от А ... Z, в алфавитном порядке.

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

4. Д а н текст . Вывести в алфавитном порядке все буквы текста, в х о д я щ и е в него: • не менее двух раз; • не более двух раз; • более двух раз;

Page 242: Pascal 8 Класс - Окулов

242 Часть третья

• по о д н о м у разу. 5. Д а н текст. Подсчитать количество гласных и согласных букв. 6. Н а п и с а т ь п р о г р а м м ы р е ш е н и я ребусов: Л О Б + Т Р И = С А М ,

И С К + И С К = К С И , Т О Ч К А + К Р У Г = К О Н У С , V O L V O + F I A T = = M O T O R , А В + В С + С А = А В С .

Page 243: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 243

Занятие № 19. Методы сортировки

План занятия • постановка задачи сортировки данных, изучение простых

методов сортировки; • экспериментальная работа с методом сортировки подсче-

том; • выполнение самостоятельной работы. Постановка задачи. Для решения многих задач необходи-

мо упорядочить данные по определенному признаку. Процесс упорядочения заданного множества объектов по заданному при-знаку называется сортировкой. Для простоты изложения рас-сматривается одномерный массив из целых чисел (Т у р е МуАг-r ау= Array [l..NMax] Of Integer; Var A:MyArray, значение Nmax определяется в разделе констант). Суть большинства алгорит-мов сортировки от такого упрощения не изменяется. Алгорит-м ы сортировки отличаются друг от друга степенью эффективно-сти, под которой понимается количество сравнений и количество обменов, произведенных в процессе сортировки. В основном м ы будем оценивать эффективность количеством операций сравне-ния (порядком этого значения). Заметим, что элементы масси-ва можно сортировать:

• по возрастанию — каждый следующий элемент больше предыдущего — А[1] < А[2] <... < A[N];

• по неубыванию — каждый следующий элемент не меньше предыдущего, то есть больше или равен А[1 ] < А[2] < ... ZA[N];.

• по убыванию — каждый следующий элемент меньше пре-дыдущего А[1 ] > А[2] > ... > A[N];.

• по невозрастанию — каждый следующий элемент не боль-ше предыдущего, то есть меньше или равен А[1] > А[2] > ... > A[N].

Научившись выполнять одну сортировку, изменить ее, что-бы получить другую, не составляет особого труда.

Сортировка простым выбором. Рассмотрим идею на приме-ре. Пусть исходный массив А состоит из 10 элементов: 5 13 7 9 1 8 16 4 10 2. После сортировки массив должен выглядеть так: 1 2 4 5 7 8 9 11 13 16.

Процесс сортировки представлен ниже. Максимальный эле-мент текущей части массива заключен в кружок, а элемент, с которым происходит обмен, в квадратик. Скобкой помечена рассматриваемая часть массива.

Page 244: Pascal 8 Класс - Окулов

244 Часть третья

1-й шаг. Рассмотрим весь массив и найдем в нем максималь-ный элемент — 16 (стоит на седьмом месте), поменя-ем его местами с последним элементом — с числом 2.

5 13 7 9 1 8 (f |_4_10j2]

Максимальный элемент записан на свое место. 2-й шаг. Рассмотрим часть массива — с первого до девятого

элемента. Максимальный элемент этой части — 13, стоящий на втором месте. Поменяем его местами с последним элементом этой части — с числом 10.

5 (П| 7 9 1 8 2 4 [ТО] 16

Отсортированная часть массива состоит теперь уже из двух элементов. 3-й шаг. Уменьшим рассматриваемую часть массива на один

элемент. Здесь нужно поменять местами второй эле-мент (его значение — 10) и последний элемент этой части — число 4.

5 (Г§Т7 9 1 8 2JT[13 16

В отсортированной части массива — 3 элемента. 4-й шаг.

5 4 7(§)1_8[2]10 13 16

5-й шаг. Максимальный элемент этой части массива является последним в ней, поэтому его нужно оставить на ста-ром месте.

5 4 7 2 тГЁПэ 10 13 16

6-й шаг.

5 4 Q 2 j T | 8 9 10 13 16

(5)4_l[2]7 8 9 10 13 16

|2(4fT]5 7 8 9 10 13 16

Г Ц 5 7 8 9 10 13 16

Page 245: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 245

Ф р а г м е н т программной реализации . Procedure Sort(N:Integer; Var A:MyArray);

Var l , j , k: Integer; m: In teger;{*3начение максимального элемента рассматриваемой части массива. *}

Begin For i:=N DownTo 2 Do Begin 1*Цикл по длине рассматриваемой части массива. *}

(*Поиск максимального элемента и его номера в текущей части массива. *}

k:=i; m:=А[i];("Начальные значения максимального элемента и его индекса в рассматриваемой части массива. *} For j:=l То 1-1 Do I f A[j ] >w Then Begin k:=j; m:=A[j] End; I f k o i Then Begin I "Перестановка элементов . *} A[k] :=A[i] ;A[i] :=m;

End; Ends-

End;

Количество сравнений. П р и первом просмотре N-1, при вто-ром — N-2, при последнем — 1. Общее количество C=N-l+N-2+ ...+1=N*(N-1 )/2 (сумма элементов арифметической прогрес-сии) и л и C=0(N2).

Сортировка простым обменом. Рассмотрим идею метода на примере . Отсортируем по возрастанию массив из 5 элемен-тов: 5 4 8 2 9. Д л и н а т е к у щ е й части массива — N-k+1, где k — номер просмотра , i — номер первого элемента проверяемой п а р ы , номер последней п а р ы — N-k.

Первый просмотр: рассматривается весь массив. 1=1 5 4 8 2 9

> меняем i = 2 4 5 8 2 9

< не меняем 1=3 4 5 8 2 9

> меняем 1=4 4 5 2 8 9

< не меняем 9 находится на своем месте.

Второй просмотр: рассматриваем часть массива с первого до предпоследнего элемента.

Page 246: Pascal 8 Класс - Окулов

246 Часть третья

1=1 4 5 2 8 9 < не м е н я е м

1=2 4 5 2 8 9 > м е н я е м

1=3 4 2 5 8 9 < не м е н я е м

8 — на своем месте .

Третий просмотр: р а с с м а т р и в а е м а я ч а с т ь массива с о д е р ж и т т р и п е р в ы х э л е м е н т а .

1=1 4 2 5 8 9 > м е н я е м

1=2 2 4 5 8 9 < не м е н я е м

5 — на своем месте .

Четвертый просмотр: р а с с м а т р и в а е м п о с л е д н ю ю п а р у эле-ментов .

i = l 2 4 5 8 9 < не м е н я е м

4 — на своем месте . Н а и м е н ь ш и й элемент — 2 о к а з ы в а е т с я на первом месте . К о л и ч е с т в о просмотров э л е м е н т о в м а с с и в а равно N-1.

И т а к , н а ш массив отсортирован . Этот метод т а к ж е н а з ы в а ю т методом « п у з ы р ь к а » . Н а з в а н и е это п р о и с х о д и т от образной ин-т е р п р е т а ц и и , п р и которой в процессе в ы п о л н е н и я с о р т и р о в к и более «легкие» э л е м е н т ы ( элементы с з а д а н н ы м свойством) ма-ло -помалу в с п л ы в а ю т на «поверхность» .

Procedure Sort(N:Integer;Var A:MyArray); Var k,i,w:Integer;{*k - номер просмотра, изменяется от 1 до N-1; i — номер первого элемента рассматриваемой пары; w — рабочая переменная для перестановки местами элементов массива. *} Begin

For k:=l То N-1 Do (*Цикл по номеру просмотра. *} For i:=1 То N-k Do

I f A[i]>A[i+l] Then {'Перестановка элементов.*} Begin

w:=A[i];A[i]:=A[i+l];A[i+l]:=w; End;

End;

Page 247: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 247

П р и сортировке методом «пузырька» выполняется N-1 про-смотров, на к а ж д о м i-просмотре производится N-i сравнений. Общее количество С равно N*(N-1 )/2, или 0(N2).

Сортировка простыми вставками. Сортировка этим мето-дом производится последовательно ш а г за шагом. На г-м шаге считается, что часть массива, содержащая первые i - l элемен-тов, уже упорядочена, то есть А[1 ]<A[2]<...<A[i-l]. Далее бе-рется г-й элемент, и д л я него подбирается место в отсортирован-ной части массива, такое, что после его вставки упорядоченность не нарушается , то есть требуется найти такое j (0<j<i-l), что A[j]<A[i]<A[j+l] (при j=0 происходит только одно сравнение, если не использовать «барьерную» технику) . Затем выполняет-ся вставка элемента A[i] на место j . На к а ж д о м шаге отсорти-рованная часть массива увеличивается. Д л я выполнения пол-ной сортировки потребуется выполнить N-1 шаг .

Рассмотрим этот процесс на примере. Пусть требуется отсор-тировать массив из 10 элементов по возрастанию.

Рассматриваем часть массива из одного элемента 13 Вставляем второй элемент массива 6 так, чтобы упорядоченность сохранилась. Так как 6< 13, записываем 6 на первое место Отсортированная часть массива содержит два элемента (6 13) Возьмем третий элемент массива 8 и подберем для него место в упорядоченной части массива 8>6 и 8< 13, следовательно, его место второе. Следующий элемент — 11 Он записывается в упорядоченную часть массива на третье место, так как 11 >8, но 11<13

Далее, действуя аналогичным образом, определяем, что 3 необходимо записать на первое место По той же причине 1 записываем на первое место

Так как 5>3, но 5<6, то место 5 в упорядоченной части — третье Место числа 9 — шестое Определяем место для предпоследнего элемента 15 Оказывается, что этот элемент массива уже находится на своем месте. Осталось подобрать подходящее место для последнего элемента 7. Массив отсортирован полностью.

1-й шаг 13.6 8 11 3 1 5 9 15 7

2-й шаг 6 138 11 3 1 5 9 15 7

3-й шаг 6 8 13 11 3 1 5 9 15 7

4-й шаг 6 8 11 13 3 1 5 9 15 7

5-й шаг. 3 6 8 11 13 1 5 9 15 7

6-й шаг. 1 3 6 8 11 13 5 9 15 7

8-й шаг. 1 3 5 6 й 9 11 13 15 7

9-й шаг. 1 Я SB R 9 11 13 15 7

1 3 5 6 7 8 9 11 13 15

Page 248: Pascal 8 Класс - Окулов

247 Часть третья

П р о ц е д у р а с о р т и р о в к и . Procedure Sort(N:Integer;Var A:MyArray);

Var l,j,w:Integer; Begin

For l:=2 To N Do Begin w:=Л[i]; j:=i-l; While (J>0) And (w<A[j]> Do Begin A [ j +1 ] : —A [ j ] ; Dec ( j ) ;End; A[j+l]:=w;

End; End;

Оценим эффективность метода . Д л я массива , н а п р и м е р , 1 2 3 4 5 6 7 8 потребуется N-1 сравнение ( н а п о м н и м , что м ы сор-тируем в п о р я д к е возрастания) , а д л я массива 8 7 6 5 4 3 2 1 — N*(N-1 )/2 и л и C=0(N2).

Экспериментальный раздел работы

Сортировка подсчетом. Пусть дан массив (А) и з 10 элемен-тов: 10, 5, 11, - 5 , 1, - 4 , 13, 12, - 4 , 13. Возьмем п е р в ы й эле-мент, он больше п я т и элементов . З а п и ш е м 5 в д о п о л н и т е л ь н ы й массив счетчиков (Count). В ы п о л н и м эту о п е р а ц и ю д л я всех элементов массива А. В массиве Count имеем: 5, 4, 6, 0, 3, 1, 8, 7, 2 , 9 . Е с л и р а з р е ш а е т с я использовать д о п о л н и т е л ь н ы й массив д л я х р а н е н и я отсортированных д а н н ы х , то остается переписать к а ж д ы й элемент исходного массива на соответствующее место в р е з у л ь т и р у ю щ е м массиве (В).

Procedure Sort(N:Integer;A:MyArray;Var B:MyArray); Var i,j:Integer;

Count:MyArray; Begin

FillChar(Count,SizeOf(Count),0); For i:=N DownTo 2 Do

For j:=i-l DownTo 1 Do I f A[i]<A[j] Then Inc(Count [ j ] ) Else Inc(Count [ i ] ) ;

For i : =1 To N Do В [Count [i]+l ] :=A[i] ; End;

Естественное ж е л а н и е — убрать массив В. Н и ж е по тексту приведена соответствующая процедура . Разберите её. П о к а ж и -те, что она действительно работоспособна. В качестве подсказ-

Page 249: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 248

к и приведем з н а ч е н и я элементов массива Count, увеличенные на единицу .

6 5 7 1 4 2 9 8 3 10 I I I I

Procedure Swap(Var х,у:Integer); Var w: Integers-Begin w: =x;x: =y; у: =w; Ends-

Procedure Sort (N: Integer ; Var A:MyArray); Var l,J:Integer;

Count:MyArray; Begin

FillChar(Count,SizeOf(Count),0) ; For l:=N DownTo 2 Do

For j:=i-l DownTo 1 Do I f A[i]<A[j] Then Inc(Count[j]) Else Inc(Count [l]) ;

For i:=l To N Do Inc(Count[l]); For l:=1 To N Do

While Count[l]<>i Do Begin Swap(A[l],A[Count[l]]);Swap(Count[l],Count [Count [ i ] ] ) ; Ends-

End;

Предположим, что значения элементов массива А принадле-ж а т интервалу [и,и]. Например, А состоит из элементов: 2, 4, 3, 2, 4, 2, 3, 4, 3, 2. Подсчитаем, сколько раз каждое значение встречается в массиве: двойка — 4 раза, тройка — 3 раза и чет-верка — 3 раза (массив Count: 4, 3, 3). Изменим значения Count на 4, 7, 10. П р и этих значениях (Count) элементы массива А можно сразу записывать на свое место в результирующий мас-сив В.

Procedure Sort (N: Integer ;A:MyArray ; Var B:MyArray) ; {*Считаем, что значения элементов массива А принадлежат интервалу [1..4]. *}

Const u=l;v=4; Var i:Integer;

Count .-Array [u. .v] Of Integers-Begin

FillChar (Count,SizeOf(Count) ,0) ; For i : =1 To N Do Inc (Coun t [A[i] ]) ;

Page 250: Pascal 8 Класс - Окулов

250 Часть третья

F o r i : =и+1 То v Do Inc ("Count [i] , C o u n t [l-l } ) ; For i:=l To N Do Begin В[Count[A[l]]]:=A[l];Dec(Count[A[l]]);

End; End;

А м о ж н о л и не использовать массив В? О к а з ы в а е т с я , да . В с л е д у ю щ е й версии п р о ц е д у р ы Sort п о к а з а н о , к а к это сделать .

Procedure Sort f t : I n t e g e r ; V a r A:MyArray); Const u=l;v=4; Var l,j,q:Integer;

Count:Array[u..v] Of Integer; Begin

FillChar(Count,SizeOf(Count),0); For i:=l To t Do Inc (Count [A[i] ]) ; q:=l! For i:=u To v Do

For j:=l To Count[l] Do Begin A[q] : =1; Inc (q) ;

End; End;

Задания для самостоятельной работы

1. И з м е н и т ь р е ш е н и я в трех р а с с м о т р е н н ы х методах т а к , что-бы о с у щ е с т в л я л а с ь сортировка : • ч е т н ы х элементов массива ; • элементов , з а п и с а н н ы х на нечетных местах ; • о т р и ц а т е л ь н ы х элементов массива и т .д .

2. Дан , н а п р и м е р , массив 12 3 5 7 9 10. З а один просмотр мето-дом «пузырька» он становится о т с о р т и р о в а н н ы м , остальные п р о с м о т р ы ничего не дают. И с к л ю ч и т ь л и ш н и е просмотры.

3. Массив 1 2 3 5 7 9 1 0 сортируется методом «пузырька» за один просмотр , а массив 5 7 9 10 1 2 3 — за п я т ь (N-1). Явное не-равноправие. Устранить его можно путем смены направлений просмотров , т . е. первоначально в н а п р а в л е н и и п о л у ч а е м 5 7 9 10 3 12, а затем в н а п р а в л е н и и <- результат — 3 5 7 9 10 12. Итак , чередуем направления , пока массив не будет отсор-тирован.

4. Объединить требования заданий 2 и 3 в единое целое. Этот ме-тод н а з ы в а е т с я « ш е й к е р - с о р т и р о в к о й » . Р е а л и з о в а т ь его.

5. В сортировке простыми вставками убрать переменную х, т. е. в н у т р е н н и й ц и к л з а п и с а т ь в виде: While А[0]<А[j] Do. Под-

Page 251: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 251

сказка. Массив А необходимо сделать типа Array[O..Nmax] Of Integer и i-й элемент записывать на 0 место, так называе-мый прием «барьерного элемента».

6. Метод вставок. На момент вставки элемента с номером i эле-менты массива с номерами от 1 до i - l отсортированы. Выбе-рем из них средний элемент (или один из двух средних) и срав-ним его с элементом A[i], Если А[i] меньше этого элемента, то поиск места вставки следует продолжать в левой полови-не, иначе — в правой половине отсортированного массива. Эта схема получила название сортировки бинарными вставками. Она предложена Дж. Мочлив 1946 году и была одной из пер-вых публикаций по методам компьютерной сортировки. Ре-ализовать данную схему.

7. Д. JI. Шелл в 1959 году предложил следующую модифика-цию метода — «сортировку с убывающим шагом». Ее суть по-кажем на примере. Массив из 8 элементов. Делим его на 4 группы по 2 элемента, сортируем элементы в каждой груп-пе. Затем — на 2 группы по 4 элемента, выполняем для них сортировку и, наконец, сортируем одну группу из 8 элемен-тов. При простых вставках мы перемещали элемент только в соседнюю позицию, а в этом методе перемещаем на большие расстояния, что приводит к получению более отсортирован-ного массива и, в конечном итоге, к повышению эффектив-ности метода. Значения 4, 2, 1 (приращения) не являются догмой. Можно выполнить сортировку при 5, 3, 1. Послед-ний элемент должен быть равен 1. Д. Кнут дает оценку мето-да при грамотном выборе приращений — 0(N' 2). Лучшим считается выбор в качестве значений приращений взаимно простых чисел. Реализовать «сортировку с убывающим ша-гом» для заданных значений приращений.

8. Пусть N является точным квадратом натурального числа, на-пример 3. Разделим массив на Sqrt(N) групп по Sqrt(N) эле-ментов в каждой. Выберем максимальный элемент в каждой группе... Проще рассмотреть пример. Дан массив 7 10 3 5 15 9 6 12 8. При разбивке на группы — (7 10 3) (5 15 9) (6 12 8). Максимальные элементы 1015 12. Максимальный из них 15, он во второй группе. Если оставшиеся элементы из второй группы, а это 5 и 9, меньше 10 и 12, то мы нашли сразу три элемента, записываемые на свои места. Если нет, то заменя-ем максимальные элементы элементами из группы. Этот ме-тод предложен Э. Х.Фрэндом в 1956 году и получил название метода квадратичного выбора. Количество сравнений имеет порядок 0(N*Sqrt(N)). Реализовать метод.

Page 252: Pascal 8 Класс - Окулов

252 Часть третья

З а н я т и е № 2 0 . М е т о д ы б ы с т р о й с о р т и р о в к и

План занятия • и з у ч е н и е методов с о р т и р о в к и с в р е м е н н о й о ц е н к о й

0( N*LogN ); • в ы п о л н е н и е самостоятельной работы.

Сортировка слияниями. П р е ж д е ч е м обсуждать метод, рас-смотрим с л е д у ю щ у ю задачу . Объединить («слить») упорядочен-н ы е ф р а г м е н т ы массива A A[k],...A[m] и А[т+1] A[q] в один A[k] A[q], естественно, т о ж е у п о р я д о ч е н н ы й (kmq). Основная и д е я р е ш е н и я состоит в сравнении очередных элемен-тов к а ж д о г о фрагмента , в ы я с н е н и и , к а к о й из элементов мень-ше , переносе его во вспомогательный массив D ( для простоты) и п р о д в и ж е н и и по тому фрагменту массива , из которого взят элемент . П р и этом следует не з абыть записать в D оставшуюся часть того фрагмента , к о т о р ы й не успел себя «исчерпать». Пример. Пусть п е р в ы й фрагмент состоит из 5 элементов: 3 5 8 11 16, а второй — из 8: 1 5 7 9 12 13 18 20. Р и с у н о к иллюстри-рует л о г и к у о б ъ е д и н е н и я фрагментов .

Procedure SI(к,т,q:Integer;Var A:MyArray) ; Vari, j , t:Integer;

D:MyArray; Begin

i:=k; j:=m+I; t:=l; While (i<=m) And (j<=q) Do Begin {*Пока не закончился хотя бы один фрагмент . * }

I f A[i]<=A[j] Then Begin D[t]:=A[i]; Inc(i); End

Page 253: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 252

Else Begin D[t]:=А[j]; I n c ( j ) ; End; Inc(t) ;

End;("Один из фрагментов обработан полностью, осталось перенести в D остаток другого фрагмента. *} While K=m Do Begin D[t] :=A(i] ; Incd) ; Inc(t) End; While J<=q Do Begin D[t] :=A[j]; Inc(j) ; Inc(t) End; For l: =1 To t-1 Do A [k+i-1 ] : =D [1 ] ;

End;

Параметр чп из заголовка процедуры можно убрать. Мы «сливаем» фрагменты одного массива А. Достаточно оставить н и ж н ю ю и верхнюю границы фрагментов, т. е. — Sl(k,q:Inte-ger;Var A:MyArray), где k — н и ж н я я , a q — верхняя границы. Вычисление m (эта переменная становится локальной) сводится к присвоению: m:=k+(q-k) Div 2. П р и этом уточнении приведем процедуру сортировки. Первый вызов процедуры — Sort(l,N).

Procedure Sort(i,j:Integer}; Var t:Integer; Begin

I f K j Then ("Обрабатываемый фрагмент массива состоит более, чем из одного элемента. *}

I f J-1=1 Then Begin I f A[j]<A[i] Then Begin {"Обрабатываемый фрагмент массива состоит из двух элементов. *}

t: =А [1 ] ; А [ i ] : =А [ j ] ;А [ j ] := t; Ends-End

Else Begin Sort ( i , i + ( j - i ) Div 2);{"Разбиваем заданный фрагмент на два. *} Sort (i+(j-i> Div 2 + 1, j ) ; SI (i,J,A) ;

Ends-End;

Метод слияний — один из первых в теории алгоритмов сор-тировки. Он предложен Д ж . фон Нейманом в 1945 году. В ло-гике реализован один из фундаментальных принципов инфор-м а т и к и — «разделяй и властвуй». Рекомендуется проделать с у ч а щ и м и с я «ручную» трассировку работы процедуры на раз-л и ч н ы х примерах . Это, во-первых, позволит в очередной раз глубже понять суть принципа и, во-вторых, проверить понима-ние и усвоение темы «рекурсия». Эффективность алгоритма, по Д. Кнуту, составляет C=0(N*logN).

Page 254: Pascal 8 Класс - Окулов

254 Часть третья

Быстрая сортировка. Метод предложен Ч. Э. Р . Хоаром в 1962 году. Эффективность метода достаточно высокая, кроме специально подобранных данных, поэтому автор назвал его «быстрой сортировкой».

Идея метода. В исходном массиве А выбирается некоторый элемент X (барьерный элемент). Нашей целью является запись X «на свое место» в массиве, пусть это будет место ft, такое, что слева от X были элементы массива, меньшие или равные X, а справа — элементы массива, большие X, т. е. массив А будет иметь вид: (А[ 1 ], А[2], .... A[k-1]), A[k] (X). (A[k+1], ..., А[п]).

В результате элемент A[k] находится на своем месте и ис-ходный массив Л разделен на две неупорядоченные части, барь-ером между которыми является элемент A[k ] . Дальнейшие действия очевидны — независимо сортировать полученные час-ти по той же логике до тех пор, пока не останутся части масси-ва, состоящие из одного элемента, то есть пока не будет отсор-тирован весь массив.

Пример. Исходный массив состоит из 8 элементов: 8 12 3 7 19 11 4 16. В качестве барьерного элемента возьмем средний элемент массива (7). Произведя необходимые перестановки, по-лучим: (4 3) 7 (12 19 11 8 16), элемент 7 находится на своем ме-сте. Продолжаем сортировку.

Левая часть: (3) 4 7 (12 19 11 8 16) 3 4 7 (12 19 11 8 16)

Правая часть: 3 4 7 (8) 11 (19 12 16) 3 4 7 8 11 (19 12 16) 3 4 7 8 11 12 (19 16) 3 4 7 8 11 12 (16) 19 3 4 7 8 11 12 16 19

Из вышеизложенного описания явно просматривается ре-курсивная схема реализации, параметрами которой являются нижняя и верхняя границы изменения индексов сортируемой части исходного массива. Приведем процедуру быстрой сорти-ровки из книги классика информатики Н. Вирта.

Procedure Quicksort(т,t: Integer); Vari,j,x,w: Integer; Begin

i : = m ; j;=t;x:=A[ (m+t) Div 2]; Repeat

While A [i] <x Do Incd);

Page 255: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 255

While A[j]>x Do Dec(j); I f K=j Then Begin w:=A[i]; A[i]:=A[j]; A[j]:=w; Inc (i) ; Dec ( j ) ; End

Until i>j; I f m<j Then Quicksort (m,j); I f Kt Then Quicksort ( l , t ) ;

End; Простая процедура, но возникает «тысяча и один вопрос».

Начнем. Соответствуют ли результаты работы процедуры рас-смотренному выше примеру, естественно, при тех ж е исходных данных? Оказывается , что нет. Лучше, если в этом учащиеся убедятся самостоятельно. Результаты работы процедуры вы-г л я д я т следующим образом (первые два столбца содержат пара-метры процедуры, третий — массив А).

т t А 1 8 4 12 37 19 11 8 16

1 8 4 7 3 12 19 11 8 16

1 3 4 3 7 12 19 11 8 16

1 2 3 4 7 12 19 11 8 16

4 8 3 4 7 8 19 11 12 16

4 8 3 4 78 11 19 12 16

4 5 3 4 7 8 11 19 12 16

6 8 3 4 7 8 11 12 19 16

7 8 3 4 7 8 11 12 16 19

П р о д о л ж и м н а ш и рассуждения . В процедуре есть сравнение «If i<=j Then <перестановка элементов А[i] и А[j]>». Получа-ется, что при равенстве м ы выполняем перестановку элементов массива. Эту конструкцию заменим следующей «If i<j Them. Результат — бесконечная работа процедуры («зацикливание») . Рекурсивный вызов процедуры осуществляется при сравнении «If т<] Then ...». А если просто вызвать? Результат не замед-лит сказаться . Произойдет переполнение стека адресов возвра-та. К о н с т р у к ц и я «Repeat ... Until i>j;» работает и при ;=/', что к а ж е т с я неестественным. Заменим ее на «Until i>=j>. Что про-изойдет? В цикле «Repeat... Until i>j;» еще два цикла типа Whi-le, причем по переменным внешнего цикла . Как этот факт трактует техника программирования, может быть, как «дурной тон»? П р и изучении темы «рекурсия» всегда обращается вни-мание на то, что в такой логике обязательно должна быть «за-г л у ш к а » . Как она реализована здесь? Заключительным «ак-

Page 256: Pascal 8 Класс - Окулов

256 Часть третья

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

Procedure Quicksort (т,t: Integer); Var i,j,x,w: Integer; Begin

i:=m; j:=t;x:=A[(m+t) Div 2]; While K=j Do

I f A [1] <x Then Inc (i) Else I f A[j]>x Do Dec ( j )

Else Begin w:=A[i]; A[i]:=A[j]; A[j]:=w; Inc (1);Dec(j);End;

I f m<j Then Quicksort (m,j); I f Kt Then Quicksort ( i , t ) ;

End;

С этой процедурой м о ж н о проделать « э к з е к у ц и ю » , анало-г и ч н у ю той, что в ы п о л н е н а с п р е д ы д у щ е й процедурой : изме-нить операторы сравнения , и л и п а р а м е т р ы рекурсивного вызо-ва, и л и условие ц и к л а While. В к о н е ч н о м итоге п о л у ч а е т с я интересное з а н я т и е , особенно если с о в м е щ а т ь «ручную» трас-сировку л о г и к и с п о ш а г о в ы м в ы п о л н е н и е м процедур .

О ц е н и м эффективность метода. П р е д п о л о ж и м , что р а з м е р массива равен числу , я в л я ю щ е м у с я степенью д в о й к и (N=21), и при к а ж д о м разделении элемент X н а х о д и т с я точно в середине массива . В этом случае при первом просмотре в ы п о л н я е т с я N сравнений и массив р а з д е л и т с я на две части р а з м е р а м и -N/2. Д л я к а ж д о й из этих частей N/2 сравнений и т . д. Следователь-но C=N+2*(N/2)+4*(N/4)+...+N*(N/N)=0(N*logN). Если N не я в л я е т с я степенью д в о й к и , то о ц е н к а будет и м е т ь тот ж е поря-док .

Д л я н е к о т о р ы х и с х о д н ы х д а н н ы х в р е м я сортировки пропор-ц и о н а л ь н о 0(N2). В этом случае на к а ж д о м ш а г е размер сорти-руемой части массива уменьшается только на единицу. Попробу-ем построить такие последовательности д л я различных значений N . О г р а н и ч и м с я случаем, когда и с х о д н ы м и д а н н ы м и я в л я е т с я перестановка чисел от 1 до N. Л о г и к у быстрой сортировки м о ж н о дополнить счетчиком числа сравнений , генерировать все перестановки к а к исходные д а н н ы е и з апоминать те, кото-рые дают м а к с и м а л ь н о е число сравнений . А если N большое число, например до 10000? Этот путь исследования не подходит. Попробуем по-другому. П р и N = 3 одной из искомых последовате-льностей является 1, 3, 2. При первом просмотре 3 переставляет-

Page 257: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 257

ся с 2 и рекурсивно вызывается обработка последовательности из двух первых элементов. При N=4 перестановка элементов на первой итерации д о л ж н а приводить к тому, что на второй ите-рации сортируется 1, 3, 2, а это последовательность 1, 4, 2, 3. Исходя из этого принципа , продолжим «ручное» построение последовательности. Результаты для первых значений N приве-дены в таблице.

Если с генерацией первой части последовательности слож-ностей нет (начинается с 1, а затем четные числа, начиная с 4), то вторую получить несколько сложнее. При нечетных значе-н и я х N на месте N Div 5 +1 записывается N. А затем на четных местах j числа j-1 и нечетных j — первое четное число, получа-емое в результате операции ]:-=(j+l) Div 2, из которого тоже вычитается единица. Исключения для вычитания единицы сле-дует сделать д л я значения j, равного 2. Программа генерации последовательности, д л я которой сортировка Хоара имеет вре-менную сложность О ( N 2 ) , имеет вид.

Program sequence; Var i , j , N: Integers-Begin

WriteLn (' Длина последовательности ' ) ; ReadLn (N) ; Write( '1',' ' ) ; i : =2 ; While (2*1<=N) Do Begin

Write (2*1, ' ' ) ; Inc(i) ;

End; 9—452

Page 258: Pascal 8 Класс - Окулов

258 Часть третья

I f (N>1) And Odd(N) Then Begin Write (N,' ' ) ; Inc ( l ) ;

End; While i<=N Do Begin J : =i ; While Odd(j) Do J:=(J+1) Div 2; I f j>2 Then Write ( j - 1 , ' ' ) Else Write (J , ' ' ) ; Inc (H ;

End; WriteLn;

End. Пирамидальная сортировка. Метод п р е д л о ж е н

Д ж . У. Д ж . У и л ь я м с о м и Р . У. Ф л о й д о м в 1964 году. Элемен-ты массива А образуют п и р а м и д у , если д л я всех з н а ч е н и й i вы-п о л н я ю т с я условия: A[i]<А[2*i] и A[i]<А[2*i+l]. Пример . Эле-менты массива 8 10 3 6 13 9 5 12 не образуют п и р а м и д у (А[1 ]>А[3], А[2]>А[4] и т. д.), а элементы 3 6 5 10 13 9 8 12 — образуют. Очевидно, что часть элементов массива A[N/2+1..N] независимо от их з н а ч е н и й образует п и р а м и д у . П р е д п о л о ж и м , что н а м необходимо отсортировать э л е м е н т ы массива А (8 10 3 6 13 9 5 12) по невозрастанию. Идею с о р т и р о в к и п о я с н и м с по-м о щ ь ю следующей таблицы (курсивом выделена часть элементов массива, образующих пирамиду, ж и р н ы м шрифтом — отсортиро-ванная часть массива). В конечном итоге массив отсортирован.

А Комментарии || 1 8 10 3 6 13 9 5 12 Строим пирамиду из элементов Ас 1-го по 8-й 2 8 10 3 6 13 9 5 12 3 8 10 3 6 13 9 5 12 4 863 10 13 9 5 12 5 365 10 1398 12 6 1265 10 13983 Меняем местами 1-й и 8-й элементы 7 1265 10 13983 Строим пирамиду из элементов Ас 1-го по 7-й 8 1265 10 13 983 9 1265 10 13983 10 568 10 139 123 11 1268 10 13953 Меняем местами 1-й и 7-й элементы 12 1268 10 139 53 Строим пирамиду из элементов А с 1-го по 6-й

16 9 108 12 13653 Меняем местами 1-й и 6-й элементы 17 9 10 6 / 2 / 3 6 5 3 Строим пирамиду из элементов Ас 1-го по 5-й

Page 259: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 258

Пусть м ы умеем строить пирамиду из элементов массива А от 1 до q (процедура Pyram(q)), тогда процедура сортировки имеет вид:

P r o c e d u r e S o r t ; Var t,w:Integer; Begin t:=N; Repeat

Pyram(t);("Строим пирамиду из t элементов."} w: =A [ 1 ] ; A [ 1 ] : =A [ t ] ; A [ t ]:=w;("Меняем местами 1-й и t-й элементы."} Dec (t) ;

Until t<2; End; Из таблицы мы видим, что процесс построения пирамиды

начинается с конца массива. Последние q Div 2 элементов явля-ются пирамидой по той простой причине, что удвоение индекса выводит нас за пределы массива. А затем берется очередной справа элемент и «проталкивается» по массиву до тех пор, пока он не будет меньше элемента с удвоенным индексом по отноше-нию к индексу рассматриваемого элемента. Процесс продолжа-ется до тех пор, пока мы «не поставим» на свое место первый элемент массива. Приведем текст процедуры.

Procedure Pyram(q:Integer); Var r,i,j,v:Integer;

pp:boolean; Begin

r:=q Div 2+1;("Эта часть массива является пирамидой."I While г>1 Do Begin("Цикл по элементам массива, для которых необходимо найти место в пирамиде.")

Dec (г) ; i:=r; v:=A[i];{"Индекс рассматриваемого элемента и сам элемент."}

Page 260: Pascal 8 Класс - Окулов

259 Часть третья

j:=2*i;{'Индекс элемента, с которым происходит сравнение. *} pp:=False;{*Считаем, что для элемента не найдено место в пирамиде. *} While (J<=q) And Not pp Do Begin I f j<q Then I f Л[J]>A[j+1] Then Inc ( j ) ; l *Сравниваем с меньшим элементом. *} I f v<=A[j] Then pp:=True (*Элемент находится на своем месте. *I Else Begin A [i] :=А [ j ] ; 1: =j ; j : =2*i ;Ena; {*Переставляем элемент и идем дальше по пирамиде.*}

End; A[i]:=v;

End; End;

Метод имеет эффективность порядка 0(N^logN). Однако если рассмотреть вышеприведенную логику, то окажется , что ее оценка 0(N2*logN). При сортировке процедура Pyram вызыва-ется N-1 раз. Эта процедура состоит из двух вложенных циклов сложности N и — logN. Итак, упорядочивание одного элемента требует не более logN действий, построение полной пирамиды NHogN действий. В вышеприведенной логике после к а ж д о й пе-рестановки элементов вновь строится пирамида д л я оставших-ся элементов массива. А зачем? Пирамида почти есть. Требует-ся только «протолкнуть» верхний элемент на свое место (строки таблицы с номерами 7 - 9 , 1 2 - 1 4 , 1 7 - 1 8 , 2 1 - 2 2 , 2 5 - 2 6 , 28). Вы-делим «проталкивание» одного элемента в отдельную процеду-ру Pr( r,q), где г — номер проталкиваемого элемента, q — верх-нее значение индекса.

Procedure Pr(г,q:Integer); Var г,i,j,v:Integer; pp:Boolean;

Begin i:=r; v:=A[i];(*Индекс рассматриваемого элемента и сам элемент. *) J:=2*i;{*Индекс элемента, с которым происходит сравнение. *} pp:=False;(*Считаем, что для элемента не найдено место в пирамиде. *} While ( j <=q} And Not pp Do Begin

I f J<q Then I f A [ j ] >A [ j +1 ] Then I n c ( j ) ;

Page 261: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 260

I"Сравниваем с меньшим элементом. ") I f v<=A[j] Then pp:=True {"Элемент находится на своем месте. *} Else Begin А[l]:=А[j];i:=j;j:=2"1/End; {"Переставляем элемент и идем дальше по пирамиде. *}

End; A[i]:=v;

End;

К а к и е изменения произойдут с процедурой Sor t? Procedure Sort;

Var t,ы,i:Integer; Begin

t:=N Div 2+1;{"Эта часть массива является пирамидой. *} For i:=t-l DownTo 1 Do Pr(l,N);{"Строим пирамиду (только один раз). "} For i: =N DownTo 2 Do Begin

w:=A[l];A[1]:=A[i];A[i]:=w;{"Меняем местами 1 -й и i-й элементы. *} Pr(1,1-1);{"Проталкиваем 1-й элемент.")

Ends-End;

Сортировка 5 элементов за 7 сравнений. Этот метод «быст-рее» рассмотренных ранее (5*Log5). Рассмотрим массив А из п я т и элементов. С помощью процедуры Swap по значению ин-дексов г, j переставляются два элемента.

Procedure Swap (i,j:Integer); Var t:Integer; Begin t:=A[i];A[i]:=A[j];A[j]:=t; End;

Действия , приведенные на рисунке (3 сравнения), приводят все 120 р а з л и ч н ы х первоначальных ситуаций к виду:

А[1 ]<А[2]<А[4] и А[3]<А[4]. Что м ы сделали? Сравнили попарно первые четыре элемента и сравнили максимальные элементы пар. Что осталось сделать? Вставить А[5] в отсорти-рованный массив из трех элементов, а затем вставить А[3] в от-сортированный массив из четырех элементов с учетом установ-ленного факта А[3]<А[4]. На то и другое действие отводится по два сравнения. Логика их выполнения приводится на рисун-к а х . В некоторых случаях , например если А[5]<А[4], переста-новка элементов массива не выполняется . Объясните, почему возможны такие ситуации?

Page 262: Pascal 8 Класс - Окулов

261 Часть третья

Swap(1,2) Swap(1,2) Swap(1,2)

Page 263: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 263

Такой способ сортировки пяти элементов предложен Г. Б. Де-мутом в 1956 году. Н и ж е приводится фрагмент программной р е а л и з а ц и и этой логики .

Begin

I f А[1]>А[2] Then Swap (1,2); I f А[3]>А[4] Then Swap (3,4),• I f A[2]>A[4] Then Begin Swap (2,4);Swap (1,3);End; I f A[5]<A[2] Then I f A[5]<A[1] Then Begin Swap(5,4);Swap(4,2);Swap(2,l);End Else Begin Swap(2,5);Swap (4,5); End Else I f A[5]<A[4] Then Swap(5,4);

I f A[3]<A[2] Then I f A[3]<A[1J Then Begin Swap(2,3);Swap(1,2);End

Else Swap (2,3) Else I f A[3]>A[4] Then Swap(3,4);

End.

Задания для самостоятельной работы

1. Д а н массив 7 9 13 1 8 4 10 11 5 3 6 2. Видим возрастающий участок 7 9 13, а справа, если читать справа налево, есть уча-сток 2 6. Слияние этих двух фрагментов массива дает 2 6 7 9 13. А затем «сливаем» 1 8 и 3 5 11, получаем 1 3 5 8 11. Запи-сываем в массив и получаем: 2 6 7 9 13 4 10 11 8 5 3 1. Обра-тите внимание на то, как записываем! Продолжим. Если с пер-выми фрагментами 2 6 7 9 13 и 1 3 5 8 1 1 все ясно, то вторые 4 10 и 10 перекрываются . Необходимо учесть сей факт и не дублировать 10 при записи в массив. Получаем: 1 2 3 5 6 7 8 9 11 13 10 4. Последнее «слияние» дает 1 2 3 4 5 6 7 8 9 10 11 13. Массив отсортирован. Этот метод называется у Д. Кнута «сортировкой естественным двухпутевым слиянием». Реали-зация его с использованием рекурсии доставит много хлопот, а при получении результата — удовлетворение от хорошо сде-ланной работы. При этом следует не забывать, что тестиро-вание решений должно составлять не менее 30% времени Ва-шей работы над программой!

2. Изменим схему выбора участков для «слияния». Не будем ис-кать монотонные участки, а работаем с участками фиксиро-ванной д л и н ы («простое двухпутевое слияние») . Пример. Исходный массив: 13 1 7 5 4 3 9 10 6 19 14 16 23 2 4 8.

Page 264: Pascal 8 Класс - Окулов

264 Часть третья

1-й шаг (длина 1).

2-й шаг (длина 2).

3-й шаг (длина 4).

4-й шаг (длина 8).

8 13 2 7 4 16 9 19 10 6 14 3 23 5 4 1.

1 4 8 13 3 4 14 16 19 10 9 6 23 7 5 2.

1 2 4 5 7 8 13 23 19 16 14 10 9 6 4 3.

1 2 3 4 4 5 6 7 8 9 10 13 14 16 19 23.

Реализовать данный алгоритм сортировки. Размер массива не всегда совпадает с числом, равным степени двойки. Интеграция алгоритма простых вставок с предыдущим упражнением дает новое задание. Разделим массив на уча-стки определенной д л и н ы . К а ж д ы й из них отсортируем с помощью алгоритма простых вставок, а затем используем «простое двухпутевое слияние». Так , в предыдущем приме-ре слияние используется на 3-м и 4-м шагах , а первые два за-меняются работой по алгоритму простых вставок. Обобщите предыдущие задания заменой слова «двухпутевое» на «трехпутевое» («четырехпутевое», ..., «ft-путевое»). Значи тельное увеличение программного кода при этом недопустимо

. Считается, что выбор элемента X случайным образом в мето де быстрой сортировки оставляет среднюю эффективность не изменной при всех типах массивов (упорядоченный, распре деленный по какому-то закону и т. д.). Реализуйте эту идею.

, Следует отметить, что метод быстрой сортировки является за тратным с точки зрения использования оперативной памяти Заменить рекурсивную схему реализации логики на не рекур сивную и оценить значение N, при котором Ваша программа не будет компилироваться по этой причине. Разумеется, та-кую оценку можно сделать и при рекурсивной реализации, просто тип ошибки будет другой.

. Изменить логику работы в методе пирамидальной сортиров-ки так, чтобы элементы массива Л сортировались в порядке неубывания.

. В таблице приводятся наименьшие известные значения чис-ла сравнений, достаточных для сортировки последователь-ностей из N элементов.

N 1 2 3 4 5 6 7 8 9 10 11 12 13 14 i Число сравнений 0 1 3 5 7 10 13 16 19 22 26 30 34 38 42

Разработайте алгоритмы сортировки для нескольких значе-ний N из таблицы, больших 5.

Page 265: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 265

9. Дано N отрезков [x[i], y[i]] на прямой (i=l..N). Найти мак-симальное k, д л я которого существует точка прямой , покры-т а я k о трезками . Число действий 0(N*LogN). П р и м е ч а н и е

Записать левые и правые концы отрезков в один массив. Сформиро-вать массив признаков, соответствующий концам отрезков, - 1 — левый коиец, +1 — правый конец. Отсортировать первый массив, одновременно переставляя элементы второго массива. Затем при просмотре массива подсчитывать суммы признаков. Максималь-ное значение этой суммы есть ответ задачи.

10. З н а ч е н и я м и элементов сортируемого массива я в л я ю т с я чис-л а 1, 2, 3. Сортировка элементов по неубыванию выполняет-с я с помощью попарных перестановок элементов. Отсортиро-вать массив за минимальное количество сравнений.

Page 266: Pascal 8 Класс - Окулов

266 Часть третья

З а н я т и е № 21. П о и с к д а н н ы х

План занятия • изучение р а з л и ч н ы х схем поиска данных , написание и ис-

следование программ по образцам процедур поиска ; • выполнение самостоятельной работы. Линейный поиск. Основной вопрос задачи поиска : где в за-

данной совокупности д а н н ы х находится элемент, обладающий з а д а н н ы м свойством? Большинство задач поиска сводится к простейшей — к поиску в массиве элемента с з а д а н н ы м значе-нием. Разберем его. Пусть требуется найти элемент X в массиве А. В данном случае известно только значение разыскиваемого элемента , н и к а к о й дополнительной и н ф о р м а ц и и о нем или о массиве, в котором его надо искать , нет. Начнем последовате-л ь н ы й просмотр массива и сравнение з н а ч е н и я очередного рас-сматриваемого элемента массива А с X . Н а п и ш е м фрагмент :

For i:=l То N Do I f A[i]=X Then k:=i; Находится место последнего элемента в А, равного X, при

этом массив просматривается полностью. А если первого? For i : =N DownTo 1 Do I f A[i]=X Then k:=i;

А если и с к л ю ч и т ь л и ш н и е проверки? Совпадение найдено, зачем «пустая» работа?

i : =1;

While С1 <=N) And (A[i]OX) Do Inc(i); I f i=N+l Then <X нет в A> Else « з н а ч е н и е I указывает на место совпадения>;

Этим фрагментом м ы р е ш а е м задачу поиска первого совпа-дения . Д л я н а х о ж д е н и я последнего совпадения его требуется изменить .

i:=N; While (i>0) And (A[i] <>Х) Do Dec(i); I f 1=0 Then <X нет в A> Else <значение i указывает на место совпадения>; П р и м е ч а н и я 1. Необходимо обратить внимание на порядок проверки составного условия цикла. Предполагается, что второе условие ие проверяет-ся, если результат логического выражения ясен после проверки первого условия. В противном случае возникает ошибка из-за вы-хода индекса за границы массива. 2. Использование директивы {$R+} уместно при изучении данной темы. 3. Замену цикла While иа цикл Repeat ... Until можно использовать в качестве упражнения.

Page 267: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 267

Вспомним технику «барьерных» элементов из темы «сорти-ровка» и применим ее для изучения поиска элемента в массиве. Это требует изменения типа массива A (Type MyArray=Array [O.NMax] Of Integer, или МуАттау=Array[l..NMax+l] Of In-teger-, Var A'MyArray, значение Nmax определяется в разделе констант).

i: =1 ;А [N+1 ] : =Х; While А [ 1 ] OX Do Inc(i) ; I f i=N+l Then <X нет в A> Else <значение I указывает на место совпадения>; Или для поиска последнего совпадения: 1:=N;A[0]:=Х; While A [l] OX Do Dec (i) ; I f i-О Then <X нет в A> Else <значение I указывает на место совпадения>; Очевидно, что число сравнений зависит от места нахожде-

ния искомого элемента. В среднем количество сравнений равно (N+1) Div 2, в худшем случае, если элемента нет в массиве, N сравнений. Эффективность поиска O(N).

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

Итак , требуется определить место X в отсортированном (на-пример, в порядке неубывания) массиве Л. Идея бинарного ме-тода стара к а к мир. Еще древние греки говорили — «разделяй и властвуй». Вот и делим, делим пополам и сравниваем X с эле-ментом, который находится на границе этих половин. Отсорти-рованность А позволяет, по результату сравнения со средним элементом массива, исключить из рассмотрения одну из поло-вин. А остальное, остальное технические детали, требующие очень пунктуального прописывания — необходимого, но, к сча-стью, не достаточного, навыка специалиста по информатике.

Пример. Пусть Х=6, а массив А состоит из 10 элементов:

3 5 6 8 12 15 17 18 20 25.

1-й шаг . Найдем номер среднего элемента: m . - ^ Так

как 6<А[57, далее можем рассматривать только эле-менты, индексы которых меньше 5. 3 5 6 8 12 15

Page 268: Pascal 8 Класс - Окулов

267 Часть третья

2-й шаг. Рассматриваем л и ш ь первые 4 элемента массива, нахо-Г1 + 41 дим индекс среднего элемента этой части т=\ =2, L 2 J

6>А[2], следовательно, первый и второй элементы из рассмотрения исключаются: 3-ё 6 8 12 16 17 18 20 25.

3-й ш а г . Р а с с м а т р и в а е м два элемента , значение

3 4 6 8 12 16 17 18 20 26; А [ 3 ] = 6 . Элемент найден, его номер — 3. П р о г р а м м н а я р е а л и з а ц и я бинарного поиска может иметь

с л е д у ю щ и й вид . Procedure Search;

Var х,j,m:Integer; f:Boolean; Begxn

i:=l; j:=N;{*Ha первом шаге рассматривается весь массив. * } f:=False;{'Признак того, что X не найден.*) Whxle (x<=j) And Not f Do Begxn m:=(x+j) Div 2;{*Или m:=1+ ( j - x ) Dxv 2;, так как x+(j~x) Dxv 2= (2*i+ ( j - l ) ) Div 2={i+j) Dxv 2. *) I f A[m]=X Then f:=True {*Элемент найден. Поиск прекращается. *}

Else I f A [m] <X Then x : =m+l { *Исключаем из рассмотрения левую часть А. *] Else j:=m-l; {*Правую часть. *)

End; End;

П р е д л о ж и м еще одну, рекурсивную, р е а л и з а ц и ю изучаемого поиска . Значение переменной t я в л я е т с я индексом искомого элемента и л и ноль, если элемент не найден .

Procedure Search(i,j:Integer;Var t : I n t e g e r ) ; {*Массив А и переменная X - глобальные величины*} Var m : I n t e g e r s -Began

I f i>j Then t:=0 Else Begin m:=(i+j) Div 2;

I f Aim] <X Then Search (m+1, j , t ) Else I f A[m]>X Then Search (i,m-l, t )

Else t:=m; End;

End;

Page 269: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 268

Каждое сравнение уменьшает диапазон поиска приблизите-льно в два раза . Общее количество сравнений имеет порядок 0(N*logN ).

Рассмотрим обратную задачу. Дана позиция элемента (Pos) в неупорядоченном массиве А из N элементов и количество допу-стимых сравнений (L). Определить все интервалы значений N в диапазоне от 1 до 10000, при которых элемент А с номером Pos может быть найден за L сравнений с помощью алгоритма би-нарного поиска.

Массив А, к а к таковой, в решении задачи не требуется. Не-обходимо проверять , совпадает ли средний элемент отрезка в схеме бинарного поиска со значением Pos и за сколько сравне-ний это совпадение достигается при заданном значении N. Оформим модифицированную схему бинарного поиска как фун-кцию Сап.

Function Can(N: Integer): Boolean; Var i,p,q,ll: Integers-Begin

p ;= 0; q := N-1; 11 := 0; While p <= q Do Begin

i := (p+q) shr 1; Inc (11) ; I f l = Pos Then Begin

Can := (11=L) ; Exit; End

Else I f l > pos Then q := l-l Else p := 2+1; Ends-Can := False;

End; Поясним схему работы функции данными ручной трассировки

логики ее работы для различных значений N при Pos= 10 и Ь=3.

Page 270: Pascal 8 Класс - Окулов

270 Часть третья

И т а к , т о л ь к о одно з н а ч е н и е N (из р а с с м о т р е н н ы х в т а б л и ц е ) у д о в л е т в о р я е т у с л о в и ю з а д а ч и ( N = 1 2 ) . Осталось о р г а н и з о в а т ь ц и к л по к о л и ч е с т в у э л е м е н т о в в м а с с и в е и з а п о м и н а т ь интерва -л ы з н а ч е н и й , у д о в л е т в о р я ю щ и х у с л о в и я м п о и с к а ( ф у н к ц и я Сап в н у т р е н н я я по о т н о ш е н и ю к п р о ц е д у р е Solve).

Procedure Solve; Const MaxN = 10000;

MaxCnt = Max N Shr 1; Var l , j : Integer;

Res: Array [1 . .MaxCnt,1. .2] Of Integer; Pos, L,Cnt: Integer; Begin

ReadLn(Pos, L); Cnt := 0; i := Pos; Repeat

j := i ; While ( j <= MaxN) And Can(j) Do m c ( j ) ; I f l < j Then Begin

inc(Cnt) ; Res[cnt,1] := i ; Res[cnt,2] := j-1; {'Запоминаем интервал значений. *}

End; i : = j+l;

Until (i >= MaxN); WriteLn (cnt) ; For i : = 1 To Cnt Do WriteLn ( Res[i,l ] , ' \ Res[1,2]) ;

End;

Ответом з а д а ч и п р и P o s = 1 0 и L = 3 я в л я ю т с я 4 и н т е р в а л а : 12-12 , 17-18 , 29 -30 , 87 -94 , а п р и P o s = 9 0 0 0 и L = 2 т а к и х интер-валов нет .

Случайный поиск. О р г а н и з а ц и я п о и с к а k-ro э л е м е н т а в неу-п о р я д о ч е н н о м массиве X в о з м о ж н а с л е д у ю щ и м образом. Выби-р а е т с я с л у ч а й н ы м образом элемент с н о м е р о м д. Массив X раз-бивается на т р и ч а с т и : э л е м е н т ы , м е н ь ш и е X[q], р а в н ы е X[q] и б о л ь ш и е Х[q], А з атем , в з а в и с и м о с т и от к о л и ч е с т в а элемен-тов в к а ж д о й ч а с т и , в ы б и р а е т с я одна и з ч а с т е й д л я д а л ь н е й ш е -го п о и с к а . Т е о р е т и ч е с к а я о ц е н к а ч и с л а с р а в н е н и й имеет поря-док k*N, т . е. д л я х у д ш е г о с л у ч а я N2, но н а п р а к т и к е он работает з н а ч и т е л ь н о быстрее .

Page 271: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 270

F u n c t i o n Search(N,к:Integer;X:MyArray):Integer; ("N - количество элементов в массиве X, к - номер по порядку разыскиваемого элемента. *)

Var 1,cnl,cne,cnm,q:Integer; Sl,Se,Sm:MyArray;

Begin I f N=1 Then Search:=X[1] Else Begin

FillChar(SI,SizeOf(SI),0); FillChar (Se, SizeOf (Se) , 0) ; FillChar (Sm, SizeOf (Sm) , 0) ; cnl:=0;cne:=0;cnm:=0; q: =Integer (Random (N)) +1 ; For l;=1 To N Do{"Разбиение на три части. *}

I f X[i)<X[q] Then Begin Inc(cnl); SI [cnl ] :=X[i ] ; End

Else I f X[i}=X[q] Then Begin Inc(cne); Se[cne]:=X[1];End

Else Begin Inc(cnm);Sm[cnm]:=X[l];End; I f cnl>=k Then Search:=Search (cnl,k,SI)("Выбор части для дальнейшего поиска.*) Else I f cnl+cne>=k Then Search:=X[q]

Else Search:=Search(cnm,k-cnl-cne,Sm); End;

End; Поиск элемента массива за линейное время. Для реализации

этой схемы поиска требуется уметь сортировать массив из 5 элементов за 7 сравнений.

m

Идея поиска поясняется рисунком. Из отсортированных пя-тиэлементных массивов формируется массив из средних элемен-тов — медиан (М). Он на рисунке изображен в упорядоченном виде. В массиве М выбирается средний элемент ш и относительно него исходный массив разбивается на три части, к а к в преды-

Page 272: Pascal 8 Класс - Окулов

272 Часть третья

д у щ е й схеме случайного п о и с к а . В ч а с т и А на р и с у н к е выделе-н ы э л е м е н т ы , заведомо б о л ь ш и е т , в ч а с т и В — м е н ь ш и е т. К а к м и н и м у м 1 / 4 часть элементов исходного м а с с и в а исключа-ется и з д а л ь н е й ш е г о п о и с к а за счет однократного п р и м е н е н и я действий этой с х е м ы .

Function Search (N, к: Integer ;Х -.MyArray) : Integer ; {*N — количество элементов в м а с с и в е X, к — номер элемента (k<N). *)

Var i , j,cnl,спе,cnm,q,r:Integer; M,SI,Se,Sm,Y:MyArray;

Begin I f N<=5 Then Begin Sort(N,X) ; Search : =X[k],-End {*Если количество элементов в массиве меньше или

равно 5, то сортируем элементы массива за 7 с р а в н е н и й и выбираем k-й элемент. *) Else Begin

i:=1;г:=0; While i<=N Do Begin

3-0; While <з<=5) And (i+j<=N) Do Begin

Inc(3);Y[3]:=X[1+3-1];{*Разбиваем на пятиэлементные массивы (не более). *}

End; Inc (i,5) ; Sort ( j , Y ) ; Inc (r) ; M[r]:=Y[g Div 2+1];{'Средний элемент отсортированного массива У записываем в массив М. *}

End; q: =Search (г, г D i v 2,М) ,-{'Находим средний по значению элемент в массиве медиан.*) cnl:=0;спе:=0;cnm:=0; FillChar(SI,SizeOf(SI),0); FillChar(Se,SizeOf(Se),0); FillChar(Sm,SizeOf(Sm),0); For i:=l To N Do {'Разбиваем массив на три части.*)

I f X[i]<q Then Begin Inc(cnl); SI[cnl]:=X[i]; End

Else I f X[i]=q Then Begin Inc(cne); Se[cne]:=X[i];End

Page 273: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 273

Else Begin Inc(cnm);Sm[cnm]:=X[l];End; I f cnl>=k Then Search:^Search(cnl,k,SI) f"Выбор части массива для дальнейшего поиска. *} Else I f cnl+cne>=k Then Search:=X[q]

Else Search:=Search (cnm,k-cnl-cne,Sm); End;

End;

Оценим в р е м я работы алгоритма T(N). После поиска элемен-та m не менее половины найденных медиан будут больше или равны т . Следовательно, по крайней мере половина pV/5~j групп дадут по три чнсла , больших и л и р а в н ы х т, кроме, может быть, последней группы и группы, содержащей т. Итак , 3*(

элементов исключаются из дальней-ш е й обработки. Остается 7*А?/10+6 элементов. Поиск среднего элемента в массиве медиан (выделен ж и р н ы м шрифтом) требу-ет T(N/5) времени. Р е к у р с и в н ы й вызов процедуры поиска (вы-делен ж и р н ы м ш р и ф т о м ) п р и разбивке массива требует !T(7*iV/10+ +6). Остальные шаги алгоритма, в к л ю ч а я сортиров-к у 5 элементных массивов, выполняются за время O(N). Таким образом, T(N)<T(N/5)+T(7*N/10+6)+0(N). Сумма коэффи-циентов в правой части 1 / 5 + 7 / 1 0 = 9 / 1 0 меньше 1, согласно тео-реме ( занятие 17) T(N)<c*N д л я некоторой константы с.

П р и м е ч а н и е

В разных источниках приводятся различные схемы оценки време-ни работы этого алгоритма и значения N (50 или 70), начиная с ко-торых время поиска пропорционально O(N). П р о д о л ж и м тему бинарного поиска. Рассмотрим следующую

задачу. Д а н целочисленный массив М из N элементов (N — не-четно и 5<Д'<1499). З н а ч е н и я элементов массива различны и п р и н и м а ю т значения от 1 до N. Пример: N= 5, А=2 , 5, 4, 3, 1. Требуется найти номер элемента i такой, что количество эле-ментов М , м е н ь ш и х M [ i ] , равно количеству элементов, боль-ш и х , чем M[i]. Д л я примера i равно 4. Единственной разре-шенной операцией при поиске является Med3, которая вызыва-ется с тремя р а з л и ч н ы м и номерами элементов массива в каче-стве аргументов и возвращает номер элемента, имеющего сред-нее значение. Д л я нашего примера Med3{ 1,2,3) возвращает 3, МеёЗ(ЗАЛ) возвращает 4 и Med3(4 ,2 ,5) возвращает 4. Ответ 4.

Пусть есть следующий массив М .

Page 274: Pascal 8 Класс - Окулов

274 Часть третья

Все, что мы можем сделать с помощью операции Med3, это разбить массив на три части, выбрав случайным образом два несовпадающих номера (а и b) элементов. Не деление пополам, к а к в бинарном поиске, а деление на три части — «тринарный» поиск. Пусть а=5 и Ь= 12. Просматриваем все номера элемен-тов, помеченные признаком True. В начальный момент време-ни для всех элементов этот признак равен True. Если операция Med3(a,b,i) дает в качестве результата:

• значение а, то помечаем элемент с номером i к а к элемент, п р и н а д л е ж а щ и й левой части (признак _Left)\

• значение г, то помечаем элемент с номером г как элемент, принадлежащий средней части (признак _Middle)\

• значение Ь, то помечаем элемент с номером г как элемент, п р и н а д л е ж а щ и й правой части (признак _Right).

В таблице буквами L, М , R обозначены признаки _Left, _Midlle, _Right. Буквой N обозначен признак _None, элемент не рассматривается на данном шаге анализа .

Итак, разбили, а что дальше? Напрашивается единственно разумный ход — выбрать одну из трех частей и продолжить по-иск (другого не дано). Вся сложность задачи в выборе парамет-ров поиска и в аккуратном переходе от одного этапа поиска к другому. Какую часть выбрать для следующего этапа поиска? Мы имеем в левой части результата поиска 4 элемента, в пра-вой — 4 и в средней (без учета а и Ь) — 3. Первоначально осуще-ствлялся поиск элемента со значением 7 в массиве из 13 элемен-тов. После этого этапа необходимо искать элемент со вторым значением среди элементов, помеченных признаком Middle. Часть параметров поиска определена: количество и признак эле-ментов, среди которых ищется искомый; номер элемента. Еще один параметр — одно из предыдущих значений а или Ь — требу-ет пояснений (это самый сложный момент). Поясним рисунком.

Page 275: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 274

Оказалось, что поиск следует продолжать в левой (по отно-шению к а) части. Генерируем новые значения а Ь (обозначены как an, Ьп). Если после этого средний элемент (Med3(an ,bn ,ac ) ) находится не на месте Ьп, то структура последующего поиска нарушается . Необходимо поменять значения у an и Ьп. Если после первого этапа поиска м ы выяснили, что его следует про-должать в левой части, то и следующее деление на три части должно осуществляться с учетом этого результата.

П р и м е ч а н и я 1. Прервите чтение и проделайте ручной просчет поиска по данным из условия задачи без смены значений. Результат уже на этом про-стом примере окажется отрицательным. 2. Сделайте еще два рисунка и выясните особенности использова-ния функции Med3 при продолжении поиска в средней и правых частях. Program Median;f"Без ввода элементов исходного массива М и функции Med3. ")

Const MaxN = 1500; Type TDir = (_None, _Middle, _ L e f t , __Right) ; Var N: Integers-

Mas : Array [l..MaxN] Of TDir; I t , Rt : Integer; ("Количество элементов в подмножествах *)

Procedure MakeMiddle(Dir: TDir);{"Помечаем элементы, среди которых осуществляется поиск. ")

Var i: Integer; Begin

For l := 1 To N Do I f Mas [i] = Dir Then Mas[i] := ^Middle Else Mas[i] := _None;

End; Procedure Swap(Var a,b: Integer);

Var c: Integer; Begin с := a; a := b; b := c; End; Procedure GetNext (Var a,b: Integer; len: Integer); { "Генерируем номера а и b."I

Var i,t,ca,cb: Integer; Begin

t := 0; ca Random (len) +1; cb Random (len) +1; Whi le ca=cb Do cb := Random (len) +1;

Page 276: Pascal 8 Класс - Окулов

276 Часть третья

For i := 1 То N Do {'Сложность в том, что номера оставшихся элементов для поиска идут не подряд. ' )

I f Mas [i] =_Middle Then Begin I n c ( t ) ; I f ca = t Then a := I ; I f cb = t Then b i ;

End; End;

Procedure Doit(Dir: TDir; Res, len, lb: Integer); {'Основная процедура, параметры поиска описаны ранее. *)

Var a, b, 1, t: Integer; Begin

MakeMiddle(Dir); {'Выделяем элементы, среди которых осуществляется поиск. *} I f len = 1 Then Begin a := 1; While Mas[a]<> __Middle Do inc (a);

WriteLn (a) ; End

Else Begin GetNext (a,b,len); I f (Dir=_Right) Then Begin I f M e d 3 ( l b , a , b ) о a Then Swap (a,b) End Else I f Med3(a,b,lb) <> b Then Swap(a,b); {'Корректируем значения а и b с учетом предыдущего поиска. Пояснение приведено на рисунке в тексте.') I f len = 2 Then Begin I f Res = 1 Then WriteLn (a) Else WriteLn(b) End

Else Begin I t := 0; rt := 0; Mas[a] := _None; Mas [b] :=_None; For l := 1 To n Do

I f Mas[i] = _Middle Then Begin t := Med3<a,b,i); I f t = b Then Begin i n c ( r t ) ; Mas[i] := _Right End('Пишем в правую часть. *) Else I f t = a Then Begin m c ( l t ) ; M a s f i J : = _ L e f t End;

End;

Page 277: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 277

I f Res = 11+1 Then WriteLn (a) Else (*Искомый элемент попал на границу

интервала, поиск завершен. *} I f Res = len-rt Then WriteLn (b) Else I f Res < lt + 1 Then Doit (_Left,Res, I t , a) { "Поиск с новыми параметрами. *} Else I f res < len-rt Then Doit (_Middle, Res-It-1,len-lt-rt-2,b) Else Doit(_Right,Res-len+rt,rt,b);

End; Ends-

End; Begin

ReadLn (N) ; { "И ввод исходного массива "} Randomize; FillChar(Mas, SizeOf(Mas), _Middle) ; Doit (_Middle,N shr 1+1, N, 1) ;{ "Первый вызов, N shr 1+1 - среднее значение, 1- условно считаем, что ранее был первый номер (обеспечивает правильный поиск). *)

End.

Поиск второго минимального элемента в массиве. Известно, что второй м и н и м а л ь н ы й элемент в массиве из N элементов можно найти за N+\~LogN~\-2 сравнений. Так, при N=8 это 9 сравнений. Попробуйте свои силы, не читая дальнейшее изло-жение , самостоятельно найдите алгоритм решения задачи. Оче-видно, что без поиска минимального элемента найти второй ми-нимальный невозможно. Поиск минимального элемента требует N-1 сравнений, от этого никуда не уйти. Осталось \LogN~\-1 сравнение. Если искать по прежней схеме, исключив найден-ный элемент, то потребуется N-2 сравнения. Однако при этом м ы как бы забываем, что делали на первом этапе. Единственно возможный (логически) вывод заключается в том, чтобы испо-льзовать результаты поиска минимального элемента для поис-ка второго минимального элемента. Но к а к это сделать? Пусть N=8 и элементы массива А имеют значения: 81, 34, 2, 90, 51, 45, 14, 31. На рисунке изображена схема поиска минимального элемента. Сравниваем соседние пары элементов, а затем срав-ниваем минимальные элементы пар и т. д. Если убрать записи в круглых скобках на рисунке, то это обычный поиск минима-льного элемента в массиве за N-1 сравнение (напишите проце-дуру такого поиска).

Page 278: Pascal 8 Класс - Окулов

278 Часть третья

8 34 2 90 51 4 5 14 31

V V V V 34(81) 2(90) 45(51) 14(31)

2(34,90) 14(45,31)

2(14,34,90)

А сейчас обратимся к з а п и с я м в к р у г л ы х скобках . Рассмот-рим, например , 2(34,90) . Почему м ы не в к л ю ч и л и 81? Причина в том, что этот элемент не может быть вторым м и н и м а л ь н ы м элементом. Только, 34 и 90 кандидаты на это место. Точно так ж е и 14(45, 31). Элемент 51 исключается из «цепочки» претен-дентов. Последнее 7-е сравнение оставляет в этой «цепочке» 2(14, 34, 90) только три элемента, ибо 45 и 31 не могут претен-довать на эту роль, 14 заведомо их меньше, а этот элемент включен в «цепочку». Итак , в списке 3 элемента, за 2 сравне-н и я м ы находим второй м и н и м а л ь н ы й элемент, общее количе-ство сравнений равно 9. Постройте точно такую ж е схему для массива из 16 элементов. Через 15 сравнений в последней «це-почке» о к а ж е т с я 4 элемента. За 3 сравнения м ы найдем второй м и н и м а л ь н ы й элемент. Итого, 18 сравнений.

Перейдем к реализации логики . Описание д а н н ы х програм-мной реализации имеет вид:

Program SearchTwoMin;{ *Без ввода исходных данных, вывода результата и основной программы. *)

Const MaxN=l0000 ; Type MyArray=Array[1..MaxN] Of Integer; Var A, IndNext, IndFirst: MyArray; f*Массив

элементов. Номер следующего элемента цепочки. Номера первых элементов цепочек. *}

N: Integer;{'Количество элементов в массиве А. *} Cntlf: Longlnt;{*Счетчик числа сравнений.*) res: Integer;{*Второй минимальный элемент. *)

Предположим, что после поиска минимального элемента в дополнительном массиве IndNext записаны индексы (номера) элементов последней цепочки, например, так : *, 4, 7, 0, *, *, 2, * а индекс минимального элемента массива (а это 3 для рас-сматриваемого примера) является значением переменной Ind First[1 ]. Символом «*» обозначено не интересующее нас значе-ние, оно может быть любым, а 0 — признак конца «цепочки».

Page 279: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 279

Задача р е ш е н а . Просмотр элементов последней «цепочки» по этим д а н н ы м реализуется с помощью следующей процедуры.

Procedure Search; Var t: Integer; Begin

t:^IndNextГIndFirst[l]];{"Длч примера значением t является 7. Определяем номер первого элемента «цепочки». ") r e s : = А [ t ] ; { " Н а ч а л ь н о е присвоение. "} t:=IndNext[t];{"Переход ко второму элементу «цепочки». *} While t<>0 Do Begin Inc (CntIf);("Изменяем значение счетчика числа сравнений."} I f A[t]<res Then res:-A[t];{"Изменяем значение минимального элемента."} t: -IndNext[t];{"Переадресация к следующему элементу «цепочки»."}

Ends-End;

Осталось сформировать (создать) з н а ч е н и я элементов масси-вов IndNext и IndFirst в процессе поиска минимального эле-мента массива А (напомним его значения д л я рассматриваемого примера : 81, 34, 2, 90, 51, 45, 14, 31). В начальный момент времени у нас 8 цепочек по одному элементу. После первого ш а г а работы получаются 4 цепочки по два элемента. Номер первого элемента первой цепочки 2 ( I n d F i r s t [ l ] ) , номер следу-ющего элемента 1 (IndNext[2]). Номер первого элемента вто-рой цепочки 3 ( I n d F i r s t [ 2 ] ) , номер следующего элемента 4 (Ind Next[3]). Номер первого элемента третьей цепочки 6 (Ind-First[3]), номер следующего элемента 5 (IndNext[6]). Номер первого элемента четвертой цепочки 7 (IndFirst[4]), номер сле-дующего элемента 8 (IndNext[7]). Результаты действий на пер-вом шаге обработки приведены в таблице.

IndFirst IndNext

Начальные значения 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0

A[lndFirst[1]] и A[lndFirst[2]] 2 2 3 4 5 6 7 8 0 1 0 0 0 0 0 0 A[lndFirst[3]] и A[lndFirst[4]] 2 3 3 4 5 6 7 8 0 1 4 0 0 0 0 0 A[lndFirst[5]] и A[lndFirst[6]] 2 3 6 4 5 6 7 8 0 1 4 0 0 5 0 о I A[tndFirst[7]] и A[lndFirst[8]] 2 3 6 7 5 6 7 8 0 1 4 0 0 5 8 0

Page 280: Pascal 8 Класс - Окулов

280 Часть третья

В результате осталось 4 элемента , точнее 4 ц е п о ч к и эле-ментов, номера их первых элементов я в л я ю т с я з н а ч е н и я м и Ind .Firsf[ l ] - IndFirst[4]. После второго ш а г а остается две ц е п о ч к и элементов .

IndFirst IndNext j

|| Начальные значения 2 3 6 7 0 1 4 0 0 5 8 0

A[lndFirst[1J] и A[lndFirst[2]] 3 3 6 7 0 4 2 0 0 5 8 0 | A[lndFirst[3]] и A[lndFirst[4]] 3 7 6 7 0 4 2 0 0 8 6 0 J

Н о м е р первого элемента первой ц е п о ч к и 3 ( I n d F i r s t [1 ]), за-тем 2 {IndNext[3]) и 4 (IndNext[2]). Н о м е р первого элемента второй ц е п о ч к и 7 ( I n d F i r s t [ 2 ] ) , з атем 6 (IndNext[7]) и 8 (Ind Next[6]). Осталось последнее с р а в н е н и е э л е м е н т о в A[Ind First[ 1]] и A[IndFirst[2]]. Элементы массива IndNext после к о р р е к т и р о в к и ссылок п р и м у т з н а ч е н и я : 0 , 4 , 7 , 0 , 0 , 8 , 2 , 0 , а IndFirstf 1 ] останется р а в н ы м 3. З н а ч е н и е А[7] больше А[3], про ц е п о ч к у седьмого элемента забываем, его в к л ю ч а е м пер-в ы м элементом в ц е п о ч к у третьего элемента , а старое значение IndNext[3] с тановится з н а ч е н и е м IndNext[7].

Procedure Create; Var l , I f , r g , t : Integer; Begin

Cntlf:=0;("Счетчик числа сравнений. *} FillChar(IndNext, SizeOf(IndNext), 0); { 'Начальные значения элементов массива IndNext. *) For l: =1 To N Do IndFirst[l]:=i;{* Начальные значения элементов массива IndFirst. *) t:=N; While t>l Do Begin ("Номер шага, t Div 2 -количество сравнений на данном шаге. Для примера: 8, 4, 2. *)

1: =2 ; While K-t Do Begin

Inc(Cntlf);("Изменяем значение счетчика числа сравнений.*) If:=i-1; rg:=1-1; I f A[IndFirst[l-l]]<A[IndFirst[i]] Then r g:=i Else lf:=i;(*B сравниваемой паре элементов массива A, rg - номер большего, a I f - номер меньшего элементов, определяемых по значению из IndFirst. *}

Page 281: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 281

{ *Следующие три строки кода - самый сложный момент логики. Связываем цепочки сравниваемых элементов массива А. Изменяем номер первого элемента новой цепочки. *)

IndNext[IndFirst[rg]]:=IndNext [IndFirst[If]1; IndNext[IndFirst[If]]:=IndFirst[rg]; IndFirst[l Div 2]:=IndFirst[lf]; Inc(i, 2) ;

End; I f t Mod 2=1 Then IndFirst[ (t+1) Div 2]: = IndFirst[t];{*Учитываем нечетность значения N. *) t : = (t+1) v 2;

Ends-End;

Основная программа, как обычно, состоит из вызова процедур: Begin

Imt; ( *Ввод исходных данных. *} Create; Search; Done; { *Вывод результата . *}

End Хеширование. В предыдущем материале рассматривался по-

иск, основанный на сравнении значений элементов массива с некоторым заданным значением переменной X (ключом). В ко-нечном итоге определялся адрес, номер элемента в массиве, где записано данное значение X . Рассмотрим простую задачу. Ко-личество различных буквосочетаний длиной не более 4 симво-лов из 32 букв русского алфавита чуть больше 106 . Мы проана-лизировали какой-то текст и выписали в соответствующий массив все встреченные буквосочетания. После этого на вход нашей простой системы анализа текста поступает некоторое буквосочетание. Нам необходимо ответить, встречалось оно ра-нее и л и нет. Очевидно, что каждому буквосочетанию соответст-вует некоторый цифровой код. Поэтому задача сводится к поис-ку в массиве некоторого цифрового значения — ключа. Эта задача рассмотрена ранее (разумеется, что можно хранить и сами буквосочетания). При линейном поиске, в худшем случае для ответа на вопрос «есть или нет», потребуется 106 сравне-ний, при бинарном — порядка 50. Можно ли уменьшить коли-чество сравнений и ослабить требования по оперативной памя-ти, резервировать не 106, а, например, 103 элементов памяти?

Page 282: Pascal 8 Класс - Окулов

282 Часть третья

Обозначим цифровой аналог буквосочетания символом R, а ад-рес в массиве (таблице) через А. Если будет найдено преобразо-вание f(R), дающее значение А, такое, что

• f выполняется достаточно быстро; • случаев, когда f(R1)=f(R2), будет минимальное количест-

во, то задача поиска трактуется совсем иначе, чем рас-смотрено выше. Этот способ поиска называется хеширова-нием (расстановкой ключей, рандомизацией и т. д.). Итак, ключ преобразуется в адрес, в этом суть метода. Требова-ние по быстродействию очевидно - одна из целевых уста-новок любого алгоритма, ибо быстродействие компьютера ограничено. Второе требование не столь очевидно. Пред-положим, что анализируется текст из специфической стра-ны «Д...», Е которой все слова начинаются с букЕЫ О, и в качестве f выбран цифровой аналог первой буквы. В этом случае получаем одно значение А для всех слов (от чего уходили, к тому и пришли, но в стране «Д...» все возмож-но). Следовательно, необходимо выбирать такое преобра-зование f , чтобы количество совпадений адресов (А) для различных значений ключей (R) , это называют коллизия-ми, было минимально. Полностью избежать коллизий не удается. Для любого преобразования f (хеш-функции) мож-но подобрать такие исходные данные, что рассматривае-мый метод поиска превращается в линейный поиск. Одна-ко цель построения хеш-функции ясна — как можно более равномерно «разбрасывать» значения ключей по таблице адресов (ее размер обозначим через М) так, чтобы умень-шить количество коллизий. Итак, реализация метода ло-гически разбивается на две составляющие:

• построение f; • схему разрешения коллизий. Коротко рассмотрим их, резюмируем известные факты из

теории информатики. При построении хеш-функции, так или иначе, для значения

ключа R находится цифровой аналог, обозначим его через t. В первом способе в качестве М выбирается простое число и нахо-дится остаток от деления t на М (t Mod М). Во втором способе находится значение t2. Из этого значения «вырезается» q ка-ких-то разрядов (лучше средних). Значение q выбирается таким образом, чтобы 21 было равно М. В третьем способе w разрядов для представления ключа t разбиваются на части длиной q раз-рядов. Выполняется, например, логическое сложение этих час-тей, и его результатом является номер элемента в таблице адре-сов. Считается (если не заниматься подбором специальных

Page 283: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 282

входных данных), что все эти способы дают достаточно хорошие, в смысле количества возможных коллизий, хеш-функции.

Из известных методов р а з р е ш е н и я к о л л и з и й рассмотрим один — «метод цепочек» . Его суть в том, что элементом табли-ц ы я в л я е т с я с с ы л к а на список элементов, и м е ю щ и х одно зна-чение х е ш - ф у н к ц и и . З р и т е л ь н ы й образ этого метода представ-лен на рисунке .

Качество х е ш - ф у н к ц и и оценивается средней длиной спис-ков. Работа со списками обсуждается позднее, поэтому перене-сем вопрос о разрешении коллизий данным методом на после-д у ю щ и й материал .

Рассмотрим следующую простую задачу. Дано N (1<N<15000) чисел т и п а L o n g l n t и таблица не очень большого размера. Оце-ним качество работы хеш-функций по среднему и максималь-ному значению количества р а з л и ч н ы х чисел, для которых в ре-зультате преобразования получается один и тот же адрес в таблице . Построим три различные хеш-функции и проверим их на с л у ч а й н ы х тестах. Д л я первичного анализа первой хеш-фун-к ц и и приведем п о л н ы й текст решения .

($R+) Program Му21_1;

Const InputFile-'Input.Txt'; MaxN=15000;I *Максимальное значение количества чисел. *} МахТЫ=547; I "Размер таблицы. ")

Var ТЫ: Array [0. .МахТЫ] Of Longlnt; Count,Dif:Array[0. .МахТЫ] Of Integer; {"Подсчитываем количество совпадающих ключей и количество различных, но дающих один адрес в таблице. *}

Page 284: Pascal 8 Класс - Окулов

284 Часть третья

N:Integer; Procedure RandFile;{"Создание файла из N чисел. "}

Var i:Integer; Begin

Randomizer-Assign (Output,InputFile);Rewrite(Output) ; N: ^Integer(Random(MaxN) )+1; WriteLn (N); For l : =1 To N Do WriteLn(Longlnt (Random(65000)) "29999); Close(Output);

End; Procedure Solve;

Var l,Ind:Integer; nm:Longlnt;

Begin F i l l C h a r ( D i f , S i z e O f ( D i f ) , 0 ) ; FillChar (ТЫ,SizeOf (ТЫ) , 0) ; Assign (Input,InputFile) , - R e s e t ( I n p u t ) ; ReadLn(N); For l : =1 To N Do Begin

ReadLn(nm);l"Читаем число. *) Ind:=nra Mod МахТЫ;{"Вычисляем адрес в таблице. *} I f ТЫ[Ind]=0 Then Begin ТЫ[Ind]:=nm; Count[Ind]:=1; Endf"Первая запись в таблицу по этому адресу."}

Else I f пт=ТЫ [Ind] Then Inc (Count [Ind] ) Else Inc(Dif[Ind]);{"Коллизия - различные числа дают один адрес в таблице. ")

End; Close ( I n p u t ) ;

End; Procedure I s s l ;

Var s , i , Max,r:Integer; Begin

s:=0;Max:=0; For i:=l To МахТЫ Do Begin

Inc (s,Dif[i]);("Подсчитываем количество коллизий.") I f Dif[i]>Max Then Max:=Dif[l];("Определяем максимальное значение количества коллизий."}

Page 285: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 284

End; r:=Round (s/МахТЫ) ; Assign (Output, 'Con') ;ReWrite (Output) ; WriteLn (N,' ' , r, ' ' ,Max) ;

End; Begin

RandFile;("Формирование файла из случайных чисел. *) Solve;( "Анализ файла с помощью различных хеш-функций.") Issl;{"Подсчет среднего и максимального значений коллизий. *)

End. Решения для второй и третьей функций требуют изменения

одной строки в процедуре Solve — Ind:=GetNum(nm), где Get-Num обеспечивает вычисления адреса элемента таблицы. Для второй хеш-функции GetNum имеет вид:

Function GetNum (nw: Longlnt) : Integer; Var w,q:Longlnt;

t: 0. . МахТЫ ; Begin

g: = (nw And $FFFF0000 ) Shr 1 6;("Выделяем старшие разряды числа и сдвигаем на 16 разрядов."} w: = (nw And $FFFF) ; ("Выделяем младшие разряды числа. *} t : = (Integer (q"w) And $7 FCO) Shr 6; ("Выделяем 9 бит произведения чисел. *} GetNum:=t;

End; Вычисление третьей хеш-функции можно организовать сле-

дующим образом. Function GetNum (nw: Longlnt) :Integer;

Var r, t: Integer; Begin

r: =In teger (nw And $1FF) ;( "Выделение младших 9 бит числа. *} t:=Integer (nw And $3FE00 Shr 9) ;("Выделение следующих 9 бит числа. *) r:=r Xor t;("Первый шаг формирования адреса элемента таблицы. *} t: =In teger (nw And $7 FC0000 Shr 18) ;( "Выделение следующей группы разрядов числа. ")

Page 286: Pascal 8 Класс - Окулов

286 Часть третья

r : = r Xor t; t:=Integer(nw And SF8000000 Shr 21) ;["Выделение оставшихся разрядов. *) r:=r Xor t; GetNuml:=r;

End;

В таблице приведены результаты подсчета для трех рассмат-риваемых х е ш - ф у н к ц и й .

Данные в таблице дают общую «картинку» работы хеш-фун-кций , и не более. Д л я сравнительного анализа необходимо по крайней мере исследовать их работу на одном и том файле из случайных чисел. Измените программу так , чтобы это требова-ние выполнялось , а т а к ж е д л я каждой хеш-функции формиро-вались оценки Ncpedl iee, rcpedHee,

Maxсреднее • Сравните работу хеш-функций по этим оценкам, например , для 100 р а з л и ч н ы х фай-лов с исходными данными.

Задания для самостоятельной работы

1. Во входном файле дан массив А и массив из элементов, по-иск которых будет осуществляться в массиве А. Изменить массив А т а к и м образом, чтобы суммарное количество срав-нений при поиске элементов было м и н и м а л ь н ы м . П р и м е ч а н и е По данвым из второго массива формируются частоты поиска каж-дого элемента и элементы массива А сортируются в порядке убы-вания значений этих частот («метод перемещения в начало»). За-дача достаточно легко превращается в небольшое исследование. Необходимо найти значения N, при которых массивы не помеща-ются в оперативную память, отводимую под программу в системе программирования.

Page 287: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 287

2. Дан массив А и число X. Переставить элементы массива та-к и м образом, чтобы вначале были записаны элементы, мень-шие или равные X, а затем — большие или равные X . Переставить элементы массива т а к и м образом, чтобы внача-ле были записаны элементы, меньшие X , затем равные X и, наконец , большие X.

П р и м е ч а н и е Указанные перестановки следует сделать за один просмотр А и без использования дополнительных массивов.

3. Найти реализацию (рекурсивную и нерекурсивную) бинарно-го поиска с использованием «барьерной» техники.

4. Н и ж е по тексту приведены три Еерсии процедуры бинарного поиска . К а к а я из них верная? Исправьте ошибки. К а к а я из них более эффективная?

Procedure Search; Var i, j , к: Integer;

l9:=l,J =N;

k:=(i+j) Div 2; If X<=A[k] Then j-=k-l; If A[k]<=X Then l•=k+l,

Until i>j, End,

Procedure Search; Var I, 7, k: Integer;

l:=l; j:=N; Repeat k: = (i+j) Div 2 If A[k]<X Then l:=k Else ]:-k;

Until lA[k]-Xl Or (i>=j 1; End;

5. Дан массив из N различных целых чисел. Разрешенными опе-рациями я в л я ю т с я сложение двух элементов массива и срав-нение сумм. Найти наибольший элемент массива за минима-льное количество сравнений.

6. Пусть Х[1М] nY[l..N] — два возрастающих массива из раз-личных элементов. Найти за время 0(LogN) средний элемент множества , полученного объединением этих массивов.

Procedure Search, bar j , k: Integer,

i:=I, j-=N;

If X<AU] then J.-K Else I:-k-H, Until i>=j;

Page 288: Pascal 8 Класс - Окулов

288 Часть третья

З а н я т и е № 22 . Д в у м е р н ы е м а с с и в ы . Р а б о т а с э л е м е н т а м и

План занятия П структура двумерного массива и его описание; П шаблон для решения задач на двумерные массивы; • экспериментальная работа с программами поиска макси-

мального элемента в массиве, формирование значений эле-ментов одномерного массива, поиска элементов с опреде-ленными свойствами, заполнения массива по заданным правилам;

П выполнение самостоятельной работы. Массивы, положение элементов в которых описывается дву-

мя индексами, называют двумерными. Логическая структура такого массива может быть представлена прямоугольной мат-рицей. Каждый элемент матрицы однозначно определяется ука-занием номера строки и номера столбца. Образом памяти компь-ютера в наших предыдущих рассуждениях был одномерный массив ячеек. Как происходит отображение логической струк-туры двумерного массива в физическую? Известны два способа: отображение строками и отображение столбцами. В конкретной системе программирования используется один из них. Рассмот-рим первый, как обычно на примере. Пусть есть двумерный массив А из целых чисел (тип Integer — 2 байта). Количество строк равно 5, количество столбцов — 4. Пусть первый элемент А[1,1] записан в ячейке с номером 1000. Вычислим адрес А[4,3]. Addr(A[4,3])= 1000+2*(4*(4-l)+(3-l))=1028. Что мы делали? В каждой строке записано по 4 элемента. В трех стро-ках — 12 элементов, в 4-й строке до 3-го элемента записано 2 элемента. Складываем и умножаем на 2, ибо 2 байта необходи-мо для хранения одного элемента. В общем случае для массива A[N,M] из элементов, занимающих V байт памяти, формула имеет вид: Addr(A[i,j])=Addr(A[l ,1 ] )+V*(M*(i-l )+(j-l) ).

Двумерный массив на языке Турбо Паскаль определяется по-разному.

Примеры: Const MaxN=>...; МахМ=...;{*Максимальные значения количест-

ва строк и столбцов двумерного массива.*} Type OMyArray=Array[l.MaxM] Of /Ше#ег;{*Одномерный мас-

сив из целых чисел.*} ТMyArray=Array[1..MaxN] Of ОМуАггоу;{*Одномерный мас-

сив, элементами которого являются одномерные мас-сивы из целых чисел.*}

Page 289: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 288

или Type ТMyArray= Array [ 1 ..MaxN, 1..МахМ] Of Integer; ^Двумер-

ный массив из целых чисел.*} В учебнике отдается предпочтение второму способу описа-

ния двумерного массива.

Д л я работы с двумерными массивами изготовим шаблон. Он включает ввод данных из файла и вывод элементов двумерного массива в файл . Размеры двумерного массива вводятся из фай-ла. Далее по тексту, если этого не требует задача, процедуры ввода и вывода двумерного массива не приводятся.

($R + ) Program Му22_1; Const MaxN=l0; MaxM=8; Type TMyArray = Array [1..MaxN,l..MaxM] Of Integer; Var A:TMyArray;

N,M:Integer; Procedure TInit(Var v,w:Integer;Var X:TMyArray);

Var i , J : In teger; Begin Assign (Input, 'Input. Txt') ; Reset (Input) ; ReadLn(v,w);{*B первой строке файла записаны значения N и М. *} For 1:=1 То v Do

For j:=l То w Do Read(X{l,j]) ; Close (Input) ;

Ends-Procedure TPrmt (v, w: Integer;X: TMyArray) ;

Var l , j : In teger; Begin Assign(Output,'Output.Txt'); Rewrite(Output); For l:=1 To v Do Begin

For j:=l To w Do Write (X [ i , j] , " ) ; WriteLn; End;

Close (Output) ; Ends-

Begin TInit (N,M,A) ; TPrint (N,M,A) ;

End.

10—452

Page 290: Pascal 8 Класс - Окулов

290 Часть третья

Массив просматривается так , к а к это п о к а з а н о на рисунке: элементы первой строки , второй и до N-й.

Отметим еще одну технологическую особенность работы, точ-нее напомним, ибо она у ж е рассматривалась . Экран должен быть разбит на три окна , которые одновременно обозримы. Из-менение значений во входном файле (не забывайте его записы-вать) и запуск п р о г р а м м ы приводят к и з м е н е н и ю выходного файла . Эти и з м е н е н и я видны без д о п о л н и т е л ь н ы х операций по переходу от одного окна к другому. Т е к у щ и м д о л ж е н быть ка-талог с ф а й л а м и программы, Input.Txt и Output.Txt. Н а з в а н и я входного и выходного файлов , естественно, могут быть други-ми.

В о з м о ж н о формирование массива с использованием д а т ч и к а с л у ч а й н ы х чисел и путем ввода д а н н ы х с к л а в и а т у р ы , но м ы не будем останавливаться на этом.

П р и м е ч а н и е

Обычно значения размерности массива вводят с клавиатуры, а сам массив — из файла. «Разрыв» по данным. Процедура TInit полно-стью обеспечивает ввод массива — это законченный фрагмент логи-ки с одной точкой входа и одной точкой выхода. Фрагмент работа-ет с конкретными данными и взаимодействует с остальной логикой по определенным правилам. Разрыв не логичен как по управле-нию, так и по данным. Структуризация задачи должна осуществ-ляться как по управлению, так и по данным, иначе какой же переход к объектно-ориентированному программированию? Если на уровне деклараций или формального ознакомления с конструкциями или инструкциями, то это не наш путь.

Экспериментальный раздел работы

1. Н а й т и первый м а к с и м а л ь н ы й элемент массива и его индек-сы. Procedure Search(v,w:Integer;X:TMyArray; Var smax,simax,sjmax:Integer);

Page 291: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 291

Var 1,у:Integer; Begin

smax : =X [1,1] ; simax:=1; sjmax:=l; For l:=1 To v Do

For J:=Z To w Do I f X[i,j]>smax Then Begin

smax: =X [i, j ] ; simax:=i; sjmax:=j;

Ends-End;

П р о ц е д у р а Append о т к р ы в а е т с у щ е с т в у ю щ и й ф а й л д л я добавле-н и я в него и н ф о р м а ц и и . Описание — A p p e n d ( V a r f:Text). Пере-м е н н а я f я в л я е т с я файловой переменной типа Text, которая д о л ж н а быть с в я з а н а с в н е ш н и м файлом с помощью процедуры Assign. Процедура Append о ткрывает существующий в н е ш н и й файл , и м я которого поставлено в соответствие переменной / . Если ф а й л у ж е о т к р ы т , то сначала он з акрывается , а затем от-к р ы в а е т с я повторно. У к а з а т е л ь т е к у щ е й позиции (вспомните головки ч т е н и я , з аписи в магнитофоне) при этом устанавлива-ется на к о н е ц данного ф а й л а .

Если сама процедура поиска первого максимального элемен-та массива в р я д ли вызывает вопросы, то д л я вывода результа-та в ф а й л Output.Txt н а ш и х знаний не хватает . Мы м о ж е м пе-реопределить вывод и значение м а к с и м а л ь н о г о элемента с его и н д е к с а м и выводить на э к р а н . Д л я этого необходимо:

Assign(Output,'Con'); Rewrite(Output);

и н а п и с а т ь оператор WriteLn(Max," '.iMax,' \jMax). Однако массив в ы в о д и т с я в ф а й л Output.Txt, а результаты работы — на э к р а н . Где л о г и к а ?

Воспользуемся процедурой Append. Добавление к основной программе м о ж е т иметь и такой вид:

Search (п,т,А,Мах, iMax, jMax) ; Append (Output); WriteLn(Max:5,iMax:5,jMax:5) ;

Page 292: Pascal 8 Класс - Окулов

292 Часть третья

И з м е н и т е п р о ц е д у р у Search д л я п о и с к а последнего максима-льного э л е м е н т а , д л я п о и с к а всех м а к с и м а л ь н ы х элементов и и х и н д е к с о в .

2. С ф о р м и р о в а т ь о д н о м е р н ы й массив , к а ж д ы й элемент которо-го равен сумме о т р и ц а т е л ь н ы х элементов соответствующей с т р о к и з а д а н н о й ц е л о ч и с л е н н о й м а т р и ц ы .

З а д а ч а требует в в е д е н и я одномерного массива , поэтому при-ведем о п и с а н и е д а н н ы х , п р о ц е д у р ы подсчета с у м м ы отрицате-л ь н ы х элементов в строке и д о б а в л е н и я д а н н ы х в выходной ф а й л , а т а к ж е основную п р о г р а м м у .

{$R + ) Program Му22_2;

Const MaxN=10; МахМ=8; Type TMyArray=Array[1..MaxN,1..MaxM] Of Integer;

OMyArray=Array[1..MaxN] Of Integer; Var A:TMyArray;

Sum:OMyArray; n,m:Integer;

Procedure SumNeg(v, w:Integer;X:TMyArray;Var Ssum: OMyArray);

Var l , j:Integer; Begin

For i:=1 To v Do Begin Ssum[l]:=0; For j :=1 To w Do

I f X[i,j]<0 Then Inc(Ssum[i], X [ l , j] ) ; End;

End; Procedure O P r i n t ( v : I n t e g e r ; O S u m : O M y A r r a y ) ;

Var i:Integer; Begin

Append(Output); For i : =1 To v Do Write (OSum[i] : 4) ; WriteLn;

End; Begin("Основная программа. *}

TInit(n,m,A); TPrmt (n,m,A) ; SumNeg (n,m, A, Sum) ; OPrint(n,Sum);

End.

Page 293: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 293

3. О п р е д е л и т ь , есть л и в м а с с и в е э л е м е н т , р а в н ы й 0.

Function SNeg(v,w:Integer;X:TMyArray):Boolean; Var l,j:Integer;

pp:Boolean; Begin pp:=False; For l:=1 To v Do

For j:=1 To w Do I f X[i,j]=0 Then pp:=True;

SNeg:=pp; End;

Ф р а г м е н т и з основной п р о г р а м м ы .

Append (Output) ; I f SNeg(n,m,A) Then WriteLn('Yes') Else WriteLn('No') ;

Н е д о с т а т о к ф у н к ц и и SNeg. П у с т ь п е р в ы й элемент массива Х [ 1 , 1 ] р а в е н н у л ю . П р о с м о т р м а с с и в а п р о д о л ж а е т с я , н е с м о т р я на его бессмысленность . Устранение неточности — «задача опре-д е л я е т т и п и с п о л ь з у е м ы х к о н с т р у к ц и й п о в т о р е н и я » — требует и с п о л ь з о в а н и я о п е р а т о р о в While и л и Repeat — Until в ф у н к ц и и Sneg. П р о д е л а й т е это.

4 . Определить , я в л я е т с я л и д а н н ы й к в а д р а т н ы й массив симмет-р и ч н ы м относительно своей главной диагонали . Элементы на г л а в н о й д и а г о н а л и х а р а к т е р и з у е т с я с о в п а д е н и е м з н а ч е н и е м индексов — i=j, на второй i=N-j+l, где N — размерность мас-сива .

Главная диагональ Вторая диагональ, но не главная

Page 294: Pascal 8 Класс - Окулов

294 Часть третья

Function Sim(v,w:Integer;X:TMyArrav):Boolean; Var i,j:Integer;

pp:Boolean; Begin pp:=True; For i:=2 To v-1 Do

For j:=i+l To w Do I f X[i, j]<>X[j,1] Then pp:=False;

Sim:=pp; End;

И з м е н и т е ф у н к ц и ю т а к , ч т о б ы с и м м е т р и ч н о с т ь м а с с и в а про-в е р я л а с ь о т н о с и т е л ь н о в т о р о й д и а г о н а л и . В п р е д ы д у щ е м при-мере б ы л р а с с м о т р е н н е д о с т а т о к ф у н к ц и и SNeg, Он п р и с у щ и этой ф у н к ц и и . У с т р а н и т е его.

5. В массиве А р а з м е р н о с т ь ю п*т к э л е м е н т а м ч е т н ы х столбцов п р и б а в и т ь элемент первого столбца с о о т в е т с т в у ю щ е й строки .

Procedure Change ( v, w:Integer;Var X:TMyArray); Var l , j : Integer; Begin For i:=1 To v Do For j : =1 to w Div 2 Do Inc(X[i,2*j ] , X [ i , 1]) ; End;

Особенностью р е ш е н и я з а д а ч этого т и п а я в л я е т с я в о з м о ж -ность и з м е н е н и я э л е м е н т а , к о т о р ы й д о л ж е н и с п о л ь з о в а т ь с я в обработке . Н а п р и м е р , п р и б а в л я е м к э л е м е н т а м н е ч е т н ы х столб-цов. М о д и ф и к а ц и я X[i,2*j-1 ] п р и в е д е т к т о м у , что э л е м е н т ы 3, 5 и т . д . с толбцов у в е л и ч а т с я на у д в о е н н о е з н а ч е н и е элемен-тов первого с т о л б ц а . П р о в е д и т е к о р р е к т н о е и з м е н е н и е процеду-р ы Change.

6 . З а п о л н и т ь м а с с и в А р а з м е р о м п*т с л е д у ю щ и м о б р а з о м , на-п р и м е р п=6 и т=8:

1 2 3 4 5 6 7 8 16 15 14 13 12 11 10 9 17 18 19 20 2 1 22 2 3 24 32 3 1 30 29 28 27 26 2 5 33 34 35 36 37 38 39 40 48 47 46 45 44 43 42 4 1 То есть з а п о л н я е т с я в виде

Page 295: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 295

Правило заполнения. Е с л и н о м е р с т р о к и — нечетное число , то A[i,j]=(i-l)*m+j, и н а ч е — A[i,j]=i*m-j+l.

Procedure Fill(v, w:Integer;Var X: TMyArray); Var l , j : Integers-Begin

For i:=1 To v Do For j:=1 To w Do

I f l Mod 2=1 Then X[i, j ] : = ( i - l ) *w+j Else X[i,j]:=i*w-j+l;

End;

З а п о л н и т е м а с с и в по с л е д у ю щ и м п р а в и л а м .

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

1 3 4 10 11 21 2 5 9 12 20 22 6 8 13 19 23 30 7 14 18 24 29 31 15 17 25 28 32 35 16 26 27 33 34 36

1 0 2 0 3 0 0 5 0 6 0 7 8 0 9 0 10 0 0 12 0 13 0 14

1 12 13 24 25 36 2 11 14 23 26 35 3 10 15 22 27 34 4 9 16 21 28 33 5 8 17 20 29 32 6 7 18 19 30 31

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

З а м е т и м , что ш а х м а т н у ю д о с к у удобно представить в виде д в у м е р н о г о м а с с и в а р а з м е р о м 8*8. К о о р д и н а т ы ф е р з я опреде-л я ю т с я д в у м я ч и с л а м и (номер с т р о к и и номер столбца) , но в ш а х м а т а х п р и н я т о в в о д и т ь б у к в у и число . Б у к в а отвечает за н о м е р с т р о к и , а ч и с л о — за номер столбца . П о э т о м у не будем о т с т у п а т ь от т р а д и ц и й и введем к о о р д и н а т ы и м е н н о т а к и м об-р а з о м . В п р о г р а м м е с д е л а е м п р о в е р к у к о р р е к т н о с т и ввода и если все п р а в и л ь н о , то переведем б у к в у в соответствующее ей ч и с л о ( ' а ' — 1, 'Ь ' — 2, ' с ' — 3 , ' d ' — 4 , V — 5, ' f ' — 6, ' g ' -7 , ' h ' - 8 ). Д л я р е ш е н и я з а д а ч и и с п о л ь з у е м р я д свойств ш а х м а т -ной д о с к и . Все д и а г о н а л и д о с к и д е л я т с я на в о с х о д я щ и е и ни-с х о д я щ и е .

Page 296: Pascal 8 Класс - Окулов

На к а ж д о й диагонали выполняется свойство: • д л я клеток любой восходящей диагонали сумма номера

строки и номера столбца постоянна ( i + j = C o n s t ) \ • для клеток любой нисходящей диагонали разность номера

строки и номера столбца постоянна ( i - j = C o n s t ) . Проверьте , что д л я в о с х о д я щ и х д и а г о н а л е й сумма индек-

сов и з м е н я е т с я от 2 до 16, а д л я н и с х о д я щ и х — разность от - 7 до 7.

Program Му22_3; Const MaxN=8;MaxM=8; Type TMyArray=Array[1.MaxN,1..МахМ] Of Integer; Procedure TInit(v,k, 1: Integer; VarX: TMyArray);

Var l , j: Integer; Begin

For l:=1 To v Do For j:=1 To v Do

I f (i=k) Or (j=l) Or (i+j=k+l) Or (i-j=k-l) Then X[i,j]:=l

Else X[l , j ] :=0; { *1 - клетка под боем, 0 — нет. *} X[k,1]:=2; {*В этой клетке находится ферзь. *}

End; Procedure Solve;

Var A: TMyArray; c: Char;str, stl: Integer; Begin Assign(Input,'Input.Txt'); Reset(Input); Assign(Output,'Output.Txt'); ReWrite(Output); ReadLn(c, stl);{* Координаты ферзя записаны в файле Input.Txt.*)

Page 297: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 297

I f (с<' а') Or (c>'h') Or (stl<l) Or (stl>MaxN) Then WriteLn {'Некорректный ввод.') Else Begin

str:= Ord(ch) -Ord('a') +1; {"Преобразуем символ в номер строки. *) TInit(MaxN,str,stl,A); TPrint(MaxN,MaxM,A);{"Процедура совпадает с ранее рассмотренной. *)

End; Close (Input) ; Close (Output) ;

End; Основная программа состоит из одной строки — вызов про-

цедуры Solve. Измените решение так, чтобы фиксировались клетки доски, находящиеся под боем хотя бы одной из фи-гур — ферзя и ладьи.

Задания для самостоятельной работы

1. Дан двумерный массив. Найти сумму и количество элемен-тов в каждом столбце: • кратных к ; или к2

ш, • попадающих в интервал от А до В; • я в л я ю щ и х с я простыми числами; • положительных и л е ж а щ и х выше главной диагонали.

2. Дан двумерный массив. Найти: • сумму элементов в строках с по к2\ • номера всех максимальных элементов; • номера первых отрицательных элементов каждой строки

(столбца); • номера последних отрицательных элементов каждой стро-

к и (столбца); • количество элементов в каждой строке, больших (мень-

ших) среднего арифметического элементов данной стро-ки;

• номера первой пары неравных элементов в каждой стро-ке.

3. Д а н ы два к в а д р а т н ы х массива А и В. Вывести тот из них, у которого след меньше (сумма элементов главной диагона-ли) .

4. Дан двумерный массив. Определить: • есть ли в данном массиве отрицательный элемент; • есть ли два одинаковых элемента;

Page 298: Pascal 8 Класс - Окулов

298 Часть третья

• есть ли данное число А среди элементов массива; • есть ли в заштрихованной области массива элемент, рав-

ный А (массив имеет размерность п*п):

h J2

к. — ... .......... —

к2

5. Дан двумерный массив. Определить, есть ли в данном масси-ве строка (столбец): • состоящая только из положительных элементов; • состоящая только из положительных или нулевых эле-

ментов; • состоящая только из элементов, больших числа А; • состоящая только из элементов, принадлежащих проме-

жутку от А до В. 6. Дан двумерный массив. Выполнить следующие преобразова-

ния с ним: • в каждой строке сменить знак максимального по модулю

элемента на противоположный; • последний отрицательный элемент каждого столбца за-

менить нулем; • положительные элементы умножить на первый элемент

соответствующей строки, а отрицательные — на послед-ний;

• заменить все элементы строки с номером k и столбца с номером I на противоположные по знаку;

• к элементам столбца с номером прибавить элементы столбца k2.

7. Написать программу, определяющую поля шахматной доски, находящиеся под боем коня.

8. Ввести координаты ферзя и коня и определить: • бьют ли фигуры друг друга; • если конь ходит первым, то бьет ли он ферзя; • бьет ли ферзь коня, если первый ход ферзя.

9. Составить программу заполнения и вывода в файл таблиц сло-жения и умножения цифр в заданной (произвольной) систе-ме счисления. Для десятичной системы счисления они име-ют вид:

Page 299: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 298

Ц 0 _

63 72 81

10. Даны два двумерных массива одинаковой размерности. Со-здать третий массив той же размерности, каждый элемент ко-торого равен: • сумме соответствующих элементов первых двух; • 1 (True), если соответствующие элементы массивов име-

ют одинаковый знак, иначе элемент равен 0 (False). 11. Дан двумерный массив. Сформировать одномерный массив,

к а ж д ы й элемент которого равен: • произведению четных, положительных элементов соот-

ветствующего столбца; • количеству элементов соответствующей строки, больших

данного числа; • наибольшему по модулю элементу соответствующего

столбца; • количеству отрицательных элементов, кратных 3 или 5,

соответствующей строки; • первому четному элементу соответствующего столбца, если

такого нет, то нулю.

Page 300: Pascal 8 Класс - Окулов

Часть третья

12. Дан двумерный массив. Определить: • есть ли в данном массиве строка, в которой ровно два от-

рицательных элемента; • есть ли в данном массиве столбец, в котором имеются

одинаковые элементы; • есть ли в данном массиве строка, в которой имеется два

максимальных элемента всего массива; • есть ли в данном массиве столбец, в котором равное ко-

личество положительных и отрицательных элементов; • есть ли в данном массиве строка, содержащая больше по-

ложительных элементов, чем отрицательных. 13. Сформировать двумерные массивы по следующим правилам:

14. Определить, является ли массив (N*N) магическим квадра-том (значения элементов массива из интервала от 1 до N*N), то есть суммы по всем горизонталям, вертикалям и двум диа-гоналям должны совпадать. Примеры магических квадратов.

24

Page 301: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 301

Поскольку сумма элементов магического квадрата равна l+2+3+...+N2=N2*(N2+l)/2, то соответствующие суммы равны N*(N2+1 )/2. Так , при N=5, в каждой строке, столбце и на главных диагоналях сумма элементов равна 5*(52+1)/2=65. 15. Дан двумерный массив (N*N). Зеркально отобразить его эле-

менты относительно: • горизонтальной оси симметрии; • вертикальной оси симметрии; • главной диагонали; • побочной диагонали.

Page 302: Pascal 8 Класс - Окулов

302 Часть третья

З а н я т и е № 2 3 . Д в у м е р н ы е м а с с и в ы .

В с т а в к а и у д а л е н и е

План занятия • и з у ч е н и е р а з л и ч н ы х методов в с т а в к и и у д а л е н и я элемен-

тов двумерного массива ; • в ы п о л н е н и е самостоятельной работы.

Экспериментальный раздел работы

1. Вставить строку из н у л е й после с т р о к и с номером t . Д л я ре-ш е н и я этой з адачи необходимо: • п е р в ы е t с т р о к оставить без и з м е н е н и я ; • все с т р о к и после t-й сдвинуть на одну; • э л е м е н т а м строки t+1 присвоить заданное значение .

В процедуре ввода д а н н ы х кроме ввода р а з м е р о в массива следует предусмотреть ввод номера строки — ReadLn(n,m,t). К л ю ч е в а я процедура в с т а в к и строки имеет вид :

Procedure InsertSti(v,w,г:Integer;Var X:TMyArray); Var l,j:Integer;

Begin For i:=v DownTo ri 1 Do For j : =1 To w Do X[i + l , j ] :=X[ 1,3] ; For 3 :=1 To w Do X[r+l,j]:=0;

End;

И з м е н и т е процедуру д л я вставки с т р о к и перед строкой с за-д а н н ы м номером.

2. Вставить строку и з нулей после всех строк, содержащих мак-с и м а л ь н ы й элемент массива . П р и в е д е м п о л н ы й текст реше-н и я (для н а п о м и н а н и я о н а ш е й схеме р е ш е н и я задач) .

($R + ) Program Му23_1;

Const MaxN=8; MaxM=9; Type TMyArray=Array[1..2*MaxN,1..MaxM] Of Integer; (* резервируем 2*MaxN строк на тот случай, если максимальный элемент массива есть в каждой строке. *} Var А:ТМуАггау;

п,m:Integer; Procedure TInit(Var v,w:Integer;Var X: TMyArray);

Var 1,3:Integer; Begin

Page 303: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 302

Assign (Input, 'Input. T x t ' ) ; Reset (Input) ; ReadLn (v, w) ; For l:=1 To v Do

For j:=l To w Do Read ( X [ i , j ]) ; Close (Input) ;

Ends-Procedure TPrmt (v, w: Integer ;X: TMyArray) ;

Var i,j-.Integer; Begin

Assign (Output, 'Output. T x t ' ) ; Rewrite (Output) ; For i:=l To v Do Begin

For j : =1 To w Do Wri te ( X [ i , j ) : 4) ; WriteLn; End; Close (Output) ;

Ends-Procedure InsertStr (v, w,r: In teger; Var X:TMyArray);

Var l , j : Integer; Begin

For i:=v DownTo r + 1 Do For j : =1 To w Do X[i + l , j ] :=X[i,j] ;

For J : =2 To w Do X [r+1, j ] -.=0 ; End;

Function TMax (v,w: Integer ;X: TMyArray) -.Integer; (*Поиск максимального элемента. *) Var l,j,max:Integer; Begin

max: =X[1,1 ] ; For i:=1 To v Do

For j :=1 To w Do I f X[i,j]>max Then max:=X [ l , j ] ;

TMax:=max; Ends-

Procedure Solve(Var v:Integer;w:Integer;Var X: TMyArray);

Var i,j,smax:Integer; Begin

smax:=TMax(v,w,X) ; ("Находим максимальный элемент. *} i:=l; While i < = v Do B e g i n (*Почему и с п о л ь з у е м э т о т тип цикла?*}

Page 304: Pascal 8 Класс - Окулов

304 Часть третья

j:=1; While (J<=w) And (X[ 1, j J O s m a x J Do I n c ( j ) ; I f jow+1 Then Begin (*B строке есть максимальный элемент. *]

InsertStr(v,w,l,X);{*Вставляем строку. *} Inc(v) ;{"Увеличиваем количество строк. *] Inc (1,2) ("Пропускаем вставленную строку. *) End

Else Inc(i) ;("Обычное изменение индекса. *) End;

End; Begin {"В основной программе только вызовы процедур - крупных « к и р п и ч и к о в » . * }

TInit(n,m,A); S o l v e ( n , m , A ) ; TPrmt (n,m,A) ;

End.

Измените решение д л я вставки нулевых строк, перед стро-к а м и , с о д е р ж а щ и м и м а к с и м а л ь н ы й элемент массива.

3. Удалить строку с номером t . Д л я того, чтобы удалить строку с номером t , необходимо

сдвинуть все строки, н а ч и н а я с данной, на одну вверх. Послед-нюю строку можно «обнулить», м о ж н о не рассматривать к а к строку массива.

Procedure DelStr(v, w,г:Integer;Var X:TMyArray); Var i,j:Integer;

Begin For i:=r To v-1 Do

For j:=l To w Do X[ i , j } : =X [ i +1, j ] ; For j:=l To w Do X[v,j] :=0;

End; 4. Удалить строки, содержащие м а к с и м а л ь н ы й элемент масси-

ва .

Procedure Solve(Var v:Integer/w:Integer; Var X:TMyArray);

Var i,j,smax:Integer; Begin

smax:=TMax (v,w,X); l: =1 ; While i < = v Do Begin

Page 305: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 304

While (]<=w) And (X [ 1, j ] Osmax) Do I n c ( j ) ; I f ]<>w+l Then Begin DelStr(v,w,i,X) ; Dec (v) ; End

Else Inc (l) ; End;

End;

Сравните эту процедуру с процедурой Solve, рассмотренной ранее при вставке строк, и объясните отличия .

4. П о м е н я т ь местами элементы столбцов с номерами и 1г-

С процедурой Swap м ы уже встречались. Напомним ее. Procedure Swap (Var х, у: Integer);

Var z: Integer; Begin

z:=x; x:=y; у: = z ; End;

Написание процедуры, реализующей требования примера, не вызывает сложностей.

Procedure Change(v,stl,st2:Integer; Var X:TmyArray);

Var i:Integer; Begin

For i: =1 To v Do Swap (X[l, stl ], X[l, st2] ) ; End;

5. Удалить все строки и столбцы, содержащие максимальный элемент массива.

2 7 9 3 4

5 6 9 0 1

2 1 9 5 8

7 6 9 4 3

5 6 9 2 1

Рассмотрим пример. Максимальный элемент равен 9. Если выбрать логику просмотра по строкам, а затем по столбцам, то результат окажется верным. При просмотре же вначале по столбцам, а затем по строкам исключается только третий стол-бец. Выход из ситуации состоит в запоминании номеров строк

Page 306: Pascal 8 Класс - Окулов

306 Часть третья

и столбцов, содержащих м а к с и м а л ь н ы й элемент массива. Пусть м ы з а п о м н и л и в массивах NumStr и NumStl номера у д а л я е м ы х строк и столбцов. Д л я п р и м е р а они и м е ю т вид: (1, 2, 3, 4, 5) и (3, 3, 3, 3, 3). П я т ь раз у д а л я т ь третий столбец явно нет смыс-ла , поэтому из массивов необходимо удалить п о в т о р я ю щ и е с я э л е м е н т ы , т. е. во втором массиве оставить одну 3. Однако этим не о г р а н и ч и в а ю т с я в о з н и к а ю щ и е сложности . П е р в а я строка удалена , требуется у д а л я т ь вторую, но она у ж е в н а ш е й матри-це стала первой. Необходимо после к а ж д о г о у д а л е н и я изменить номера строк (столбцов) в массиве NumStr — у м е н ь ш и т ь на е д и н и ц у те номера , которые больше номера удаленной строки. Н и ж е по тексту приведена только процедура Solve, остальные части п р о г р а м м ы совпадают с ранее рассмотренными.

Procedure Solve(Var v, w:Integer;Var X:TMyArray); Type OMyArray=Array[1..MaxN] Of Integer; ("Определяем одномерный массив для хранения номеров удаляемых элементов. Константа MaxN берется при условии, что количество строк в матрице всегда больше количества столбцов.*) Var 1,smax:Integer;

NumStr,NumStl:OMyArray;(*Номера удаляемых строк и столбцов. *} ykStr,ykStl:Integer;{"Количество элементов в массивах NumStr и NumStl. *}

Procedure FNumfVar q,r:Integer;Var Y, Z: OMyArray) ; (*Формирование массивов с номерами удаляемых с т р о к и столбцов. *)

Var 1,j:Integer; Begin

q:=0;r:=0; For l:=1 To v Do

For j:= 1 To w Do I f X[I,j]=smax Then Begin Inc (q) ;Y[q] ,-=i; Inc (r);Z[r]:=j;

End; End;

Procedure Sz (Var t:Integer; Var X:OMyArray); {*Удаление и з м а с с и в а повторяющихся элементов.*} Var i,j,1:Integer; Begin

i : =1;

Page 307: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 307

While K t Do Begin З - i + l ; While j<=t Do

I f X[i]=X[j] Then Begin

For l:=j To t-1 Do X[l]:=X[1+1]; X[t]:=0; Dec ( t ) ;

End Else I n c ( j ) ;

I n c ( i ) ; Ends-

End; Procedure Change(t,a:Integer; Var X:OMyArray); {"Уменьшение на единицу значений элементов

массива, превышающих заданную величину.*} Var 1:Integer; Begin

For i:=l To t Do I f X[i]>a Then Dec(X[I]);

End;

Begin {"Текст основной процедуры."j FillChar(NumStr,SizeOf(NumStr) , 0 ) ; ("Инициализация массивов."} FillChar(NumStl,SizeOf(NumStl) ,0) ; smax:=TMax (v, w,X) ; ( "Находим максимальное значение - текст функции не приводится."} FNum(ykStr,ykStl,NumStr,NumStl);("Определяем номера удаляемых строк и столбцов."} Sz (ykStr, NumStr) ; ("Удаляем повторяющиеся элементы."} Sz (ykStl,NumStl); i : =1 ;

While i<=ykStr Do Begin DelStr(v,w,NumStr[l],X);{"Удаляем строку."} Change(ykStr,NumStr[i],NumStr);{"Корректируем массив номеров строк. *) Dec (v) ; Inc ( i ) ;

End; i : = 1 ; While K=ykStl Do Begin

DelStl(v,w,NumStl[i],X);("Удаляем столбец."}

Page 308: Pascal 8 Класс - Окулов

308 Часть третья

Change (ykStl,NumStl[±],NumStl) ; ("Корректируем массив номеров столбцов.*} Dec(w); Inc(i);

End; End;

О т е х н о л о г и и н а п и с а н и я . Н а п о м и н а е м , что процесс разра-ботки п р о ц е д у р ы Solve з а к л ю ч а е т с я в н а п и с а н и и «тела» проце-д у р ы с заголовками составляющих частей. Программа компили-руется, сохраняется и только затем дописываются составляющие части . В процедуре Solve есть два п р а к т и ч е с к и с о в п а д а ю щ и х ф р а г м е н т а ( ц и к л ы по i). И с к л ю ч и т е эти повторения , они недо-стойны Вас.

Задания для самостоятельной работы

1. Вставить • первую строку после строки , в которой н а х о д и т с я пер-

в ы й в с т р е ч е н н ы й м и н и м а л ь н ы й элемент; • второй столбец после первого столбца, в котором все эле-

м е н т ы п о л о ж и т е л ь н ы ; • нулевую строку и нулевой столбец перед строкой и столб-

цом, в к о т о р ы х н а х о д и т с я п е р в ы й м и н и м а л ь н ы й эле-мент;

• последнюю строку после всех строк , в к о т о р ы х есть за-данное число А;

• второй столбец перед всеми столбцами, в к о т о р ы х нет от-р и ц а т е л ь н ы х элементов;

• первую строку перед всеми строками , в к о т о р ы х есть 0, и п е р в ы й столбец после всех столбцов, в к о т о р ы х есть от-р и ц а т е л ь н ы е элементы;

• столбец из нулей после столбцов с м и н и м а л ь н ы м и эле-ментами ;

• первую строку м е ж д у средними строками; • строку из нулей перед всеми строками , п е р в ы й элемент

к о т о р ы х делится на 3. 2. У д а л и т ь

• столбцы, в к о т о р ы х есть м и н и м а л ь н ы й элемент; • строку с номером k и столбец с номером I; • все столбцы, в которых нет нулевого элемента ; • все строки и столбцы, на пересечении к о т о р ы х стоят от-

р и ц а т е л ь н ы е элементы; • среднюю строку (строки);

Page 309: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 309

• все столбцы, в которых первый элемент больше послед-него;

• столбец, в котором находится первый четный отрицате-льный элемент;

П все столбцы, в которых первый элемент больше заданно-го числа А.

3. Заменить • минимальный элемент в каждой строке на противопо-

ложный по знаку; • все элементы первых трех столбцов на их квадраты; • все симметричные элементы квадратной матрицы на нули.

4. Поменять местами • средние столбцы; • средние строки с первой и последней; • средние столбцы со вторым и предпоследним; • средние строки; • первый максимальный и последний минимальный эле-

менты; • в каждой строке первый элемент и максимальный по мо-

дулю; • в каждой строке первый отрицательный и последний по-

ложительный; • первую строку и строку, в которой находится первый ну-

левой элемент; • вторую и предпоследнюю строки; • первую строку с последней строкой, вторую — с предпос-

ледней и так далее; • столбцы так, чтобы в конечном итоге они имели следую-

щий порядок: • последний, предпоследний, ..., второй, первый; • первый, последний, второй, предпоследний, третий,...

5. Найти максимальный элемент массива, встречающийся бо-лее одного раза.

6. Расстоянием между строками назовем сумму произведений соответствующих элементов строк. Найти две строки с мак-симальным расстоянием.

7. Дан массив размерностью N*N, N - нечетное число. Вывес-ти элементы массива, при обходе его по спирали, начиная с центра.

8. Для заданного целочисленного массива (N*N) найти макси-мум среди сумм элементов диагоналей, параллельных глав-ной диагонали.

Page 310: Pascal 8 Класс - Окулов

309 Часть третья

9 . Д л я з а д а н н о г о ц е л о ч и с л е н н о г о м а с с и в а (N*N ) н а й т и м и н и -м у м с р е д и с у м м э л е м е н т о в д и а г о н а л е й , п а р а л л е л ь н о й побоч-н о й д и а г о н а л и м а т р и ц ы .

10 . З н а ч е н и я э л е м е н т о в м а с с и в а н а х о д я т с я в и н т е р в а л е от А до В . Д в е с т р о к и м а т р и ц ы н а з о в е м п о х о ж и м и , если о н и отлича-ю т с я т о л ь к о п о р я д к о м э л е м е н т о в . Н а й т и п а р ы п о х о ж и х строк .

1 1 . Э л е м е н т м а с с и в а А[i,j] н а з ы в а ю т с е д л о в о й т о ч к о й , е с л и он яв-л я е т с я м и н и м а л ь н ы м в с т р о к е с н о м е р о м i и м а к с и м а л ь н ы м в с т о л б ц е с н о м е р о м ] . Н а й т и с е д л о в ы е т о ч к и .

1 2 . Д л я д а н н о г о м а с с и в а н а й т и т а к и е з н а ч е н и я h, что k с т р о к а с о в п а д а е т с k с т о л б ц о м .

1 3 . С р е д и с т о л б ц о в з а д а н н о г о ц е л о ч и с л е н н о г о м а с с и в а н а й т и с т о л б ц ы , с о с т о я щ и е т о л ь к о и з н е ч е т н ы х э л е м е н т о в .

14 . Э л е м е н т A[i,j] н а з о в е м л о к а л ь н ы м м и н и м у м о м , е с л и он стро-го м е н ь ш е всех с в о и х с о с е д е й . С о с е д я м и я в л я ю т с я э л е м е н т ы A[k,l]c (i-l< k< i+1), (j-l< I <j+l), (k,l)* ( i j ) . Н а й т и к о л и ч е с т -во л о к а л ь н ы х м и н и м у м о в д л я з а д а н н о г о м а с с и в а .

Н а й т и м а к с и м у м с р е д и л о к а л ь н ы х м и н и м у м о в . 1 5 . Р е ш и т ь з а д а ч у п о и с к а ч и с л а X в д в у м е р н о м м а с с и в е

А[ 1..N.1..M] ( э л е м е н т ы в с т р о к а х и с т о л б ц а х у п о р я д о ч е н ы ) з а в р е м я , п р о п о р ц и о н а л ь н о е 0(N+M).

Page 311: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 311

З а н я т и е № 2 4 . Н е с к о л ь к о з а д а ч н а т е х н и к у р а б о т ы с д в у м е р н ы м и м а с с и в а м и

План занятия • разбор и м о д и ф и к а ц и я программ: перемножения матриц,

р е ш е н и я системы л и н е й н ы х уравнений, приведение мат-р и ц ы к блочному виду, сортировки элементов строк мат-р и ц ы и поиска суммы м и н и м а л ь н ы х элементов в строках и столбцах матрицы;

• выполнение самостоятельной работы.

Экспериментальный раздел работы

1. Составить программу вычисления произведения двух цело-ч и с л е н н ы х матриц А[п,т] и B[m,t], Элементы результиру-ющей, т а к ж е целочисленной, матрицы C[n,t] определяются по формуле C[i,j]=A[i.l]*B[l.]]+A[i,2]*B[2.j]+...+A[i,m]* *В[т,]], где (п, т), (m,t), (n,t) — размерности матриц А, В, С соответственно. П р и выполнении умножения количест-во столбцов в первом массиве равно количеству строк во вто-ром массиве. Пример:

2*3+1*2+5*1 = 13 2*4+1*0+5*2=18

Приведем полный текст решения. В файл Output.Txt запи-сываются массивы А, В-а. результат — массив С. Независимость фрагментов логики (процедур TInit, TPrint) дает результаты. Они используются д л я ввода и вывода двумерных массивов лю-бой размерности.

($R+) Program Му24_1; Const MaxN=8/ MaxM=8; Type TMyArray=Array[1..MaxN,1..MaxM] Of Integer; Var A,B, C: TMyArray ;

n,m,t:Integer; Procedure Init; Begin

Assign (Input, 'Input.Txt') ; Reset (Input) ; Assign (Output, 'Output. Txt') ; ReMrite (Output) ;

Page 312: Pascal 8 Класс - Окулов

311 311 Часть третья

End; Procedure TInit(Var v, w:Integer;Var X:TMyArray);

Var i,j:Integer; Begin

ReadLn(v,w); For i;=1 To v Do

For j:=1 To w Do Read(X[i,j]) ; End;

Procedure TPrint(v,w:Integer;X:TMyArray); Var i,j:Integer; Begin

Append (Output) ; For i:=l To v Do Begin

For j:=l To w Do Write (X [ i , J ] : 4) ; WriteLn;

End; WriteLn;

End; Procedure Solve(v,w, r:Integer;X,Y:TMyArray;Var Z:TMyArray);

Var l , j , t : I n t e g e r ; Begin

For i:=1 To v Do For t: =1 To r Do Begin Z[i,t] :=0;

For j:=1 To w Do Z[i,t]:=Z[i,t]+X[i,j]*Y[j,t];

End; End;

Procedure Done; Begin Close (Input) ; Close(Output); End;

Begin Init; TInit (n,m,A); TPrint (n,m,A); TInit (m,t,B); TPrint(m,t,B); Solve (n,m,t,A,B,C); TPrint (n, t,C) ; Done;

End.

П у с т ь A — к в а д р а т н ы й м а с с и в . И з м е н и т е п р о г р а м м у т а к , ч т о б ы в ы ч и с л я л о с ь в ы р а ж е н и е А+А*А+А*А*А.

Page 313: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 313

2. Р е ш е н и е системы л и н е й н ы х уравнений методом Гаусса. Си-стема из п л и н е й н ы х алгебраических уравнений с п неизве-стными имеет вид:

3n*x1+ai2*X2+. +a 1 „*x l n =b 1

a2i*xi+a22*:<2+ +Я2„*х2„=Ьг

ani*Xi+3n2*X2+ +a„„*x„„=b„ Р е ш е н и е м системы называется т а к а я упорядоченная сово-

купность чисел Xj=cr х2=с2, ..., хп=ся, которая обращает все уравнения системы в верные равенства. Мы предполагаем, что система имеет единственное решение (не будем вдаваться в тео-рию линейной алгебры). В этом случае н и ж е описанная схема р е ш е н и я имеет право на существование. Суть метода Гаусса -последовательное исключение неизвестных из уравнений (пря-мой ход метода). После применения схемы исключения исход-н а я система уравнений преобразуется в равносильную (имею-щ у ю то ж е решение) треугольную:

XL+L2*Х2 + 13*Х3+ + 2 п *Х„ = 1

Х2+23*Х3+ +2П*Х„=2

хл=л И з этой системы последовательно находятся значения всех

неизвестных хя, xn_v ..., xt (обратный ход метода). Значение хп

подставляется во все уравнения , затем полученное значение хп_, и т. д . , до первого уравнения , из которого находится значе-ние хг Д л я сведения системы к треугольному виду коэффици-енты первого уравнения делятся на ап, получим:

X1 + a12/a11 *х2+. +aln/a11*x„=b1/au или (обозначив частные по-другому) х1+12*х2+гз*х3+ +п*х„=1.

Если аП=0, то в первом столбце ищется ненулевой коэффи-циент , а затем переставляются соответствующие строки. Суще-ствование такого коэффициента следует из единственности реше-ния системы. Пользуясь полученным результатом, неизвестное х[

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

Page 314: Pascal 8 Класс - Окулов

314 Часть третья

Program Му24_2; Const MaxN=l0; MaxM=MaxN+l;

Type TMyArray=Array[1..MaxN,1. . MaxM] Of Real; OMyArray=Array[1..MaxN] Of Real ;

Var A:TMyArray; X:OMyArray; n:Integer;

Procedure TInit(Var v:Integer;Var X:TMyArray) ; ("Ввод коэффициентов системы уравнений. *) Var i , j -.Integer; Begin

Assign(Input,'Input.Txt')/Reset(Input) ; ReadLn(v); For i:=l To v Do

For j:=l To v+1 Do Read(X[l,j ]) ; Close ( I n p u t ) ;

End; Procedure TPrint(v:Integer;X:OMyArray); (*Вывод решения. *} V a r l:Integer; Begin

Assign (Output, 'Output. T x t ' ) ; ReWnte (Output) ; For i:=l To v Do Write (X[i] : 6) ;

WriteLn; Close (Output);

End; F u n c t i o n Solve(v:Integer;A:TMyArray;Var X:OMyArray) :Boolean;{ "Находим решение - массив X, если оно есть. *)

Var 1,j,k,1:Integer ; v.Real; Pp:Boolean;

Procedure Swap(k,1:Integer);{*Переставляем строки с номерами k , 1.*}

Var j : Integer;w:Real; Begin

For j : =1 To v Do Begin w:=A[k, j ] ;A [k, j ] : =A[1 , j ] ; A[1,j]:=w;End;

End; Function Search(j:Integer):Integer;("Поиск ненулевого коэффициента в столбце с номером j .

Page 315: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 314

Поиск осуществляется в строках с номерами 3+1 .. v.") Var г:Integer; Begin

i:=J+l;

While (i<=v) And (A[i,j]=0) Do Inc(i); S e a r c h : = i ;

Ends-

Begin {"Основная часть решения.*) j:=1;Pp:=False; While (J<=v) And Not (Pp) Do Begin If A[j,J]=0 Then Begin k:=Search(j); If k>v Then Pp: =True Else Swap (j ,k) ;

End; If Not Pp Then Ведш{*Если есть решение.*) w:=A[j,j];(*Прямой ход. Преобразуем уравнение с номером j . *) For k:=j То v+1 Do A[j,k]:=A[j,k]/w; For k:=j+l To v Do Begin {"Исключаем ху из остальных уравнений. *) w:=A[k,j]; For l:=j To v+1 Do A[k, 1] : =A[k, 1 ]-A [j , 1] *w;

Ends-End; Inc (j);{*Переходим к другой строке.*}

End; If Not Pp Then Begin (*Обратный ход. *) For j:=v DownTo 1 Do Begin X[j] :=A[j,v+l] ; For i:=j-l DownTo 1 Do ("Исключаем найденное xj и з в с е х уравнений.*} A [i, v+1 ] : =А [1, v+1]-А [1,3 ] *X[j] ;

End; End; SolveNot Pp;

End; Begin("Основная программа.") TInit(n,A);[*Ввод системы уравнений.*} If Solve(n,A,X) Then TPrint(n,X);("Если есть решение, то его поиск и вывод результата. *) End.

Page 316: Pascal 8 Класс - Окулов

315 Часть третья

3. Д а н а м а т р и ц а из 0 и 1. Требуется привести ее к блочному по столбцам виду, т. е. переставить столбцы так , чтобы вначале ш л и столбцы с единицей в первой строке , затем — во второй (если они есть) и т. д.

Пример: В первой строке у к а з а н ы номера столбцов. Суть решения в

просмотре каждой строки одновременно слева направо и справа налево. Если при просмотре слева направо найден столбец с 0, а справа налево — с 1, то эти столбцы переставляются . П р и пере-ходе к следующей строке просмотр слева направо следует про-д о л ж и т ь со столбца, на котором закончены действия п р и обра-ботке п р е д ы д у щ е й строки.

Procedure Solve(v:Integer; Var X:TMyArray); Var i,w:Integer; Procedure Swap(q,r:Integer);

Var i,w:Integer; Begin

For i:=l To v Do Begin w :=X [ i,q] ;X [l, q] : =X [i , r] ; X[i,r]:=w;End;

End; Procedure Change_St (i:Integer;Var k:Integer); ("Обработка строки с номером i . Параметр k -номер с т о л б ц а , на котором заканчиваются действия в строке 1. *)

Var j:Integer; Begin

j :=v; While k<j Do Begin

I f X[i,k]=l Then Inc(k)l"Поиск единицы. *} Else I f X[i,j]=0 Then Dec(j){"Поиск нуля.")

Else Begin Swap(k,j) ; Inc(k); Dec(j); End;

End;

Page 317: Pascal 8 Класс - Окулов

Массив — фундаменталь

I f X[i,k]=l Then Inc (к) ;{ "Зачем этот оператор?*} Ends-

Begin w:=J ; i: =1 ; While (K=v) And (w<v) Do Begin {*Пока не просмотрены все строки и не исчерпаны все столбцы, выполняем действия из тела цикла. *}

Change_St (i,w);I*Обрабатываем строку с номером 1.*} Inc(i); {*Изменяем номер строки.*}

End; End;

Логика обработки соответствует примеру, приведенному выше по тексту? В некоторых задачах необходимо проверить, являет-ся ли матрица блочной и по столбцам и по строкам (сверху и слева у к а з а н ы номера столбцов и строк).

i_ J L J L A J L J L _L _°__L J L _ L JL JL i l i l l l l

1 A A 1 U L 5 0 1 0 1 0 0

1 j L _ L _ ! J L J L J L _2_ _o_ _o_ _o_ _o_

i l l l i J L 4_ _0_ _0_ J _ _1_ _0_ _0_ 5 1 1 0 0 0 0

4__2__6_ _5_J_ J3_ J 1_ J 0__0__0__0_ _5_J__1__0__0__0__0_

j4__o__o_ j 3 0 0 0 0 1 1

Проверить, имеет ли матрица блочный вид. 4. Рассмотрим следующую задачу. Дана целочисленная матри-

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

В 3 I 2 I 1 I 6 I 5 I 4 4 6 1 5 2 3 2 6 1 4 3 5_ 4 5 2 3 6 1 3 2 5 1 6 4

Page 318: Pascal 8 Класс - Окулов

318 Часть третья

Type OMyArray=Array[1..MaxN] Of Integer; TMyArray=Array[l..MaxN] Of OMyArray;

Элементы к а ж д о й строки переписываются в дополнитель-н ы й массив, а затем сортируются одним из известных методов с одновременной перестановкой соответствующих элементов в стро-ке м а т р и ц ы В. Н а ч а л ь н ы й вид В приведен в таблице .

В

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

Procedure Solve(v:Integer;X:TMyArray;Var Y:TMyArray);

Var i,t,j:Integer; Z:OMyArray;

Procedure Swap(Var q, r:Integer) ; Var w:Integer; Begin

w:=q;q:=r;r:=w; End;

Begin For i:=l To v Do Begin

Z:=X[i]; For t:=v-l DownTo 1 Do{"Сортировка.*)

For j:=1 To t Do I f Z[J]>Z[J+1] Then Begin Swap (Z [ j ] , Z [ j +1 ] ) ;Swap (Y[I , j ] , Y[l, j +1 ] ) ;

End; End;

End; 5. Дана целочисленная матрица А. Требуется выполнить следу-

ющее преобразование: н а й т и в к а ж д о й строке м и н и м а л ь н ы й элемент и вычесть его из всех элементов данной строки; за-тем аналогичное преобразование выполнить и со всеми столб-цами; при выполнении действий подсчитывать сумму мини-мальных элементов.

Page 319: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 318

Сумма м и н и м а л ь н ы х элементов для приведенного примера равна 31.

Function Solve(v:Integer;Var X:TMyArray):Integer; f "Функция возвращает сумму минимальных элементов. *}

Var s,i,w,j:Integer; Function MmStr (t: Integer) : Integer ;{ "Поиск минимального элемента в строке. *} Var j ,mm-.Integer ; Begin mm : =X[ t , 1] ; For j:=l To v Do I f X[t, j] <mm Then mm : =X [ t , j ] ; MmStr: =mm ;

Ends-Function MmStl (t: Integer) -.Integer; ("Поиск минимального элемента в столбце. ") Var i,mm: Integer ; Begin min:=X[l,t]; For l: =2 To v Do I f X[i,t]<min Then mm : =X[i, t ] ; MinStl :=mins-

End; Procedure SubStr (i , k: Integer) ; ( "Вычитание из элементов строки с номером i значения к.*}

Page 320: Pascal 8 Класс - Окулов

320 Часть третья

Var j:Integer; Begin

For j:=l To v Do X[1,j] :=X[1,J]-k; End;

Procedure SubStl(j,к:Integer);{*Вычитание из элементов столбца с номером j значения к.*}

Var 1:Integer; Begin

For l: =1 To v Do X[l, J ] : =X[l, j ] -k ; End;

Begin s : =0;{"Искомое значение.*} For l: =1 To v Do Begin { * Преобразование no строкам. *}

w:=MmStr ( l ) ; I n c (s ,w) ; SubStr ( l , w) ;

End; For j:=l To v Do Begin {*Преобразование no столбцам. *}

w: =MmStl ( j ) ; Inc (s, w) ; SubStl (J , w) ;

End; Solve:=s;(*Резулътат. *}

End; Модифицируйте решение задачи следующим образом. Из по-

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

Задания для самостоятельной работы

1. Решить системы линейных уравнений:

4,4*, -2,5хг + 192*3 - Ю3х4 = 4,3 5,5*, - 9,3*г - 142*э + 132*4 = 63 7,1*, - 115*2 + 5.3*э " 6,7*4 = -13 142Х, + 23,4хг - 83*э + 5.3*4 = 72

15,7х, + 6,6*2 - 5,7х3 + 1 Х5х4 = -2,4 83*, - 6,7х2 + S3*, - 45*4 = 5,6 6,3*, - 5,7хг - 23,4*3 + 6,6*4 = 7,7 5,6*, + 83*2 " 5.7*3 " 233*4 = 7.3

Page 321: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 321

2. Следом квадратной матрицы называется сумма элементов, расположенных на главной диагонали.

Даны квадратная матрица А порядка п, натуральное число т. Вычислить следы матриц А, А2, А3, .... Ат.

3. Дана квадратная матрица А порядка п. Переставить строки так , чтобы элементы в первом столбце были упорядочены по неубыванию.

4. В матрице А порядка п*т заменить нулями элементы, стоя-щ и е в строках и столбцах, где имеются нули.

5. Дана квадратная матрица А порядка га, каждый элемент ко-торой равен 0, 1, 5 или 11. Подсчитать в ней количество чет-верок A[i,j],A[i+l,j], А[i,]+l],A[i+l,j+l], в каждой из ко-торых все элементы различны. П р и м е ч а н и е Если все числа различны, то их сумма равна 17. Верно и обратное утверждение.

6. Дана квадратная матрица А порядка га из 0 и 1. Прямоуголь-ником назовем часть матрицы, заполненной 1. Известно, что прямоугольники не соприкасаются друг с другом. Найти ко-личество прямоугольников. На рисунке их 4.

0 1 0 0 1 1

1 1 0 0 0 0

1 1 0 1 1 0 1 1 0 1 1 0

7. Дана квадратная матрица А порядка га из натуральных чи-сел. Находится минимальный элемент А и вычитается из всех элементов матрицы. Затем строки и столбцы, содержащие ну-левые элементы после вычитания, «вычеркиваются» из мат-рицы. Процесс продолжается до тех пор, пока не будет полу-чено одно число. Найти суммы минимальных элементов.

8. Дана квадратная матрица А порядка га из 0 и 1. Считаем, что столбец «покрывается» другим столбцом, если множество строк, представленных 1 в этом столбце, содержится в мно-жестве строк, представленных 1 в другом столбце. Исключить такие столбцы из матрицы.

Page 322: Pascal 8 Класс - Окулов

322 Часть третья

9. Д а н а к в а д р а т н а я м а т р и ц а А порядка п из 0 и 1. Исключить из м а т р и ц ы столбцы и строки, содержащие одну единствен-ную 1. Если после и с к л ю ч е н и я п о я в и л и с ь новые строки и столбцы, удовлетворяющие этому условию, то продолжить процесс и с к л ю ч е н и я .

10. Квадратную матрицу А порядка га из 0 и 1 назовем правиль-ной, если в ней нет квадратов 2*2 и более, составленных то-л ь к о из 0 и л и 1. Проверить , я в л я е т с я ли А правильной. На рисунке А я в л я е т с я правильной.

Page 323: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 323

Занятие № 25. Комбинированный тип данных (записи)

План занятия • описание типа данных; • разбор процедур и функций работы с датами и простейши-

ми геометрическими объектами; • выполнение самостоятельной работы.

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

Запись — это составной тип данных, содержащий набор элемен-тов разных типов. Составляющие запись элементы называются ее полями. В записи каждое поле имеет свое собственное имя . Чтобы описать запись, необходимо указать ее имя , имена объ-ектов, составляющих запись и их типы. Общий вид такой: Туре <ммя записи> = Record

<поле 1>:<ТИП 1>; <поле 2>:<тмп 2>;

<поле п>:<тип п> End;

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

Туре уеаг=1583..3000; month_number=l..12; day=l..31; date=Record

dyear:year; dwonth:month_number; Dday:days-End;

Разумеется, что тип date можно было определить и так: Type date=Record

dyear: 1583..3000; dmonth: 1..12; dday: 1..31; End;

Page 324: Pascal 8 Класс - Окулов

324 Часть третья

но остановимся на первом способе, ибо при решении задач с да-т а м и нам потребуется работа и с т и п а м и д а н н ы х year , month_ number, day.

С полем записи в программе м о ж н о поступать, к а к с пере-менной того ж е типа , что поле. Обращаются к полю по состав-ному имени:

<имя записи>.<имя поля>.

Пример. Есть Var t:date; Следующие операторы присвоения имеют силу:

t.dyear:=1987; t.dmonth:=12 ; t.dday:=30;

Д л я не любителей писать много эта запись сокращается с по-мощью оператора W i t h : W i t h < и м я записи> Do <оператор>; . Операторы присвоения в этом случае записываются несколько по-другому.

With t Do Begin dyear:=1987 ; dmonth:=12; dday:=30;

End;

Оператор Case состоит из в ы р а ж е н и я (селектора) и списка опе-раторов, к а ж д о м у из которых предшествует либо одна и л и не-сколько констант (называемых константами варианта) , либо слово Else. Селектор должен быть порядкового типа , причем порядковые значения верхней и н и ж н е й границ этого типа дол-ж н ы находиться в диапазоне от - 3 2 7 6 8 до 32767. Оператор Case выбирает д л я выполнения тот оператор, перед которым стоит константа, равная значению селектора или диа-пазону, содержащему значение селектора. Если в диапазоне выбора не существует такой константы выбора и в операторе имеется часть Else, выполняется оператор, написанный после слова Else. Если часть Else отсутствует, ни один оператор не выполняется .

Примеры: Var c:Char; Begin

ReadLn (с) Case с Of

Page 325: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 324

' 0' . . ' 9' : Wri teLn ('с — цифра . ' ) ; 'а ' . . ' z ' : WriteLn ('с — маленькая латинская буква . ' ) ; 'А'. . ' Z' -.WriteLn (' с — большая латинская буква. ' ) ; Else WriteLn С с - некоторый другой символ.')

End; End; Var i:Integer; Begin

ReadLn (1) ; Case l Of

0,2,4,6,8: WriteLn (' l - четное число меньше 10.'); 1,3,5,7,9: WriteLn (' l — нечетное число меньше 10.');

End; End;

Экспериментальный раздел работы

1. Работа с датами. Используются типы данных year, month_ number, day, date, определенные выше по тексту. Определим ряд процедур и ф у н к ц и й . Год считается високосным (366 дней), если он не последний год столетия и делится без остатка на 4. Кроме того, последний год любого столетия считается ви-сокосным только в том случае, если его номер делится на 400. Function Leap(x.-year) .-Boolean;

Begin Leap:=(x Mod 400 =0) Or (x Mod 100O0) And (x Mod 4=0) ;

End; Задача определения количества дней в месяце решается с

помощью следующей функции: Function DayM (х: yea г ;y:month_ п umber) -.day;

Begin Case у Of

4,6,9,11: DayM: =30 ; { *B апреле, июне, сентябрей ноябре 30 дней. *} 1,3,5, 7,8,10,12: DayM: =31; { *В январе, марте, мае, июле, августе, октябре и декабре 31 день. *) 2: I f Leap (х) Then DayM:=29 Else DayM:=28; (*Если год високосный, то в феврале 29 дней. *)

End; End;

12—452

Page 326: Pascal 8 Класс - Окулов

Д л я вывода даты , н а п р и м е р в виде 3 0 . 1 2 . 1 9 8 7 , используется ее преобразование в т и п String.

Function StringDate(w.Date):String; Var s,q:String; Begin

str (w.dday,s); I f w.dday<10 Then s : = ' 0 ' + s ; f " П р е о б р а з у е м день даты. *} s : = s + ' . ' ; str(w. dmonth,q);{"Преобразуем месяц даты. *} I f w.dmonth<10 Then s : = s + ' 0 ' + q + ' . ' Else s:=s+q+'.'; str(w. dyear,q);{*Преобразуем год даты. *} s:=s+q; StringDate:=s;

End; Из т е к у щ е й даты требуется получить дату следующего дня .

П р и решении этой задачи необходимо учитывать , я в л я е т с я ли: • день д а т ы последним днем месяца ; • месяц д а т ы последним месяцем года. Procedure Tomorrow(х:date;Var y.date); Begin

У ••=>:; I f x. ddayODayM (x. dyear, x. dmonth J Then у.dday:=x.dday+1{"День даты не является последним днем месяца . *}

Else I f х. dmonth<>12 Then Begin у.dmonth:=x.dmonth+1;{"Месяц даты не является последним месяцем в году."} у.dday:=l;

End Else Begin

у.dyear:=x.dyear+1; у. dmonth:=1; у. dday:=1;

End; End;

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

Page 327: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 327

Procedure Future(х:Integer;Var у:Date);(*х — количество д н е й , у — текущая дата. ")

Var 1:Integer; Function Dyears (z:year):Integer;{"Определяем количество дней в году. "I

Begin I f Leap(z) Then Dyears:=366 Else Dyears:=365 ;

End; Begin

For i:=l To y.dmonth-1 Do x: =x+DayM(y.dyear, i ) ; I"Возвращаемся в начало года, определенного текущей датой.. ") х:=х+у.dday;{"Прибавляем количество дней из даты. Получаем общее количество дней от начала текущего года. *) While x>Dyears(у.dyear) Do Begin{"Счет идет по годам. *)

х: =x-Dyears (у. dyear) ; Inc (у. dyear) ;

E n d ; { " Г о д будущей даты сформирован. *) у. dmonth:=1;{"Определяем месяц будущей даты.") While x>DayM(у.dyear,у.dmonth) Do Begin I"Счет идет по месяцам."}

х: =x-DayM (у. dyear ,у. dmonth) ; Inc (у. dmonth) ;

End;{"Месяц будущей даты получен.") у. dday:=x;{"Оставшиеся дни являются днями будущей даты. *)

End; Основная программа для работы с датами может иметь сле-

дующий вид: Program Му25_1; Var t , г .-date; w:Integer; { "Процедуры и функции. "} Begin

WriteLn Введите год, месяц, день даты: ' ) ; ReadLn (t. dyear, t . dmonth, t . dday) ; Tomorrow ( t , r) ; WriteLn (StrmgDate (r)) ; WriteLn (' Введите количество дней до будущей даты.'); ReadLn (w) ;

Page 328: Pascal 8 Класс - Окулов

Чвсть третья

Future(w,t); WriteLn (StringDate ( t j ) ; ReadLn;

End.

Многочисленные м о д и ф и к а ц и и данной программы получают-ся при выполнении ряда заданий д л я самостоятельной работы.

2. Н а ч а л ь н ы е сведения из компьютерной геометрии . Точка на плоскости в декартовой п р я м о у г о л ь н о й системе координат о п и с ы в а е т с я парой вещественных чисел . Введем соответст-в у ю щ и й т и п д а н н ы х — запись . П р и использовании вещест-венного типа операции сравнения лучше оформить специаль-ными функциями . Причина известна, на типе Real в системе п р о г р а м м и р о в а н и я Турбо П а с к а л ь нет о т н о ш е н и я порядка , поэтому записи вида а=Ь, где а и Ь вещественные числа , луч-ше не использовать. По этой ж е причине все вычисления с ве-щ е с т в е н н ы м т и п о м л у ч ш е в ы п о л н я т ь с к а к о й - т о , з аранее определенной, точностью. Type TPomt=Record х, у: Real; End; Const Eps: Real=le-7;("Точность вычисления.*) ZeroPnt: TPoint = (x:0; y: 0) ;{"Точка с координатами 0,0. *} П р и м е р р е а л и з а ц и и операций сравнений: Function RealEqfa, Ь: Real): Boolean; {"Строго равно.*}

Begin

RealEq:=Abs(a-b)<=Eps; End;

А ф у н к ц и я проверки совпадения двух точек на плоскости имеет вид:

Function EqPoint(А, В: TPoint):Boolean;{"Совпадают ли точки ?"}

Begin EqPoint:=RealEq(А.х, В.х) And RealEqfA.y, В.у);

End; П р и в ы ч и с л е н и и расстояния м е ж д у д в у м я т о ч к а м и на плос-

кости удобнее опять ж е использовать ф у н к ц и ю . Function Dist(А, В: TPoint): Real;("Расстояние между двумя точками на плоскости. *)

Begin Dist:=Sqrt(Sqr(A.x-B.x) + Sqr(A.y-B.y));

End;

Page 329: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 329

Л ю б а я точка на плоскости определяет вектор (v) — направ-л е н н ы й отрезок, соединяющий точку (0,0) с точкой (х,у). Век-тор v м о ж н о задать в полярной системе координат через его длину (модуль) v и угол относительно оси ОХ. Координаты по-л я р н о й системы координат (и , ) и прямоугольной декартовой (х, у) с в я з а н ы соотношениями:

x=v*cos( a), y=v*sm (а) , v=i/x2 + у 2 , t an (а) = у / х .

Разработаем ф у н к ц и ю определения угла а (в радианах) по координатам точки в декартовой системе координат.ъ

Function GetAngle (w:TPomt) :Real;(*w - ненулевая точка на плоскости. *}

Var v,angle:Real; Begin

v:=Dist(w,ZeroPnt);{"Расстояние. *} I f RealEq(v,0) Then GetAngle:=0 Else Begin

I f RealEq (w. x, 0) Then I f w.y>0 Then angle:=Pi/2

Else angle:=3*Pi/2 (*Выделяем особые случаи. Точка находится на одной из осей. *) Else I f RealEq(w.y,0) Then I f w.x>0 Then

angle:=0 Else angle:=Pi Else Begin

angle:=ArcTan (Abs (w.y/w.x)) ; (*Определяем четверть плоскости. *) I f w.x*w.y>0 Then Begin I f w.x<0

Then angle:=Pi+angle; End

Page 330: Pascal 8 Класс - Окулов

330 Часть третья

Else I f w.x<0 Then angle:=Pi/2+ angle Else angle:=2*Pi-angle;

End; GetAngle:=angle;

End; End;

С к а л я р н ы м произведением двух векторов называется число, равное произведению длин этих векторов на косинус угла меж-ду ними. Если хоть один из векторов нулевой, то угол не опре-делен, и скалярное произведение по определению считается равным нулю. Т а к и м образом, (v,w)= v*w*Cos(a), где а — угол м е ж д у векторами v и и>.

Напомним, что косинус угла между векторами v, w вычис-ляется по формуле: Cos(a)=(v.x*w.x+v.y*w.y)/(v*w). Таким об-разом, скалярное произведение (v,w)= v.x*w.x+v.y*w.y.

Function ScDec (v, w: TPoint): Real; {"Скалярное произведение векторов в прямоугольной декартовой системе координат. *) Begin

ScDec:=v.x*w.x+v.y*w.y; End;

Вычисление скалярного произведения векторов в полярной системе координат ( ( и , а ) и выполняется по формуле: v*w=v.x*w.x+v.y*w.y =v*Cos( а )*ui*Cos(fi )+v*Sm( а )*w*Sin(fi )= =v*w*Cos(a-p). Из этого соотношения следует, что скалярное произведение

• ненулевых векторов равно нулю тогда и только тогда, ког-да векторы перпендикулярны;

• больше нуля , если угол м е ж д у векторами острый, и мень-ше нуля , если угол тупой.

П р я м а я л и н и я на плоскости, проходящая через две точки p t

и р2 , определяется следующим л и н е й н ы м уравнением от двух переменных: (р2.х-р1.х)*(у-р1.у)^(р2.у-руу)*(х-р1.х). После пре-образований получаем: ~(р2.у~ргу) *х +(р2.х-р1.х)*у+(р2.у~ргу)* * р,.х - (p2.x-prx) * Pj.y=0, или А*х + В*у + С = 0, после соот-ветствующих обозначений.

Если прямые заданы с помощью уравнений А ^ х + В ^ у + С ^ О и А2*х+В2*у+С2=0, ТО точка их пересечения, в случае ее суще-ствования (А,*В 2-А 2*В, <>0) , определяется по формулам:

Page 331: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 330

х=- (С2 *B2~C2*Bj) / (А2 *В2-А2*В2) , у=(А2*с1-А,*с2) / (А1*В2-А2*В1) .

Определим тип данных д л я описания прямой Type TLine= =Record А, В, С: Real;End;, процедуру вычисления коэффициен-тов в уравнении прямой , проходящей через две точки и функ-ц и ю в ы ч и с л е н и я координат точки пересечения двух п р я м ы х .

Procedure Point2ToLme (А, В: TPomt; Var L: TLine}; { "Определение уравнения прямой по координатам двух точек. *} Begin

L. А: =В. у-А. у ; L . В; =Л. х-В. х ; L. С: =- (A.'<*L.A + А.y*L.Ь);

End; Function Lme2ToPomt (£L, sL: TLine; Var P: TPomt) : Boolean; {*Определение координат точки пересечения двух

линий. Значение функции равно True, если точка есть, и False, если прямые параллельны. *}

Var st: Real ; Begin

st:=fL.A*sL.B-sL.A*fL.B; I f Not (RealEq(st, 0)) Then Begin

Lme2ToPoint :=True; P.x:=-(fL.C*sL.B-sL,C*fL.B)/st; P. y:=(sL.A*fL.C-fL.A*sL.C)/st;

End Else Lme2ToPomt :=False;

End;

Задания для самостоятельной работы

1. Написать процедуру (или функцию) определения: • д а т ы вчерашнего дня; • даты, которая была за m дней до указанной даты; • количества дней, прошедших от даты t t до t2 ; Q дня недели, в ы п а д а ю щ и й на дату (для решения зада-

чи необходимо ввести тип данных «день недели» и опре-деленную дату «связать» с конкретным днем недели);

• годов столетия (1900-2000) , которые начинаются и за-канчиваются в воскресенье;

• годов столетия, содержащих максимальное число вос-кресений.

Page 332: Pascal 8 Класс - Окулов

331 Часть третья

2. Д а н а дата В а ш е г о р о ж д е н и я ( в к л ю ч а я и день недели) . Н а й т и те даты, когда В а ш день р о ж д е н и я «попадает« на тот ж е день н е д е л и .

3. Д а н ы д а т ы р о ж д е н и я ч л е н о в семьи и з п я т и ч е л о в е к . Столет-н и м и ю б и л е я м и семьи я в л я ю т с я д н и , когда с у м м а возрастов всех ч л е н о в семьи к р а т н а 100. Н а й т и т а к и е д а т ы .

4. Д а н о в р е м я , о п и с а н н о е с л е д у ю щ и м образом:

Type t i m e = Record h: 0..23;т, s: 0..59 End;

Описать : • л о г и ч е с к у ю ф у н к ц и ю д л я п р о в е р к и , п р е д ш е с т в у е т л и

в р е м я tt в р е м е н и t2 (в р а м к а х суток) ; • п р о ц е д у р у , п р и с в а и в а ю щ у ю п а р а м е т р у 11 в р е м я , на 1 се-

к у н д у большее в р е м е н и t (учесть смену суток) .

5. Д а н о с л е д у ю щ е е о п и с а н и е д а н н ы х :

Const п=300; Type MyRecord = Record key: Integer; name : String; End; Table=Array[1..n] Of MyRecord. С ч и т а я , что з аписи в массиве и м е ю т р а з л и ч н ы е к л ю ч и , опи-сать: • процедуру , у п о р я д о ч и в а ю щ у ю з а п и с и м а с с и в а по убыва-

н и ю з н а ч е н и й п о л я key; • л о г и ч е с к у ю ф у н к ц и ю поиск(4,/г,/г), о п р е д е л я ю щ у ю , есть

л и в массиве t (все з аписи к о т о р о й у ж е у п о р я д о ч е н ы по в о з р а с т а н и ю з н а ч е н и й п о л я key) з а п и с ь со з н а ч е н и е м п о л я key, р а в н ы м k, и , если есть, п р и с в а и в а ю щ у ю ее но-м е р п а р а м е т р у h.

6. Д а н массив , с о д е р ж а щ и й и н ф о р м а ц и ю об у ч е н и к а х некото-рой ш к о л ы (данные вводятся из файла ) . Н а п и с а т ь програм-м ы : • ф о р м и р о в а н и я второго массива с д а н н ы м и об у ч е н и к а х

т о л ь к о д е в я т ы х классов ; • о п р е д е л е н и я к о л и ч е с т в а у ч е н и к о в в о с ь м ы х и д е в я т ы х

к л а с с а х . 7. Б а г а ж п а с с а ж и р а х а р а к т е р и з у е т с я к о л и ч е с т в о м в е щ е й и об-

щ и м весом в е щ е й . Д а н массив , с о д е р ж а щ и й сведения о бага-ж е н е с к о л ь к и х пассажиров (данные вводятся из файла) . Све-д е н и я о багаже каждого пассажира представляют собой запись с д в у м я п о л я м и : одно поле целого т и п а (количество вещей) и одно — действительное (вес в к и л о г р а м м а х ) . Определить :

Page 333: Pascal 8 Класс - Окулов

Массив — фундаментальная структура данных 333

• багаж, средний вес одной вещи в котором отличается не более чем на 0 ,3 кг от общего среднего веса одной вещи;

• число пассажиров , и м е ю щ и х более двух вещей, и число пассажиров, количество вещей которых превосходит сред-нее число вещей;

• имеется ли пассажир, багаж которого состоит из одной в е щ и весом менее 30 кг .

8. Разработать процедуры перевода координат точки из декар-товой в полярную (и обратно) системы координат. Д л я опи-с а н и я точки в полярной системе координат использовать тип д а н н ы х Type TPol=Record r,ang:Real; End. Описание проце-дур д о л ж н о иметь следующий вид:

Procedure TurnDecPol (w: TPomt; Var q: TPol); {"Перевод из декартовой системы координат в полярную.*) Procedure TurnPolDec (q: TPol; Var w: TPomt); ("Перевод из полярной системы координат в

Декартовую. *)

9. Разработать процедуры сложения , вычитания векторов в де-картовой (полярной) системе координат.

Пример:

Procedure AddPol(а, Ь: TPol; Var с: TPol); {"Сумма двух векторов в полярной системе координат. *}

Begin c.ang:=(a.ang+Ь.ang)/2; С. r:=Sqrt (Sqr (a.r*Cos ($ . ang) -b. r"Cos (b.ang)) + Sqr (a . r*Sin (a. ang) -b.r*Sin (b.ang) )) ;

End; 10. Координаты точек отрезка (pvp2) можно задать двумя пара-

метрическими уравнениями от одной независимой перемен-ной t: p.x=prx+(p2.x-prx)*t u p.y=pvy^(p2.y-pvx)n. П р и 0<t<l т о ч к а p ( x , y ) лежит на отрезке, а при t<0 или t>l — вне отрезка на прямой линии , продолжающей отрезок. Д а н ы координаты двух точек на плоскости, определяющих отрезок, и координаты третьей точки. Определить, принад-л е ж и т л и третья точка отрезку?

11. Д а н ы два отрезка . Н а рисунке точка пересечения отрезков, если она есть, обозначена к а к р = ( х , у ) . Первый отрезок за-дан т о ч к а м и р1=(х1,у2) и р2=(х2,у2), а второй — точками р3=(х3,у3) и р 4 = ( х 4 , у 4 ) . Определить координаты точки пере-сечения отрезков.

Page 334: Pascal 8 Класс - Окулов

334 Часть третья

Р4 = (Х4,У4) 'Р2 = (Х2,У2)

Pi=(xi,yi)

12. Д а н о N точек . Н а й т и пару точек с м а к с и м а л ь н ы м расстояни-ем м е ж д у н и м и .

13. Д а н о м н о ж е с т в о п р я м ы х . П о д с ч и т а т ь к о л и ч е с т в о точек пе-ресечения этих п р я м ы х .

14. Дано два множества точек. Н а й т и пересечение и разность этих м н о ж е с т в .

15. Известно , что два отрезка н а х о д я т с я на одной п р я м о й . Опре-д е л и т ь их взаимное р а с п о л о ж е н и е .

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

17. Д а н ы координаты вершин треугольника и точки. Определить взаимное расположение этих геометрических объектов .

18. Д а н ы координаты вершин треугольник и отрезка. Определить и х взаимное расположение .

19. Д а н ы к о о р д и н а т ы в е р ш и н двух треугольников . Определить их взаимное расположение .

20. Дано множество п р я м ы х . Определить количество частей , на которые они разбивают плоскость . П р и м е ч а н и я 1. В задачах все геометрические объекты задаются на плоскости. 2. Исходные данные в задачах вводятся из файла.

Page 335: Pascal 8 Класс - Окулов

Ч а с т ь ч е т в е р т а я

Динамические структуры данных

Занятие № 26. Динамические структуры данных

План занятия • основные сведения о ссылочном типе данных; • линейные списки, изучение основных операций; • экспериментальная работа с программой «считалочка» и

реализацией списка через обычные массивы; • выполнение самостоятельной работы. Основные понятия о ссылочном типе данных (указате-

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

Выделение памяти для переменных на стадии выполнения программы возможно с использованием нового типа данных — указателей (ссылок). Значением указателя (переменной ссы-лочного типа) является адрес области памяти (первой ячейки) на переменные заданного базового типа. Итак, не значение пе-ременной (величины), а адрес памяти, в которой находится пере-менная (принцип косвенной адресации). Для указателей область памяти выделяется статически (как обычно), а для переменных, на которые они указывают, — динамически, т. е. на стадии вы-полнения программы (они и называются динамическими). Для хранения динамических переменных выделяется специальная область памяти, называемая «кучей». Работая с указателями, мы работаем с адресами величин, а не с их именами. Может быть, поэтому и появляется возможность выделять для дина-мических переменных память на стадии выполнения?

Для объявления указателей (переменных ссылочного типа) используется специальный символ «"», после которого указы-вается тип динамической (базовой) переменной.

Page 336: Pascal 8 Класс - Окулов

336 Часть четвертая

Туре <имя_типа>=л <базовый тип> ; Var <имя_переменной>: <имя_типа>; или

<имя переменной>: л <баэовый тип> ;

Н а п р и м е р :

Type ss = "Integer; Var х, у: ss;{"Указатели на переменные целого типа. *) a: "Real;{"Указатель на переменную вещественного типа. *}

З а р е з е р в и р о в а н н о е слово Nil обозначает к о н с т а н т у ссылоч-ного типа , к о т о р а я н и на что не у к а з ы в а е т .

В ы д е л е н и е о п е р а т и в н о й п а м я т и (в «куче») д л я динамиче-ской переменной базового т и п а о с у щ е с т в л я е т с я с п о м о щ ь ю про-цедуры New(x), где х определен к а к соответствующий указа-тель .

Обращение к д и н а м и ч е с к и м п е р е м е н н ы м в ы п о л н я е т с я по п р а в и л у : <имя_переменной>~. Н а п р и м е р , х~:=15 — в область п а м я т и (два байта) , адрес которой я в л я е т с я з н а ч е н и е м указате-л я х, з а п и с ы в а е т с я 15.

П р о ц е д у р а Dispose(х) освобождает п а м я т ь , з а н я т у ю динами-ческой переменной . П р и этом з н а ч е н и е у к а з а т е л я х становится неопределенным.

Линейные списки. Списком на зывается структура д а н н ы х , к а ж д ы й элемент которой посредством у к а з а т е л я связывается со с л е д у ю щ и м элементом. И з определения следует, что к а ж д ы й элемент списка содержит к а к м и н и м у м одно поле д а н н ы х (на-зовем его data и д л я простоты считаем его типа Integer), оно может иметь с л о ж н у ю структуру , и поле ссылки на следующий элемент (назовем его next). Поле с с ы л к и последнего элемента

Page 337: Pascal 8 Класс - Окулов

Динамические структуры дачных 336

списка имеет значение Nil. Указатель на начало списка (пер-вый элемент) является значением отдельной переменной. При-мер списка, содержащего в поле данных целые числа 3, 5, 1, 9, приведен на рисунке.

Rrst

Описание элемента списка, используемого в течение данного занятия , имеет вид:

Type pt="elem;{"Указатель на элемент списка.*) elem=Record

data:Integer;{*Поле данных. *) next:pt;{*Указатель на следующее элемент списка. *} End;

Var f i r s t:pt; {"Указатель на первый элемент списка. *} Основные операции с элементами списка: • просмотр элементов списка; • вставка элемента в список; • удаление элемента из списка. Просмотр элементов списка, если он создан, очевидная про-

цедура. Procedure Print (z:pt) ; Begin

While zONil Do Begin Write (z*. data, ' ' ) ; z:=z*.next; End;

End; Ее рекурсивная реализация: Procedure Print (z:pt) ; Begin

I f zONil Then Begin Write (z*. data, ' ' ) ; Print (z*. next) ; End;

End;

Page 338: Pascal 8 Класс - Окулов

338 Часть четвертая

И з м е н и т е ее т а к , чтобы э л е м е н т ы с п и с к а в ы в о д и л и с ь , начи-н а я с последнего .

В с т а в к а э л е м е н т а в список в о з м о ж н а л о г и ч е с к и в его нача-ло, к о н е ц и средину . Разберем эти с л у ч а и . В с т а в к а в н а ч а л о с п и с к а имеет вид:

Rrsl New_first

Procedure Ins_begm (Var f i r s t : p t ; el:Integer); Var new_first: pt;

Begin New (new_first) ; ( *1*) new_firstЛ.next:=first;{*2*} new_first".data:=el; f i r s t :=new_first;(*3*)

End;

« Ж и р н ы м и » о т р е з к а м и на рисунке в ы д е л е н ы действия , вы-п о л н я е м ы е в процедуре . С п о м о щ ь ю ц и ф р на рисунке и в тексте процедуры п о к а з а н ы действия соответствующих операторов.

Вставка в к о н е ц списка осуществляется с п о м о щ ь ю следую-щ е й процедуры. Вопрос о том, к а к и м образом з н а ч е н и е м указа-теля las t стал адрес последнего элемента списка , пока оставим о т к р ы т ы м .

First

Page 339: Pascal 8 Класс - Окулов

Динамические структуры дачных 338

Procedure Ins_end(Var last:pt; el:Integer); Begin

New (last" . next) ; (*l*j last: =lasf . next; { *2*} last".data:=el; last".next:=Nil;

End;

Вставку в средину списка проиллюстрируем очередным ри-сунком.

Рх

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

Пусть м ы создаем упорядоченный по неубыванию список эле-ментов (информационная часть любого элемента списка меньше информационных частей следующих за ним элементов списка и л и равна им). Д л я того, чтобы найти место для вставки очеред-ного элемента (значение указателя рх на рисунке), следует про-сматривать элементы списка до тех пор, пока вставляемый эле-мент больше информационной части текущего элемента.

Г ~| tновое

Page 340: Pascal 8 Класс - Окулов

340 Часть четвертая

Procedure Ins_med(Var f i r s t : p t ; el:Integer); Var t,new_m:pt; Begin

New (new_m) ; t:=first;

While el>t".next".data Do t:=t".next; new_m".next:=t".next; new_m".data:=e; tл.next:=new_m;

End; И наконец , общая логика вставки элемента в упорядочен-

н ы й список. Создаваемый список отличается от декларирован-ного в начале тем, что он описывается двумя указателями : на п е р в ы й и последний элементы списка .

Procedure Ins(Var first,last:pt; el:Integer); Begin

I f el<first". data Then Ins__begin ( f i r s t , el) Else I f el>last".data Then Ins_end(last,el)

Else Ins_med(first,el); End; Ввод исходных данных при создании упорядоченного списка

осуществляется с к л а в и а т у р ы . Напомним, что признак к о н ц а файла (Eof = True) в этом случае вырабатывается при н а ж а т и и к л а в и ш Ctrl+Z. Все действия по организации ввода представле-ны в процедуре Solve.

Procedure Solve(Var f i r s t , l a s t : p t ) ; Var el:Integer;

Begin Write('First element: ' ) ; Read (el) ; I f Not Eof Then Begin first:=Nil;

Ins_begin ( f i r s t , el) ; last:=first;

End Else Begin first:=Nil;last:=Nil;End;

While Not Eof Do Begin W r i t e ( " N e x t : ' ) ; Read(el); I f Not Eof Then I n s ( f i r s t , l a s t , e l ) ;

End; End;

Page 341: Pascal 8 Класс - Окулов

Динамические структуры дачных 341

Проведите несколько экспериментов и проанализируйте ре-зультаты:

• исключите логику отдельного ввода первого элемента спи-ска из Solve-,

• исключите анализ признака конца файла из тела ц и к л а While-,

• исключите переменную Last, список должен иметь только один указатель на начало.

Действия при удалении элемента из списка различны в за-висимости от места удаляемого элемента: первый он или нет. На рисунке показано удаление элемента из средины списка. Д л я сохранения структуры списка необходимо помнить адрес элемента списка, предшествующего удаляемому элементу^*) , а т акже запоминать адрес удаляемого элемента для корректно-го использования процедуры Dispose.

dx х

э -

Приведем процедуру удаления всех элементов списка, ин-формационная часть которых равна заданному числу (el).

Procedure Del_el (Var first:pt; el: Integer) ; Var t,x,dx:pt; Begin

t:=first;(*Переменная цикла. *} While toNil Do (*Пока список не просмотрен.*)

I f t*. data=el Then(*EcTb совпадение. *) I f t=first Then Begin(*Удаляем первый элемент списка. *}

х := first;{* Запоминаем, ибо «кучу» засорять не следует. *} First:=first*.next;("Изменяем значение указателя на первый элемент списка. *) Dispose (х) ; ( *Освобождаем место в «куче». *}

Page 342: Pascal 8 Класс - Окулов

342 Часть четвертая

t:=first;{"Переменная цикла изменила свое значение. *} End

Else Begin х:=t;{"Запоминаем адрес удаляемого элемента. *)

t:=t".next; dx".next:=t; f"Удаление элемента не должно нарушать структуру списка, ключевой оператор процедуры. *} Dispose (х);

End Else Begin dx:=t;t:=t".next;End;f"Переход к следующему элементу списка. Адрес т е к у щ е г о запоминается в переменной dx. "}

End;

Отладка программ, использующих указатели , достаточно не-простое з а н я т и е , особенно при первом знакомстве . Рекоменду ется создавать небольшой список , н а п р и м е р из четырех элемен-тов 1, 2, 3, 4 , и в окне Watches о т с л е ж и в а т ь изменение всех связей . П р и отладке процедуры Del_el в пошаговом р е ж и м е окно Watches м о ж е т иметь в н а ч а л ь н ы й момент времени следу-ю щ и й вид.

Watches

T.data 1 t'.nexT.data 2 t".next".next" data 3 t" next" next".next" data 4 first".data 1 firstt" next".data 2 firstt" next" next" data 3 first".next".next" next" data 4

Экспериментальный раздел работы

1. «Считалочка» . N ребят расположены по кругу . Н а ч а в отсчет от первого , у д а л я ю т к а ж д о г о k-го, с м ы к а я п р и этом круг . Определить порядок удаления ребят из круга .

Д л я х р а н е н и я д а н н ы х об у ч а с т н и к а х и г р ы используем коль-цевой список (значением п о л я N e x t последнего элемента явля-ется адрес первого элемента списка) .

Page 343: Pascal 8 Класс - Окулов

Динамические структуры дачных 343

Program County Const N=10;

Type pt="elem; elem=Record

data:Integer; next:pt;

end; Var f i r s t : p t ; Procedure Print(u:pt);("Вывод элементов списка.*}

Var t : p t ; Begin

t:=u; Repeat

Write(t".data,' ' ) ; t : = t л . n e x t ; Until t=и;{*До тех пор, пока указатель не получит значение адреса на первый элемент списка. *) WriteLn;

End; Procedure I m t (Var f i r s t :pt) ;{ *Первоначальное формирование списка.*}

Var t , l a s t : p t ; i : I n t e g e r ; Begin

New(t);t*,data:=N;t".next:=Nil;first:=t;last:=t; For i:=N-l DownTo 1 Do Begin

New ( t ) ; tл.next:=first;t".data:=i; f i r s t : = t ; l a s t ' . n e x t : = t ;

End; End;

Procedure Game(u:pt; k:Integer); Var t : p t ;

i .-Integer; Begin

t : =u ; Repeat

For i:=l To k-1 Do Begin ( *Пропускаем k-1 элемент. *}

Page 344: Pascal 8 Класс - Окулов

344 Часть четвертая

t:=u;и:=и . next; End; WriteLn(и".data,' ');f"Удаляемый элемент.*} t".next:=u".next; Dispose (u); u:=t".next; Print(u);{*To, что осталось в списке.*}

Until u=u".next;{"Пока не останется один элемент в списке. *}

End;

О с н о в н а я п р о г р а м м а .

Begin first:=Nil; Init ( f i r s t ) ; P r i n t ( f i r s t ) ; Game(first,2);{"Удаляем каждого второго ребенка. *}

End.

И с с л е д у й т е з а д а ч у д л я р а з л и ч н ы х з н а ч е н и й N. Составьте т а б л и ц у о с т а в ш и х с я ребят (t — номер о с т а в ш е г о с я ребенка) д л я з н а ч е н и й N от 1 до 6 4 .

12 13 14 15 16 17

Э к с п е р и м е н т а л ь н ы м п у т е м у с т а н о в и т е з а к о н о м е р н о с т ь : • t(l)=l п р и N=1, • t(2*N)=2*t(N)-l п р и N>1, • t(2*N+l)=2*t(N)-l п р и N>1. Е с л и iV=2 m +9, где 2т — н а и б о л ь ш а я степень 2, не превосхо-

д я щ а я N, a q — ра зность N-2m, то номер о с т а в ш е г о с я ребенка в ы ч и с л я е т с я по ф о р м у л е : t(2m+q)=2*q+l п р и т>0 и 0<q<2m. Э к с п е р и м е н т а л ь н о проверьте п р а в и л ь н о с т ь д а н н о й ф о р м у л ы и н а п и ш и т е в е р с и ю п р о г р а м м ы , в ы ч и с л я ю щ е й н о м е р оставшего-с я р е б е н к а без и с п о л ь з о в а н и я ссылочного т и п а д а н н ы х . Срав-ните р е з у л ь т а т ы .

2. П р о м о д е л и р у е м работу со с п и с к о м , и с п о л ь з у я м а с с и в ы и не используя указателей . В массиве А х р а н и м значения элемен-тов в порядке и х поступления на обработку, в массиве Next — с с ы л к у на с л е д у ю щ и й элемент . Создадим у п о р я д о ч е н н ы й по возрастанию список элементов. В переменной head храним ад-рес первого элемента списка . Глобальные переменные н а ш е й п р о г р а м м ы :

Page 345: Pascal 8 Класс - Окулов

Динамические структуры дачных 344

Const N= ; N1=7777;{ *N1 - несуществующая ссылка на следующий элемент. *)

Type MyArray=Array[1..N] Of Integer; Var A,Next:MyArray;

ind,head:Integer;

L

Пусть на обработку поступили числа 5, 7, 3, 4. Состояние переменных отражено на рисунке. После обработки чисел 1 и 9 з н а ч е н и я элементов массивов А и Next изменится на следую-щее. В массив А элементы записываются в порядке их поступ-ления на обработку. Д л я такой записи необходимо знать адрес очередной свободной ячейки (значение переменной ind). Неско-лько сложнее с массивом Next. В переменной head указывается место записи наименьшего элемента массива А, а в Next [head] — адрес следующего по возрастанию элемента массива А.

Д л я вставки очередного элемента необходимо, кроме записи его в т екущую позицию Л, изменить ссылки в массиве Next, в Next[ind] записывается адрес следующего по возрастанию эле-мента массива Л, a ind является значением элемента Next, со-ответствующим предыдущему элементу. Итак, процедура встав-ки элемента в список имеет вид:

П—452

Page 346: Pascal 8 Класс - Окулов

346 Часть четвертая

Procedure Insert (Var head:Integer;x:Integer); Var 1,3:Integer; Begin

Inc (md) ;A[md] :=x;Next [md] : =0/ ( *Записываем в А по текущему значению переменной md. *} 1:=head;j:=head;{*B j храним адрес предыдущего элемента списка.*} While (1<>0) And (x>A[i]) Do Begin j:=i;i:=Next [1];

End; (*Обычный просмотр элементов списка.*} Next[md]:=1;(*Номер следующего элемента списка является значением переменной 1. *) I f i=head Then head:=md Else Next[3]:=ind; {*Если элемент вставляется в начало списка, то изменяется значение переменной t , иначе корректируется поле ссылки предыдущего элемента. *}

End;

Н а п и с а н и е процедуры у д а л е н и я элемента и з с п и с к а не вы-зывает особых сложностей. Необходимо найти у д а л я е м ы й эле-мент и изменить значение с с ы л к и у предыдущего элемента — Next. В процедуре на место удаляемого элемента в массиве А записывается 0, а в массиве Next значение 7777.

Procedure Delete(Var head:Integer;x:Integer); Var 1,3:Integer; Begin

1:=head;3:=head; While (10O) And (xOA [1] ) Do Begin { *Поиск удаляемого элемента.*} j : =1; i : =Next [1 ] ; End; I f iohead Then Begin

Next[j]:=Next[1];{*Изменение значения ссылки на следующий элемент списка. *) A[i]:=0; Next[1]:=N1;{*Необязательные операторы. *}

End Else Begin j:=head; head:=Next[head]; I*Необязательные операторы. *)

Nex t [ j ] : =N1; I * Удаляем первый элемент списка.*}

End; End;

Page 347: Pascal 8 Класс - Окулов

Д и н а м и ч е с к и е с т р у к т у р ы д а ч н ы х 347

Разработайте полный текст программы для работы со спис-ком. Измените программу так, чтобы запись очередного эле-мента осуществлялась на первое свободное место массива А, т. е. в Вашей основной программе вставка и удаление элемен-тов должны быть не последовательными процессами, а взаимно чередующимися.

Задания для самостоятельной работы

1. Разработать: • функцию, вычисляющую среднее арифметическое эле-

ментов непустого списка; • рекурсивную функцию проверки наличия в списке за-

данного элемента; • процедуру перестановки первого и последнего элементов

непустого списка; • процедуру вставки нового элемента перед (после) каж-

дым вхождением заданного элемента; • функцию проверки совпадения списков L t и Ь2; • функцию проверки вхождения списка L t в список Ь2; • процедуру переноса в конец непустого списка L его пер-

вого элемента; • процедуру переноса в начало непустого списка его по-

следнего элемента; • процедуру копирования в список L за каждым вхожде-

нием заданного элемента всех элементов списка ; • процедуру объединения двух упорядоченных по неубы-

ванию списков Lj и Ь2 В ОДИН упорядоченный по неубы-ванию список путем построения нового списка L и изме-нением соответствующим образом ссылок в Lj и Ь2,

• функцию проверки упорядоченности элементов списка; • функцию подсчета количества слов списка (поле Data

имеет тип String), начинающихся и оканчивающихся од-ним и тем же символом.

2. Разработать процедуру удаления из списка L: • второго элемента, если такой есть; • всех элементов, равных х; • первого отрицательного элемента, если такой есть; • всех отрицательных элементов.

3. Разработать процедуру формирования списка L путем вклю-чения в него по одному разу элементов: • входящих хотя бы в один из списков Lj и Ь2; • входящих одновременно в оба списка Ll и L2;

Page 348: Pascal 8 Класс - Окулов

347 Часть четвертая

• входящих в список Lj , но не входящих в список Ь2 , • входящих в один из списков L, и Ь2 , но в то же время не

входящих в другой из них. 4. Многочлен Р(х )=апхп+ап_1хп 1+...+а1х+а0 с целыми коэффи-

циентами можно представить в виде списка, причем если а=0, то соответствующий элемент не включается в список (на рисунке показано общее представление многочлена и пример s(x)=-5x6+3x2-x+7).

р о ч шш Описать тип данных, соответствующий такому представле-

нию многочленов, разработать следующие функции и процеду-ры для работы с этими списками-многочленами:

• логическую функцию Equality(p,q), проверяющую ра-венство многочленов р и д;

• функцию Meaning(p.x), вычисляющую значение много-члена в целочисленной точке х;

• процедуру Add(p,q,г) вычисления суммы многочленов q и г, результат — многочлен р.

5. Выполнить задания 1, 3 при реализации списков с помощью массивов типа А и Next так, как это сделано во втором упраж-нении экспериментального раздела занятия.

6. В задаче о «считалочке» удаляется каждый второй ребенок. Написать программу определения номеров двух последних оставшихся ребят.

7. В задаче о «считалочке» Петя находится на месте i. Сущест-вует ли значение k (k-й удаляемый ребенок) такое, что Петя останется последним ребенком в круге. Написать программу поиска значений k.

8. Вставка в линейный список перед заданным элементом вы-полняется неэффективно, ибо есть ссылка только на следую-щий элемент списка. При использовании списка с двумя ука-зателями (адресами связи) на следующий и предыдущий элементы этот недостаток устраняется. Напишите програм-му работы (вставки, удаления элементов) с таким типом спис-

В заключении занятия отметим ряд преимуществ и недостат-ков динамических структур. Динамическое размещение структур данных имеет свои преимущества и недостатки. В компетенции

Page 349: Pascal 8 Класс - Окулов

Динамические структуры дачных 349

программиста оценивать их с точки зрения конкретной решае-мой задачи.

Преимущества • Разумное использование динамических структур данных

приводит к сокращению объема памяти, необходимого для работы программы.

• Динамические данные в противоположность статическим и автоматически размещаемым данным не требуют объяв-лений их как данных фиксированного размера. В боль-шинстве систем программирования для «кучи» выделяет-ся достаточно большой объем памяти.

• Ряд алгоритмов более эффективен при реализации их с использованием динамических структур. Например, встав-ка элемента в массив на определенное место требует пере-мещения части элементов массива. При вставке в середи-ну списка достаточно нескольких операторов присваивания.

Недостатки • Алгоритмы на динамических структурах обычно более

сложны, трудны для отладки по сравнению с аналогичны-ми алгоритмами на статических данных.

• Использование динамических структур требует затрат на память для ссылок. В некоторых задачах объем памяти, отводимой для ссылок, превосходит объем памяти, выде-ляемой непосредственно для данных.

• Существуют алгоритмы, реализация которых более эф-фективна на обычных данных. Например, в ряде задач индекс элемента в массиве можно просто вычислять, в то время как использование списковых структур потребует обхода списка.

Page 350: Pascal 8 Класс - Окулов

350 Часть четвертая

Занятие № 27. Стек

План занятия • с т р у к т у р а д а н н ы х стек , основные сведения ; • э к с п е р и м е н т а л ь н а я работа с п р о г р а м м а м и п р о в е р к и пра-

в и л ь н о с т и р а с с т а н о в к и скобок , в ы ч и с л е н и я в ы р а ж е н и я в п о с т ф и к с н о й форме , п р е о б р а з о в а н и я в ы р а ж е н и я из ин-ф и к с н о й в п о с т ф и к с н у ю форму;

• в ы п о л н е н и е с а м о с т о я т е л ь н о й работы.

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

Простой п р и м е р : д е т с к а я п и р а м и д к а . Процесс сборки и р а з б о р к и п и р а м и д к и подобен процессу ф у н к ц и о н и р о в а н и я сте-ка . В любой момент времени доступен л и ш ь один элемент с т е к а — в е р х н и й . И з о п р е д е л е н и я следует, что и з в л е к а т ь э л е м е н т ы из стека м о ж н о только в п о р я д к е , об-ратном п о р я д к у и х добавления в стек («первый п р и ш е л , по-следний у ш е л » ) .

Основные о п е р а ц и и со стеком: з апись элемента в стек , изв-л е ч е н и е элемента из стека , п р о в е р к а н а л и ч и я элементов в сте-ке .

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

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

Type pt="elem; elem=Record d a t a : I n t e g e r ; n e x t : pt;End;

Var st: pt; Если стек пуст, то значение у к а з а т е л я st равно Nil. Д л я работы со с п и с к а м и будем использовать процедуры, ра-

зобранные на п р е д ы д у щ е м з а н я т и и . Н а п о м н и м их . Запись в стек. Процедура записи элемента в стек д о л ж н а со-

д е р ж а т ь два параметра : п е р в ы й определяет указатель на нача-ло стека , второй — записываемое в стек значение .

З а п и с ь в стек производится аналогично вставке нового эле-мента в начало списка :

Page 351: Pascal 8 Класс - Окулов

Динамические структуры дачных 350

Procedure MriteStack(Var u:pt; dig:Integer); Var x: pt; Begin

New (x) ;

x".data:=dig; x".next:=u

End; Извлечение элемента из стека. В результате выполнения

этой операции некоторой переменной i должно быть присвоено значение первого элемента стека и изменено значение указате-л я на начало списка.

Procedure ReadStack(Var u:pt; Var dig:Integer); Var x: pt; Begin

dig:=u".Data; x:=u; u:=u".Next; Dispose (x);

End;

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

Function Free(u.-pt) .-Boolean; Begin

I f u=Nil Then Free:=False Else Free:= True; End;

Экспериментальный раздел работы

1. Написать программу, проверяющую своевременность закры-тия скобок типа «(, ), {, }, [, ]» в строке символов (строка со-стоит из одних скобок этих типов).

Д л я решения задачи определим стек, элементами которого я в л я ю т с я символы:

Type pt="el; el=Record data: Char; next: pt; End;

В процессе решения анализируем символы строки а: String. Если встречена одна из открывающих скобок, то она записыва-ется в стек. П р и обнаружении закрывающейся скобки, соответ-ствующей скобке, находящейся в вершине стека, последняя удаляется . П р и несоответствии скобок выдается сообщение об ошибке, которое фиксируется в логической переменной.

Page 352: Pascal 8 Класс - Окулов

351 Часть четвертая

Осталось в ы я с н и т ь , к а к определить, соответствует ли очеред-н а я з а к р ы в а ю щ а я скобка скобке, н а х о д я щ е й с я в вершине стека. Оказывается , что коды соответствующих друг друту скобок отли-ч а ю т с я не более чем на 2: { } имеют коды 1 2 3 - 1 2 5 ; [ ] — 91-93 ; ( ) — 4 0 - 4 1 , причем код о т к р ы в а ю щ е й скобки меньше. То есть выполнение условия If (Ord(a[i])-Ord(stack" .data))<=2 Then... говорит о соответствии обрабатываемых скобок.

Program Parenth; Type pt="el/

el=Record data:Char/next:pt/End/ Var s:String/

f -.Boolean / Procedure ReadStack(Var t:pt/Var x:Char)/(*3Ta и следующие процедура и функция описаны ранее. *} Procedure WriteStack(Var t:pt/x:Char)/ Function Free (t:pt) .-Boolean/ Procedure Solve(a:String/Var pp:Boolean)/

Var head:pt/ i :Integer/ ch:Char/

Begin head:=N11/pp:=True/ i : =1 / While (i<=Length (a)) And pp Do Begin

I f a[i] In [ ' ( ' , ' [ ' , ' { ' ] Then WriteStack (head, a [i]) Else Begin

I f Free(head) Then Begin ReadStack (head,ch)/

I f ABS (Ord(ch)-Ord(a[i]))>2 Then pp:=False/

End Else pp:=False/

End/ Inc (i) /

End/ End/

Begin WriteLn('Введите строку.')/ ReadLn(s)/ I f s o " Then Begin

Solve ( s , f ) /

Page 353: Pascal 8 Класс - Окулов

Динамические структуры дачных 353

I f f Then WriteLn('Строка записана правильно.') Else WriteLn('В строке содержится ошибка.');

End Else WriteLn('Строка пустая.'); ReadLn;

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

2. Написать программу вычисления значения выражения, пред-ставленного в обратной польской записи (в постфиксной фор-ме). Выражение состоит из цифр от 1 до 9 и знаков операций.

Просматривая строку, анализируем очередной символ, если это:

• цифра , то записываем ее в стек; • знак , то читаем два элемента из стека, выполняем матема-

тическую операцию, определяемую этим знаком, и зано-сим результат в стек.

После просмотра всей строки в стеке должен оставаться один элемент, он и я в л я е т с я решением задачи.

Необходимо выяснить , к а к записать условие: очередной сим вол строки — знак, и к а к перевести символ, обозначающий цифру, в саму цифру. Процедура Val(s.x.k) преобразует симво-льное представление цифры s в соответствующее числовое зна-чение. П р и этом значение k=0, если такое преобразование воз-можно , в противном случае k<>0.

Program Polish; Тур pt="el;

el=Record data:Real;next:pt;End; Var s:String;

f:Boolean; rez:Real;

Procedure ReadStack(Var t:pt;Var x:Real);

End.

Обычная запись: (b+c) *d

a + (b+c) *d

Обратная польская запись: b с + d *

a b с + d * +

Page 354: Pascal 8 Класс - Окулов

354 Часть четвертая

Procedure WriteStack(Var t:pt;x:Real); Function Free(t:pt):Boolean; Procedure Operation(ch:Char;a,b:Real; Var c:Real);

Begin Case ch Of

'+':c:=a+b; :c:=b~a;

'*':c:=a*b; ' / ' : c : = b / a ;

End; End;

Procedure Solve(a:String;Var pp:Boolean;Var z:Real); Var head: p t ;

i , k : Integer; r,w: Real;

Begin head:=Nil; pp:=True; 1: =1 ; While (K=Length (a)) And pp Do Begin

I f a[i]<>' ' Then Begin("Пропускаем пробелы. *} I f Not(a[i] In [ ' + ' , ' - ' , ' * ' , ' / ' ] ) Then Begin

Val (a[i] , r , k ) ; I f k=0 Then WriteStack(header)

Else pp:=False; End

Else Begin I f Free(head) Then ReadStack(head,r)

Else pp:=False; I f Free(head) Then ReadStack(head, w)

Else pp:=False; I f pp Then Begin

Operation (a[i ] , r , w , r ) ; WriteStack(head,r) ;

End; End;

End; Inc ( i ) ;

End; ReadStack(head,z);

End; Begin

WriteLnС Введите строку.');

Page 355: Pascal 8 Класс - Окулов

Динамические структуры дачных 355

ReadLn (s) ; I f s<>" Then Begin

Solve (s, f , r e z ) ; I f f Then WriteLn(rez:6:2) Else

WriteLn('Выражение не может быть вычислено.') ; End

Else WriteLn ('Строка пустая.'); End. Обработка 3 .1 6 * даст ответ 6. Измените программу так,

чтобы обрабатывались и выражения, содержащие веществен-ные числа.

При вводе выражения 3 4 6 5 + получается результат 11, что явно не соответствует действительности. Выражение ошибочно. Устраните эту неточность.

Пусть операция возведения в степень имеет обозначение «"». Измените программу так, чтобы осуществлялась обработка и этой операции.

3. Рассмотрим выражение А+В. Оно записано в обычной, или инфиксной, форме. Запись А В + называют постфиксной, а запись + А В — префиксной. Префиксы *пре», «пост», «ин» говорят об относительной позиции оператора по отношению к обоим операндам. Преобразование ( и вычисление) выраже-ния А+В*С в А В С * + (или А В + С *) требует знания того, к а к а я из двух операций выполняется первой. Для операций « + », «—», «*», «/» и «"» приоритет считаем традиционным (от высшего к низшему): возведение в степень, умножение/деле-ние, сложение/вычитание. Нашей задачей является написа-ние программы преобразования выражения из инфиксной в постфиксную форму. В предыдущем задании мы брали по-стфиксную форму (польская запись) как нечто заданное и то-лько вычисляли выражение. Но прежде чем перейти к раз-работке, приведем несколько примеров записи одного и того же выражения в различных формах.

Инфиксное представление

Постфиксное представление

Префиксное представление

А+В-С АВ+С- -+АВС (А+ВПС-D) AB+CD-' '+AB-CD

A'B'C-D+E/F/(C+H) AB"C'D-EF/GH+/+ +-'ABCD/EF+GH A-B/(C'D~E) ABCDE-'/- -A/B'C^DE

Page 356: Pascal 8 Класс - Окулов

355 Часть четвертая

П р о ц е с с п р е о б р а з о в а н и я в ы р а ж е н и я в п о с т ф и к с н у ю ф о р м у о т р а ж е н в т а б л и ц е д л я в ы р а ж е н и я (A+B)*(C+D). В первом столбце п р и в о д и т с я ч и т а е м ы й с и м в о л из исходной с т р о к и , во втором столбце — р е з у л ь т и р у ю щ а я строка и в третьем столб-це — состояние с т е к а .

( ( A A < + A В AB (+ ) AB+

AB+

( AB+ С AB+C + AB+C *( + D AB+CD ) AB+CD+

AB+CD+*

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

Program Change_expression; Type pt="el;

el=Record data:Char;next:pt;End; Var s,rez:String; Procedure ReadStack(Var t:pt;Var x:Char);(*Эти процедуры и функция рассмотрены ранее. Изменения не существенны. *) Procedure WriteStack(Var t:pt;x:Char); Function Free(t:pt):Boolean;

Procedure Solve(a:String;Var z:String); Var head:pt;

i:Integer; w: Chart-

Begin head:=Nil;z:='';

Page 357: Pascal 8 Класс - Окулов

Then z:=z+a[1] Else I f a[i] in [' + ' , *'r ' / ' , ' (>j Then WriteStack(head,a[i])

Else I f a[1]=')' Then Begin{"Считываем из стека до символа « (». *}

ReadStack (head, w) ,-While wo' (' Do Begin z:=z+w; ReadStack (head, w) ;

End; Ends-

End; Inc (1) ;

End; While Free(head) Do Begin{"Дополняем строку символами операций, запомненных в стеке. *) ReadStack(head,w); z:=z+w; Ends-

End; Begin

WriteLn('Введите выражение в обычной (инфиксной) форме.'); ReadLn (s) ; Solve (s,rez); Wri teLn (rez) ;

End.

В программе не предусмотрена обработка ошибок в исходной строке. Устраните этот недостаток, чем больше возможных о ш и б о к будет анализироваться , тем лучше. Определите, при к а к и х исходных д а н н ы х приведенная программа выдает непра-в и л ь н ы й результат . Сохранение «костяка» программы (мини-м а л ь н ы е изменения) говорит о качестве его написания.

Измените программу так , чтобы обрабатывались не только однобуквенные имена переменных, но и вещественные числа. З н а ч е н и я переменных могут быть заданы предварительно в от-дельном массиве.

Page 358: Pascal 8 Класс - Окулов

358 Часть четвертая

Задания для самостоятельной работы

1. Д л я х р а н е н и я элементов стека и с п о л ь з у е т с я о б ы ч н ы й одно-м е р н ы й массиЕ из N элементов . Р е а л и з у й т е о п е р а ц и и рабо-т ы со стеком ( R e a d S t a c k , WriteStack, Free) в этом случае . За-м е т и м , что перед и с п о л ь з о в а н и е м WriteStack н е о б х о д и м о п р о в е р я т ь переполнение стека .

2. Д а н а строка вида : s*w (s, w — строки из символов , не содер-ж а щ и х с и м в о л а *). Ч т е н и е р а з р е ш е н о по одному символу . П р о в е р и т ь , я в л я е т с я л и строка w обратной строке s. Напри-мер, д л я с л у ч а я s=ABCDEF, w=FEDCBA ответ положитель -н ы й .

3. Преобразуйте каждое из приведенных н и ж е в ы р а ж е н и й в пре-фиксную и п о с т ф и к с н у ю формы:

• А+В-С • (А+В )*( C-D )"E*F • (A+B)*(C~(D-E)+F)-G • A+(((B-C)*(D~E)+F)/G)"(H-J) • ((A-(B+C))*D)-(E+F)

4. Разработать программу преобразования в ы р а ж е н и я из инфик-сного в префиксное представление .

5. Разработайте программу в ы ч и с л е н и я в ы р а ж е н и я в префик-сной форме.

6. Разработать программу преобразования постфиксной формы в инфиксную. П р и этом результирующее в ы р а ж е н и е должно быть записано с необходимыми скобками. Например , АВ+С* д о л ж н о быть преобразовано в (А+В)*С.

7. А б с т р а к т н а я в ы ч и с л и т е л ь н а я м а ш и н а имеет один регистр и шесть и н с т р у к ц и й :

LD А Помещает операнд А в регистр, ST А Помещает содержимое регистра в переменную с именем А. AD А Прибавляет значение А к регистру Результат остается в

регистре. SB А Вычитает значение А из регистра. Результат остается в регистре. ML А Умножает содержимое регистра на значение переменной с

именем А Результат остается в регистре. DV А Делит содержимое регистра на значение А. Результат остается в

регистре

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

Page 359: Pascal 8 Класс - Окулов

Динамические структуры дачных 359

ходимых для вычисления выражения. Результат вычисления должен оставаться в регистре, разрешается использовать обо-значения Тп для временных переменных.

Пример. Для выражения ABC*+DE-/ перечень инструкций имеет вид:

LD О

SB Е ST Т1 ST Т2

LP А LP Т1

DV 72

Page 360: Pascal 8 Класс - Окулов

359 Часть четвертая

Занятие № 28. Очередь

План занятия • экспериментальная работа с программами реализации оче-

реди и дека через массив и с помощью двунаправленного списка;

• выполнение самостоятельной работы.

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

• запись элемента в очередь, если это возможно, т. е. нет пе-реполнения структуры данных, выбранной для хранения элементов очереди;

• чтение элемента из очереди, естественно, если очередь не пуста.

В очереди, в силу её определения, доступны две позиции: ее начало (не с точки зрения обслуживания, а с точки зрения за-писи), куда заносятся новые элементы, и ее конец, откуда изв-лекаются элементы. Поэтому для работы с очередью (независи-мо от выбранной структуры данных для хранения её элементов) необходимо описать две переменные, назовем их head и tail.

Экспериментальный раздел работы

1. Рассмотрим использование одномерного массива к а к струк-туру данных для хранения элементов очереди.

А 1 '

tail

head

На рисунке показан «срез» очереди на какой-то момент вре-мени. В очереди остался один не обслуженный элемент — 7. После следующего чтения очередь окажется пустой. Оговорим значения head и tail. В начальный момент работы они равны

Page 361: Pascal 8 Класс - Окулов

Динамические структуры дачных 361

нулю. После записи элемента значение head указывает на запи-санный элемент, т. е. перед записью его необходимо увеличи-вать на 1. Значением tail является адрес прочитанного элемента, т. е. перед следующим чтением его также необходимо увеличить на 1. Запись в очередь и чтение из нее осуществляется по прин-ципу «кольца» . После записи в ячейку с номером N выполняет-ся запись в первую ячейку , если, конечно, она свободна. После чтения из я ч е й к и с номером N осуществляется чтение из пер-вой я ч е й к и , если в нее выполнялась запись. Таким образом, в логике работы с очередью необходимо предусмотреть переход через границу — значение N .

head

tail

Второй момент, требующий разъяснения , это понятия «оче-редь пуста», «очередь переполнена». Очень сложный момент, ибо к а к значение tail может «догонять» значение head — в этом случае «очередь пуста», так и значение head «догоняет» tail — очередь переполнена. Как различить эти случаи? Отло-ж и м анализ этого момента логики и сделаем «костяк» програм-мы.

{$R') P r o g r a m T u r n _ A r r a y ;

Const N=10; Type MyArray=Array[l..N] Of Integer; Va rA:MyArray;

head,tail,w:Integer; Function Over_Turn (up, down: Integer) .-Boolean; /*Первая версия функции, вряд ли работающая правильно. То же самое относится и к следующей функции. *)

Begin

Page 362: Pascal 8 Класс - Окулов

362 Часть четвертая

I f up =down Then Over_Turn:=True Else Over_ Turn:=Fal set-

End; Function Empty_Turn(up,down:Integer) :Boolean;

Begin I f up =down Then Empty_Turn:=True Else Empty_ Turn:=False;

End; Procedure Add_Turn(Var up:Integer;x:Integer); ("Добавляем элемент в очередь.*}

Begin Inc (up); I f up=N+l Then up:=l; A[up]:=x;

End; Procedure Extrac_Turn(Var down:Integer;Var x: Integer);{"Извлекаем элемент из очереди."}

Begin Inc (down); I f down=N+l Then down:=l; x:=A[down];A[down]:=0;

End; Procedure Print_Turn;["Рабочий (отладочный) вариант процедуры. Требуется изменить ее так, чтобы просмотр очереди осуществлялся по значениям переменных up, down. Пока значение down меньше или равно up — выводить элемент и изменять значение down. "}

Var i:Integer; Begin

For l : =1 To N Do Wri te(A[i] , ' ' ) ; WriteLn; WriteLn(head, ' ' , t a i l ) ;

End; Begin ("Основная программа. *)

Randomize; FillChar(A,SizeOf (A),0); head:=0;tail:=0; {"Начальные значения указателей записи и чтения."} WriteLn('Занесение в очередь первого элемента.'); Add_Turn (head, 100) ; While Not(Empty_Turn (head, t a i l ) ) And Not

Page 363: Pascal 8 Класс - Окулов

Динамические структуры дачных 362

(Over_Turn(head,tail) ) Do Begin{*Пока очередь не пуста (а вдруг окажется операция чтения из очереди) и нет переполнения очереди, выполняем действия.*)

I f Random (2)>0 Then Extrac_Turn(tail,w) {"Извлекаем из очереди. ") Else Add^Turn(head,Random(200)+1) {*Записываем в очередь. *};

Pnnt_Turn ; ( "Наблюдаем за изменением состояния очереди. *)

End; I f Not(Empty_Turn (head, tail)) Then WriteLn С Очередь переполнена.') ("Если очередь не пуста, то, з н а ч и т , она переполнена.*) Else WriteLn('Очередь пуста.');

WriteLn(head,' ' , t a i l ) ; End.

Запуск программы позволяет провести первичный анализ ее работы. Любые варианты проверки приводят к сообщению «оче-редь пуста». Д а ж е в том случае, когда «очередь переполнена». П о п ы т к и изменить начальные значения tail или head, а т акже способов записи и считывания не изменяют ситуацию. После ряда безуспешных попыток достигается понимание того, что необходима дополнительная переменная логического типа для анализа ситуации: или tail «догоняет» head, или head «догоня-ет» tail. Договоримся о том, что в первом случае р (логическая переменная) равна True, а во втором — False. В начальный мо-мент её значение равно True и при переходе к а к того, так и другого указателей (heap и tail) через значение N она изменяет свое значение на противоположное.

Модифицированный вариант анализа и изменения состоя-н и я очереди имеет вид:

Function Over_Turn (up, down: Integer) -.Boolean; Begin

I f (up =down) And Not p Then Over_Turn:=True Else Over_Turn:=False;

End; Function Empty_Turn (up,down: Integer) -.Boolean;

Begin I f (up =down) And p Then Empty_Turn:=True Else Empty_Turn:=False;

End;

Page 364: Pascal 8 Класс - Окулов

364 Часть четвертая

Procedure Add_Turn (Var up:Integer;x:Integer); Beg±n

Inc (up) ; I f up=N+l Then Begin up:=l; p : = N o t p;End; A[up]:=x ;

End; Procedure Extrac_Turn(Var down:Integer;Var x: Integer);

Begin Inc (down} ; I f down=N+1 Then Begin down:=1;p:=Not p;End; x:=A[down];A[down]:=0;

End;

Е с т е с т в е н н о , что p н е о б х о д и м о ввести в о п и с а н и е перемен-н ы х и п р и с в о и т ь н а ч а л ь н о е з н а ч е н и е р:=Тгие, н а п р и м е р , в той с т р о к е , где head:=0 и tail:=0.

Н а э т и х и з м е н е н и я х н а ш и беды не к о н ч а ю т с я . Е с л и в оче-р е д ь о с у щ е с т в л я е т с я т о л ь к о з а п и с ь и не б ы л о ни одного чте-н и я , то з н а ч е н и е у к а з а т е л я tail о с т а е т с я р а в н ы м 0, а з н а ч е н и е у к а з а т е л я head «идет по в т о р о м у к р у г у » и з а п и с ь в ы п о л н я е т с я на з а н я т ы е м е с т а . В ф у н к ц и ю Over_Turn н е о б х о д и м о внести е щ е одно и з м е н е н и е :

I f ((up =down) And Not p) Or ((down=0) And (up=N)) Then Over_Turn:=True Else Over_Turn:=False;

В п р о ч е м , все п р о б л е м ы с а н а л и з о м у к а з а т е л е й head и tail ре-ш а ю т с я и «детским» образом . Перед к а ж д ы м ч т е н и е м и з очере-д и в ы п о л н я е т с я п р о в е р к а того, а не р а в н ы л и все э л е м е н т ы оче-реди н у л ю . Е с л и «да», то очередь пуста , а если «нет», то в ы п о л н я е т с я ч т е н и е . Точно т а к ж е р е ш а е т с я и вопрос о занято-сти очереди . О д н а к о , в о - п е р в ы х , н а р у ш а е т с я п р и н ц и п работы с очередью (а это главное , очередь — « ч е р н ы й я щ и к » , взаимо-действовать с к о т о р о й д о п у с к а е т с я т о л ь к о через з н а ч е н и я head и tail); в о - в т о р ы х , имеет место л и ш н и й п р о с м о т р элементов м а с с и в а ; в - т р е т ь и х , п р е д п о л а г а е т с я , что из н у л е в ы х элементов очередь состоять не м о ж е т .

Д е к о м н а з ы в а е т с я с т р у к т у р а д а н н ы х , в к о т о р о й запись и у д а л е н и е элементов р а з р е ш а е т с я с обоих к о н ц о в . Р е а л и з а ц и я д е к а с и с п о л ь з о в а н и е м м а с с и в а требует дополнительного введе-н и я двух п р о ц е д у р : ч т е н и я «с головы» и з а п и с и «с хвоста» .

Page 365: Pascal 8 Класс - Окулов

Динамические структуры дачных 364

ISR+} Program Dec_Array;

Const N=10; Type MyArray=Array[1..N] Of Integer; Var A:MyArray;

head,tail,w:Integer; p-.Boolean;

Procedure Prmt_Dec; {"Эта и следующие процедуры и функции рассмотрены ранее (изменения только в названиях). *} Procedure Add_Head(Var up:Integer;x:Integer); Procedure Extrac_Tail(Var down:Integer;Var x: Integer) ; Function Over_Dec (up, down : Integer) -.Boolean; Function Empty_Dec (up,down:Integer):Boolean;

{*Новые процедуры. *) Procedure Add_Tail(Var down:Integer;x:Integer);

Begin A[down]:=x; Dec(down); I f down=0 Then Begin down:=N; p:=Not p;End;

End; Procedure Extrac_Head(Var up:Integer;Var x: Integer);

Begin x:=A[up];A[up]:=0; Dec(up); I f up=0 Then Begin up:=N;p:=Not p;End;

End; Begin(*Может быть, потребуется несколько запусков программы, чтобы отследить все варианты изменения содержимого дека. *)

Randomize; FillChar (A, SizeOf (А) , 0) ; head:=0;tail:=0;р:=True; WriteLn('Запись первого элемента в д е к . ' ) ; Add_Head (head, 100) ; While Not (Empty_Dec (head, t a i l ) ) And Not (Over_Dec (head, t a i l ) ) Do Begin I f Random(2)>0 Then("Запись и чтение с «головы» дека.*)

I f Random (3) >0 Then Add_Head (head,Random (50))

Page 366: Pascal 8 Класс - Окулов

365 Часть четвертая

Else Extrac_Head (head, w) Else("Запись и чтение с «хвоста» дека. *)

I f Random(2)>0 Then Begin I f tailOO Then Add_Tail ( t a i l , Random (100) +50) End

Else ExtracJTail(tail,w); Print_Dec; ReadLn;

End; I f Not (Empty_Dec(head,tail) ) Then WriteLn(' Дек переполнен.')

Else WriteLn('Дек пуст.'); WriteLn(head,' ' , t a i l ) ;

End.

2. П р и выборе двунаправленного списка д л я реализации очере-ди снимается проблема анализа переполнения очереди, а при-знаком «пустой очереди» я в л я е т с я значение указателя на на-чало с п и с к а (head), равное Nil. Д л я д а н н о й з а д а ч и м о ж н о и с п о л ь з о в а т ь и о д н о н а п р а в л е н н ы й список , но в этом случае при к а ж д о м чтении из очереди необходимо находить послед-н и й элемент (требуется корректировка адреса связи предпос-леднего элемента). Структура очереди приведена на рисунке.

Описание данных и процедуры работы с элементами очереди приведены н и ж е по тексту .

Type pt="elem; elem=Record data:Integer; next,pred:pt; End;

Var head,tail:pt; Procedure Wnte_Turn (Var up,down:pt;x: Integer) ; {*Запись в очередь — запись в начало списка, х — записываемое число.*)

Var t:pt; Begin

New(t) ; t".data:=x; t " . n e x t : = u p ; t ' . p r e d : = N i l ;

Page 367: Pascal 8 Класс - Окулов

Динамические структуры дачных 366

I f up=Nil Then down:=t Else up".pred:=t;

up:=t; Ends-

Procedure Read_Turn(Var up,down:pt/Var x:Integer) ; (*Чтение не из пустой очереди, чтение последнего элемента списка.*}

Var t :pt ; Begin x:=down".data; t:=down; I f down" . pred=Nil Then Begin down: =Nil ,-up: =Nil ; End

Else Begin down:=down".pred/ down".next:=Nil; End;

Dispose (t) ; End;

Экспериментальная работа возможна с использованием сле-дующей основной программы (необходимо добавить процедуру Print и описать переменную w). Требуется несколько запусков, чтобы она в ы ш л а из р е ж и м а однократной запись в очередь. Из-менение в операторе If Random(2) на Random(3), естественно, увеличит длину очереди, и, может быть, придется использовать Ctrl+Break д л я прерывания работы программы. Добавление оператора ReadLn после Print (head) позволит более детально отслеживать состояние очереди.

Begin Randomize; head:=Nil;tail:=Nil; Write_Turn(head,tail,100);("Запись в очередь первого элемента. *) While headoNil Do Begin ("Пока очередь не пуста. *}

I f Random (2) >0 Then Write_Turn (head, tail, Random(50)) {"Записываем в очередь случайное

Else Read_Turn(head,tail,w);{"Считываем иэ очереди первый иэ записанных элементов. *}

Print(head);("Стандартная процедура вывода элементов списка. *)

End; End.

Page 368: Pascal 8 Класс - Окулов

367 Часть четвертая

Р е а л и з у й т е с т р у к т у р у д а н н ы х дек с п о м о щ ь ю двунаправлен-ного с п и с к а .

3. Диапазон представления ц е л ы х чисел ( In teger , Word, Longlnt) ограничен . Об этом не раз у ж е ш л а речь в этом учебнике . Так , н а п р и м е р , ф а к т о р и а л ч и с л а 13! в ы х о д и т за д и а п а з о н т и п а Longlnt. И с п о л ь з о в а н и е т и п а Real ч а с т и ч н о р е ш а е т пробле-му , но не гарантируется точность вычисления . Однако решать з адачи с м н о г о з н а ч н ы м и ч и с л а м и п р и х о д и т с я . Наиболее ча-сто д л я представления таких чисел используется одномерный массив . К а ж д а я ц и ф р а ч и с л а (или г р у п п а ц и ф р ) х р а н и т с я в отдельном элементе массива. На этом занятии м ы рассмотрим представление многозначного ч и с л а в виде двусвязного спи-ска . К а ж д а я цифра числа — элемент списка . Так , число 25974 имеет следующее представление .

Ч и с л о х р а н и т с я «задом наперед» . Такое представление удоб-но при в ы п о л н е н и и а р и ф м е т и ч е с к и х о п е р а ц и й , а д л я вывода у нас есть адрес п р е д ш е с т в у ю щ е г о элемента . П р и в е д е м «костяк» п р о г р а м м ы , а затем рассмотрим н е с к о л ь к о а р и ф м е т и ч е с к и х операций .

Program Long_Arithmetic; Const Basis=10;{"Основание системы счисления. *) Type pt="elem;

elem=Record data:Integer; next,pred:pt; End; Var head, tail :pt; Procedure WriteLong(t:pt);{"Вывод элементов списка, начиная с его конца. "} Begin

While t<>Nil Do Begin Write(t*.data); t:=t*.pred; End; WriteLn;

End; Procedure Insert_Begm(Var up,down:pt;x:Integer) ; ("Процедура вставки элемента в начало списка

рассмотрена ранее."}

Page 369: Pascal 8 Класс - Окулов

Procedure ReadLong (Var up,down:pt) ; Var ch: Char ; Begin Read(ch) ; While Not (ch In ['0'..'9']) Do Read(ch) ; ("Пропуск символов. Пока не встретим символа цифры. *) While ch In ['0'..'9'] Do Begin

Insert_Begm (up,down,Ord(ch) -OrdCO')) ; Read(ch);

End; Ends-

Begin ("Основная программа.*j Assign (Input,'Input. Txt');Reset (Input); Assign (Output, 'Output. Txt') .-Rewrite (Output) ; head:=Nil; tail:=Nil; ReadLong(head,tall); WriteLong (tail); C l o s e ( I n p u t ) ; Close (Output) ;

End. П р и м е ч а н и е Напомним, что одновременно на экране должны быть обозримы три файла: файл с текстом программы; Input.Txt и Output.Txt. Если для отладки потребуется окно Watches, то и для него должно быть место на экране. Рассмотрим операцию сложения двух чисел. Она выполняет-

ся как обычно, берутся две очередные цифры, находится их сум-ма с учетом значения разряда переноса из предыдущего разряда по основанию системы счисления и новое значение разряда пе-реноса. Результат запоминается. Единственная особенность — записывать результат необходимо в конец нашего списка, иначе потребуется новый вариант процедуры вывода результата.

Procedure AddLong (x,y:pt;Var up, down :pt) ; ( *x, у -указатели на начало списков слагаемых; up, down — указатели на начало и конец списка для представления результата. *)

Var w,v,a,b:Integer; Begin

v:=0;(* Разряд переноса.*)

Page 370: Pascal 8 Класс - Окулов

370 Часть четвертая

While <v<>0) Or (xONil) Or (yONil) Do Begin {*Берем очередную цифру первого слагаемого. *}

I f xONil Then Begin a:=x".data;x:=хЛ . next; End Else a:=0;

f*Берем очередную цифру второго слагаемого. *} I f yONil Then Begin b : =ул . da t a ; y : = у л . next; End Else b:=0; w:=(v+a+b) Mod Basis;("Находим сумму цифр с учетом разряда переноса.*) Insert_End(up,down,w);{*Записываем. *} v(v+a+b) Div Basis;("Вычисляем новое значение разряда переноса.*)

End; End;

Д л я п о л н о т ы о п и с а н и я приведем процедуру вставки элемен-т а в к о н е ц двусвязного с п и с к а .

Procedure Insert_End(Var up,down:pt;х:Integer) ; Var t:pt; Begin

New(t); tЛ.Data:=x;tЛ.pred:=down;t".next:=Nil; I f down=Nil Then u p : = t Else down".next:=t;

down:=t; End;

П р о ц е д у р а у м н о ж е н и я длинного числа на короткое число (меньше , к а к м и н и м у м , в 9 раз м а к с и м а л ь н о г о ч и с л а т и п а Inte-ger) очень походит н а процедуру AddLong.

Procedure Multip (х:pt;у:Integer;Var up,down: pt) ; f *x — указатель на первый элемент списка, соответствующего множителю; у - короткое число; up, down - указатели на начало и конец списка для представления результата. *) Var w,v,a:Integer; Begin

v:=0;{*Разряд переноса.*} While (v<>0) Or (xONil) Do Begin

I f xONil Then Begin a : =x". data ;x: =x". next ;End Else a:=0;

w: = ( v + a * y ) Mod Basis;{"Вычисляем цифру результата. *}

Page 371: Pascal 8 Класс - Окулов

371

. Iasert_End (upf down, ы) ; {"Записываем цифру. *} • v: = (v+a *y) Div Basis; l *Вычисляем новое J значение разряда переноса.*} j End; 5 - End;

j Ирмените основную программу для проверки процедур сло-ж е н и я длинных чисел и умножения длинного числа на корот-кое число.

Как изменится программа, если в качестве основания систе-' мы счисления (константа Basis) взять не 10, а, например, 1 0 0 0 ?

Задания для самостоятельной работы

1. За один просмотр файла f , элементами которого являются це-лые числа, и без использования дополнительных файлов пе-реписать его элементы в другой файл так, чтобы первонача-льно были записаны все числа, меньшие заданного а, затем все чнсла из отрезка [а. Ь] и все остальные. Взаимный поря-док чисел в каждой из групп должен быть сохранен.

Идея решения. В решении задачи числа последовательно считываются из файла. Если очередное число меньше а, то оно записывается в файл, если оно принадлежит отрезку [а, Ь], то заносится в первую очередь, иначе — во вторую очередь. После завершения чтения в выходной файл записываются числа из первой и второй очередей.

2. Содержимое текстового файла Л разделенного на строки, пе-реписать в текстовый файл g, перенося при атом в конец каж-дой строки все входящие в него цифры, с сохранением вза-имного исходного порядка.

3. Д в е с т и в порядке возрастания первые N натуральных чи-сел, в разложение которых на простые множители входят то-

ы г ? tjjbjKO,4H&na 2, 3 и 5. сред Д ,р в ч а н и е

да» p&b D e c 2 ' D e ? 3 ' в котащых хранятся числа, « „ „ г^ы^ Щще не вылолиея. Д з деков счи-

т т ^ т & ' Ъ Р Щ Я ® выводится

Page 372: Pascal 8 Класс - Окулов

372 Часть четвертая

Вывод элемента

Dec 7 Dec2 Dec3 I I

1 2 3 5

2 4 3, 6 5, 10

3 4 , 6 6, 9 5 , 1 0 , 1 5

4 6, 8 6, 9, 12 5 , 1 0 , 1 5 , 2 0

5 6, 8, 10 6, 9, 12, 15 10 ,15 ,20 ,25

6 8, 10, 12 9, 12, 15, 18 1 0 , 1 5 , 2 0 , 2 5 , 3 0

8 10, 12, 16 9, 12, 15, 18, 24 10, 15, 20 25, 40 I

Решите задачу двумя способами — реализацией деков через массивы и через двунаправленные списки.

4. Разработать ф у н к ц и ю сравнения двух д л и н н ы х чисел. 5. Разработать процедуру умножения д л и н н ы х чисел. 6. Разработать программу вычисления 100!

Page 373: Pascal 8 Класс - Окулов

Динамические структуры дачных 373

Занятие № 29. Поиск в графе

План занятия • понятие графа и его представление в памяти компьютера; • экспериментальная работа с программами поиска в глуби-

ну и в ширину , топологической сортировки; • выполнение самостоятельной работы. Термин «граф» впервые появился в работах венгерского ма-

тематика Д. Кенига в 1936 году, хотя ряд задач по теории гра-фов решался ещё JI. Эйлером в XVIII веке.

Пусть V — непустое конечное множество, V<2) — множество всех его двухэлементных подмножеств. Пара G=(V,E), где Е — произвольное подмножество множества V<2\ называется графом (неориентированным графом). Элементы множества V называются вершинами графа, а элементы множества Е — реб-рами. Мощность (количество элементов) множеств V я Е будем обозначать буквами N и М. Говорят, что две вершины и и и гра-фа смежные, если (u,v) является ребром, т.е. принадлежит мно-жеству Е. Граф G называется помеченным, если его вершинам присвоены некоторые метки, например числа 1, 2, ..., N. Отож-дествление каждой из вершин графа с ее номером приводит к увеличению количества графов. На рисунке показаны три раз-личных помеченных графа.

1 2 3

2 1 1

Представление графа в памяти компьютера. Рассмотрим два способа из всех существующих: матрицу смежности и перечень списков смежных вершин. Матрица смежности (Л) имеет раз-мерность N*N. Элемент A[i,j]= 1, если вершины с номерами i, j соединены ребром, и A[i,j]=0, если — i и j не связаны ребром. Представление графа G в виде перечня списков смежных вер-шин требует введения N списков, по количеству вершин. Каж-дый из списков содержит номера всех смежных вершин. Указа-тели на первые элементы списков объединены в массив.

Пример. Д л я помеченного (метки в центре кружков) графа на рисунке матрица смежности и перечень списков смежных вершин приведены н и ж е по тексту.

Page 374: Pascal 8 Класс - Окулов

374 Часть четвертая

1 | 2 | з | 4 | 5 | б | 7 ~ 8 | 9 1 0 0 1 1 _j0 0 0 0 2 0 0 1 0 0 0 0 0 0 3 1 1 0 _ 0 0 1 0 0 0^

4 1 0 _0 0 1 0 0 0 0_

5 0 0 _0 1 0___0 1 1 0_

6 _0_ О J ] ° _7 0 _ _ 0 0 0 1 1 0 1 1_

_8 0 0 _ _ 0 0 1 0 1 0 0_ 9 0 0 0 0 0 0 1 0 0

Экспериментальный р а з д е л работы 1. Поиск в глубину. Идея поиска в глубину формулируется сле-

дующим образом: н а ч и н а я с некоторой в е р ш и н ы v (напри-мер, первой) идем «вглубь», пока это возможно. Рассматри-вается в е р ш и н а и, с м е ж н а я с v. Она выбирается . Процесс повторяется с вершиной и. Если на очередном ш а г е оказа-лось, что нет непросмотренных вершин , с в я з а н н ы х с теку-щ е й , то выполняется возврат к предыдущей вершине и ищет-ся новая вершина , с в я з а н н а я с ней. Если такой вершины не

Page 375: Pascal 8 Класс - Окулов

Динамические структуры дачных 375

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

При реализации поиска используется второй способ пред-ставления графа (перечень списков смежных вершин). Описа-ние элементов списка очевидно. Для хранения значений указа-телей на первые элементы списков (для каждой вершины графа) используется массив А. Номера вершин в той очередности, в которой они просматриваются в процессе поиска в глубину (то, что в квадратных скобках на рисунке), храним в массиве Rez. Для фиксации признака, просмотрена вершина графа или нет, требуется массив Nnew с элементами логического типа.

Program Graph_Depths; Const MaxN-10 ; Type pt="elem;

elem=Record data:Integer;next:pt;End; MyArray=Array[1.. MaxN] Of pt; MyNew=Array[1..MaxN] Of Boolean; MyRez=Array[1 . .MaxN] Of Integer;

Var A:MyArray; ( "Массив указателей на первые элементы списков. "}

Nnew:MyNew;("Массив признаков просмотренных вершин графа. *} Rez:MyRez;("Очередность просмотра вершин графа."} N,cnt:Integer;("Количество вершин в графе. Счетчик числа записей в массив Rez."}

Procedure Insert_List_End (Var t: pt ;x: Integer) ;

Page 376: Pascal 8 Класс - Окулов

376 Часть четвертая

{*Вставка элемента в конец списка. Процедура рассмотрена на предыдущих занятиях. "}

Procedure Init;f*Ввод и инициализация данных. Структура 2 3 4 входного файла: в первой строке ^ количество вершин графа (N) ; затем N

о 1 & о строк, по одной на каждую из вершин 2 1 5 графа. Первое число в строке - 3 4 7 8 количество ребер, выходящих из 2 3 7 вершины, а затем номера вершин, с 4 5 6 8 9 которыми связана текущая вершина. 2 5 7 Справа по тексту приведен пример ^ у входного файла для графа, рассмотренного р а н е е . " )

Var 1,j,w,q:Integer; Begin

Cnt:=0; A s s i g n ( I n p u t , ' I n p u t . t x t ' } ; Reset ( I n p u t ) ; ReadLn(N);{"Количество вершин графа. *} For i:=l To N Do Begin

A[l]:=Nil;Nnew[i]:=True;Rez[i]:=0; End; For i:=l To N Do Begin

Read(q);{"Степень вершины графа — число ребер, связанных с данной вершиной. *) For j :=1 То q Do Begin

Read (w) ; Insert_List_End (A[i] , w) ;

End; End; Close(Input);

End; Procedure Inc_Rez(v:Integer);

Begin Inc (cnt); Rez[cnt]:=v;

End; Procedure Search_ D e p t h s (v:Integer);

{"Рекурсивный вариант процедуры поиска в глубину. "} Var t : p t ; Begin

Nnew[v] :=False; ( "Помечаем вершину как просмотренную.")

Page 377: Pascal 8 Класс - Окулов

Динамические структуры дачных 377

; I 'Начинаем просмотр вершин гоафа, смежных с вершиной v . *} While tONil Do Beam

I f Nnew[t".data] Then Begin{*Если вершина не просмотрена, то записываем ее номер в результирующий массив и идем «вглубь», т.е. К этой вершине. *}

Inc_Rez (t".data); Search_ Depths (t data) ;

E n d ; t.next;{*Переход к следующему элементу списка. *)

End; End;

Procedure Prmt;{"Вывод результата.*) Var l:Integer; Begin

Assign(Output,'Output.txt'); ReWrite(Output) ; For i: =1 To N Do Write (Rez [ I ], ' ' ) ;Wri teLn; Close (Output);

End; Begin{"Один из вариантов основной программы. *)

Imt; Inc_Rez (1) ; Search_ Depths (1) ; Print;

End. В ы ш е рассмотрена рекурсивная реализация поиска в глуби-

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

2. Поиск в ширину. Поиск начинается с фиксированной верши-ны v0. Затем проверяются все вершины, смежные с v0 и не про-смотренные ранее. Их номера заносятся в очередь. Далее про-цесс продолжается с первой из вершин, записанной в очередь. И так до тех пор, пока очередь не окажется пустой. На ри-сунке приведена часть ребер исходного графа, тех, по кото-р ы м осуществлялся «переход» в процесс просмотра вершин графа при поиске в ширину. В квадратных скобках указана очередность просмотра вершин.

Page 378: Pascal 8 Класс - Окулов

378 Часть четвертая

Program Graph_Breadth ; C o n s t Type Var I"Описание данных совпадает с тем, что приведено в предыдущей программе. *}

Procedure Insert__List__End(Var t : p t ; х : I n t e g e r ) ; {"Процедуры совпадают с приведенными в предыдущей программе. *} Procedure I n i t ; Procedure Inc_Rez (v:Integer); Procedure Print;

("Новая процедура. Поиск в ширину."} Procedure Search_Breadth (v:Integer);{ "v — номер вершины, с которой начинается поиск в ширину.*}

Var t : p t ; w:Integer;

Type ptr="el_turn ; ( "Для реализации очереди используется двусвязный список."}

el_turn=Record data:Integer; next,pred:ptr; End;

Var head,tail:ptr; Procedure W r i t e _ T u r n ( V a r up, down:ptr;x:Integer); ("Действия с элементами очереди рассмотрены на п р е д ы д у щ и х занятиях.*) Procedure Read_Turn (Var up,down:ptr; Var x: Integer); Function Empty_Turn(t,q:ptr):Boolean; Begin(" «Тело» процедуры Search_Breadth. *)

head:=Nil;tail:=Nil; Nnew[v]:=False;Write_Turn(head, t a i l , v ) ;

Page 379: Pascal 8 Класс - Окулов

Динамические структуры дачных 379

{"Записываем в очередь номер вершины графа, с которой начинается просмотр. *) While Not (Empty_Turn(head,tail) ) Do Begin('Пока очередь не пуста, выполняем действия.*} Read_Turn (head,tail,w);("Читаем элемент из очереди. *} t:=A[w]; While tONil Do Begin ("Пока не просмотрены все элементы списка. *}

I f Nnew[t".data] Then Begin("Вершина «не посещалась» ранее.")

Inc__Rez f t " .data) ; Nnew [ t". data ] :=False; Write_Turn(head,tail,t*.data);

End; t:=t л . n e x t ;

End; End;

End; ("Один иэ вариантов основной программы. ") Begin Imt; Inc_Rez (1) ; Search_Breadth (1) ; Print;

End. Изменить программу так , чтобы реализация очереди выпол-

нялась через массив, с о д е р ж а щ и й не более N/2 ячеек . Приве-дите п р и м е р ы графов, для которых данный вариант программы о к а ж е т с я неработоспособным.

3. Топологическая сортировка. Ориентированный граф (или орграф) — это пара G=(V,E), где V - множество вершин, Е — множество ориентированных ребер, которые называются ду-гами. На рисунках дуги отмечаются стрелками. Для оргра-фов м е т о д ы обхода в е р ш и н графа , рассмотренные выше, т а к ж е имеют силу. Предположим, что в орграфе нет циклов (интуитивно этот термин понятен) и он связен. Оказывается, что при этом предположении вершины, а им присвоены зна-ч е н и я меток и они пронумерованы, м о ж н о перенумеровать так , что к а ж д а я дуга будет иметь вид ( v ^ v j , где i<j. В этом суть задачи топологической сортировки. Эта задача, естест-венно, отличается от рассмотренных ранее задач сортировки. В них м ы переупорядочивали элементы массива (в основном), в этой задаче требуется переопределить метки вершин графа.

Суть алгоритма. П р и вводе описания графа, а м ы по-преж-нему работаем с его описанием в виде списков смежности, фор-мируем д л я к а ж д о й вершины (i) характеристику Deg[i] — чис-

Page 380: Pascal 8 Класс - Окулов

380 Часть четвертая

ло дуг, входящих в вершину с данным номером г. Просматриваем вершины и номера вершин г, имеющих нулевое значение Deg[i], запоминаем в стеке. После этого, пока стек не пуст, выбираем первый элемент из стека, присваиваем новый т е к у щ и й номер выбранной вершине и у м е н ь ш а е м степени всех вершин графа, с в я з а н н ы х с данной, на единицу. Если оказалось , что при этом степень в е р ш и н ы стала нулевая , то ее номер заносим в стек. Используется л о г и к а просмотра вершин графа — метод поиска в глубину (а м о ж н о л и использовать поиск в ширину? ) .

Приведем описание данных нашего р е ш е н и я . Program Topol_Sort;

Const MaxN= .; Type pt="elem;{"Тип используется как для формирования описания графа в виде списков связи, так и для организации стека. "}

elem=Record data:Integer;next:pt;End; MyArray=Array[1..MaxN] Of pt;{"Массив указателей для хранения адресов на первые элементы списков, связанных с каждой вершиной графа. *I MyRez=Array[1. . MaxN] Of Integer;

Var A:MyArray; Deg,NewNum:MyRez;{"Степени вершин и новые номера вершин. "} N:Integer;{"Количество вершин графа."}

Рассмотрим пример, он приведен на рисунке, и трассировку логики д л я него (она дана в таблице) по процедуре реализации топологической сортировки.

Procedure ChangeNum; Var head:pt;I"Указатель на начало стека."}

nm,v,i:Integer; t:pt;

Begin head:=Nil;

Page 381: Pascal 8 Класс - Окулов

Динамические структуры дачных 380

пт:=0;{"Текущий новый номер вершины. *} For 1:=1 То N Do

I f Deg[i]=0 Then WriteStack(head,1); ("Первоначальное занесение в стек.*}

While Free (head) Do Begin { *Пока стек не пуст. Процедуры WriteStack, ReadStack и функция Free рассмотрены ранее на занятии по стеку. *} ReadStack(head,v);Inc(nm);NewNum[v]:=nm; {*Присваиваем новый номер вершине, выбранной из стека. *}

t:=А[v]; While tONil Do Begin {"Просматриваем вершины, \ связанные с данной. *) Dec (Deg[t*.data]);('Уменьшаем степень этой вершины на единицу. *} I f Deg[t".data]=0 Then WriteStack(head, t" . data);{*Если оказалось, что степень вершины после этого равна нулю, то заносим ее в стек. *} t:=t". next; { *Переходим к следующей вершине, с в я з а н н о й д у г о й с д а н н о й . * }

End; End;

End;

J Номер итерации Deg NewNum Состояние стека nm

Начальные 2, 2, 2, 1,0, 1 0, 0, 0, 0, 0, 0 5 0

1 2, 2, 1, 0, 0, 1 0,0, 0,0, 1,0 4 1

2 1,2,0, 0,0, 1 0,0, 0,2, 1,0 3 2

3 0, 2, 0, 0, 0, 0 0, 0, 3, 2, 1,0 6, 1 3 4 0, 1, 0, 0, 0, 0 0, 0,3, 2, 1,4 1 4 5 5,0,3,2, 1,4 2 5

j 6 0, 0,0,0,0,0 5,6,3, 2, 1,4 6J

В процедуре Init, рассмотренной ранее, следует добавить формирование элементов массива Deg. Разработайте полный текст решения , проверьте его на различных примерах. Другой способ топологической сортировки состоит в том, чтобы после-довательно находить вершины со степенью 0, запоминать их (или выводить) и удалять из графа вместе со всеми входящими в них дугами. Реализуйте эту модификацию алгоритма.

Page 382: Pascal 8 Класс - Окулов

382 Часть четвертая

Задания для самостоятельной работы

1. Чередующаяся последовательность е ; , и2, е2, ..., е(, и1+1 вер-шин и ребер графа, такая, что e=vlvl+1 (i=l,...,l), называется маршрутом, соединяющим вершины v1 и vl+1. Очевидно, что маршрут можно задать последовательностью u l t и2, ..., vl+1 его вершин. Маршрут называется цепью, если все его ребра раз-личны, и простой цепью, если все его вершины, кроме, воз-можно, крайних, различны. Граф называется связным, если любые две его несовпадающие вершины соединены маршру-том. Рассмотренные выше методы обхода вершин графа ра-ботают только для связных графов. Если граф несвязен, то просмотр вершин выполняется только в пределах одной связ-ной компоненты. Напишите программу определения количе-ства связных компонент графа.

2. Разработайте программы поиска в глубину и поиска в шири-ну (рекурсивный и не рекурсивный варианты) при описания графа с помощью матрицы смежности.

3. Маршрут называется циклическим, если его начальная и ко-нечная вершины совпадают. Циклическая цепь называется циклом, а циклическая простая цепь — простым циклом. Определить, есть ли в связном графе простые циклы нечет-ной длины.

4. Граф G=(V,E) называется двудольным (V=X>JY), если сущест-вует такое разбиение множества его вершин на две части (X и У), что концы каждого ребра принадлежат разным частям. Д. Кёниг сформулировал простой критерий двудольности гра-фа. Для двудольности графа необходимо и достаточно, чтобы он не содержал циклов нечетной длины. При поиске в ширину приписываем вершине, с которой начинаем поиск, номер 0. Всем вершинам, в которые мы попадаем из исходной, припи-сываем номера 1, всем вершинам, еще не пронумерованным, приписываем номер 2, если мы попадаем в них из вершин с но-мерами 1. Запишем в множество X вершины с четными но-мерами, а в множество У — вершины с нечетными номера-ми. Осталось проверить, что вершины, принадлежащие X и У, не смежны. Напишите программу проверки двудольности графа, и если он двудолен, то найдите множества Х и У .

5. В ориентированном графе аналогом понятия цепи является понятие «путь». Если (и,*) дуга, то вершины u u t называют-ся её началом и концом соответственно. Вершина t называ-ется достижимой из вершины и, если существует путь из и в t . В ориентированном графе найти множество всех вершин,

Page 383: Pascal 8 Класс - Окулов

i структуры данных

достижимых из заданной вершины v. Найти для каждой вер-шины графа множество достижимых из нее вершин.

6. Пусть G — связный граф, а и и и — две его несовпадающие вершины. Длина кратчайшего маршрута между и и v (он, ес-тественно, является простой цепью) называется расстоянием между вершинами и и v, обозначим как d(u,v). Поиск в ши-рину дает кратчайшие расстояния от первой вершины (обо-значим как и) до всех остальных вершин. Максимальное зна-чение этих расстояний называют эксцентриситетом вершины и. Максимальное значение среди всех эксцентриситетов вер-ш и н называется диаметром графа d(G). Минимальное из эк-сцентриситетов вершин связного графа называется его ради-усом и обозначается через r(G). Написать программу поиска диаметра и радиуса графа. Найти простые цепи, длины ко-торых совпадают с диаметром и радиусом графа.

7. Вершина и и ребро е называются инцидентными, если v явля-ется концом ребра е. Степенью вершины графа G называется число инцидентных ей ребер и обозначается deg( и). Напиши-те программу проверки «леммы о рукопожатиях»: сумма сте-пеней всех вершин графа — четное число, равное удвоенному числу ребер. П р и м е ч а н и е Проверьте на примерах следствие этой леммы: «в любом графе число вершин нечетной степени четно».

8. Степенью ребра (и,и) назовем неупорядоченную пару (deg(и), deg(v)). Написать программу определения, совпадают ли сте-пени всех ребер заданного графа, и если нет, то можно ли уда-лить из него одну вершину (вместе с инцидентными ребрами) так , чтобы полученный граф обладал этими свойством.

9. Для произвольного графа G его дополнение — граф G' — опре-деляется следующим образом. Любые две несовпадающие вер-шины смежны в G' тогда и только тогда, когда они не смежны в G. Напишите программу проверки утверждения: «для любо-го графа либо он сам, либо его дополнение является связным».

10. Источником в ориентированном графе назовем вершину, от которой достижимы все другие вершины, стоком — верши-ну, достижимую от всех других вершин. Написать програм-му поиска всех источников и стоков данного ориентирован-ного графа.

Page 384: Pascal 8 Класс - Окулов

384 Часть четвертая

Занятие № 30. Двоичные деревья

• основные понятия ; • двоичные деревья, основные операции; • экспериментальная работа с программами реализации де-

рева с помощью массивов, пирамидальной сортировки с помощью двоичного дерева;

• выполнение самостоятельной работы.

Основные понятия. Связный граф без циклов называется де-ревом. Пример дерева приведен на рисунке.

Следующие определения дерева эквивалентны. Граф G=(V, Е), где 1V| =ЛГ и \Е\=М является деревом, если:

• G — связный граф и M=N~1\ • G — ациклический граф и M=N-1\ • любые две несовпадающие вершины графа G соединяет

единственная простая цепь; • G — ациклический граф, обладающий тем свойством, что

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

Определим терминологию, связанную с понятием дерева: • корнем дерева называют единственную вершину, находя-

щуюся вверху «перевернутого» дерева; • самые нижние вершины дерева называют листьями; • вершину называют внутренней, если она не является ни

корнем и ни листом; • о вершине, которая находится непосредственно над дру-

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

Page 385: Pascal 8 Класс - Окулов

Динамические структуры дачных 385

На рисунке вершина с номером 1 — корень; вершины с но-мерами 4, 5, 6, 9, 10, 11 — листья; вершины 2, 3, 7, 8 — внут-ренние; вершина 3 — родитель вершины 7; вершина 8 — пото-мок в е р ш и н ы 3.

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

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

Type pt="node; node=Record data:Integer;("Информационное поле. "} left,right:pt;{"Ссылки на левого и правого п о т о м к о в . * }

End; Var root:pt;{"Указатель на корень дерева."} Основные операции: • вставка элемента в дерево; • удаление элемента из дерева; D обход дерева. Вставка элемента в двоичное дерево поиска реализуется с

помощью следующей простой рекурсивной процедуры. Перво-начальный вызов — Ins_Tree( root,number).

Procedure Ins_Tree (Var t:pt;x: Integer) ; { "x -значение вставляемого элемента.")

Begin I f t=Nil Then Begin

New(t) ; With t " Do Beg in left:=Nil; right:=Ni1; data:=x;

Page 386: Pascal 8 Класс - Окулов

386 Часть четвертая

End; End

Else I f x<=tn.data Then Ins_Tree(t".left ,x) Else Ins__Tree (t" .right, x) ;

End;

Выполните «ручную» трассировку логики процедуры на при-мере в с т а в к и н е с к о л ь к и х элементов в произвольное двоичное дерево п о и с к а .

Р е а л и з а ц и я удаления элемента из дерева чуть сложнее. Если узел имеет одного п о т о м к а , то в поле с с ы л к и родителя удаляе-мого э л е м е н т а з а п и с ы в а е т с я с с ы л к а , не р а в н а я Nil, и на этом все з акончено . В том случае , когда у у д а л я е м о г о элемента два п о т о м к а , д л я с о х р а н е н и я с т р у к т у р ы дерева п о и с к а на место этого э л е м е н т а необходимо записать и л и самый п р а в ы й эле-мент левого поддерева , и л и с а м ы й л е в ы й элемент правого под-дерева . Один из двух вариантов , другого не дано, и выбирается один из н и х . Н а р и с у н к е приведена схема у д а л е н и я элемента д л я первого в а р и а н т а ( самый п р а в ы й элемент левого поддерева удаляемого элемента) .

Вид дерева до удаления Вид дерева после удаления

Procedure Del_Tree (Var t:pt; x:Integer); Var q:pt; Procedure Del(Var w:pt);("Поиск самого правого элемента в дереве. *)

Page 387: Pascal 8 Класс - Окулов

Динамические структуры дачных 386

Begin I f w" . rightONil Then Del (w". right) Else Begin

q:=w;{"Запоминаем адрес для того, чтобы освободить место в «куче». *}

tА.data:=wл.data; l e f t ;

End; End;

Begin I f tONil Then

I f x<t".data Then Del_Tree ( t * . l e f t , x ) Else I f x>f .data Then Del_Tree (t" . right, x)

Else Begin q:=t; I f t " . right=Nil Then t:=t*.left {"Правого поддерева нет.") Else I f t л . left=Nil Then t : = t n . r i g h t {"Левого поддерева нет.")

Else Del(t*.left);{"Находим самый правый элемент в левом поддереве.")

Dispose (q) ; End;

End;

Первоначальный вызов процедуры — Del_Tree(root,number), где number — значение удаляемого элемента.

Д л я вывода значений элементов двоичного дерева необходи-мо выполнить полный обход дерева. При обходе дерева его от-дельные в е р ш и н ы посещаются в определенном порядке. В про-цедуре Print_Tree, а с её помощью осуществляется обход дерева, м о ж н о шестью способами переставить операторы 1, 2, 3. Каж-д ы й способ перестановки даст вывод элементов дерева в опреде-ленной последовательности.

Procedure Print_Tree (t:pt); Begin

I f tONil Then Begin Print_Tree(t".left); {"Первый оператор (1).") Write (t* .data: 3) ; { "Второй оператор (2).") Print_Tree ( f . right) ; { "Третий оператор (3).")

End; End;

Page 388: Pascal 8 Класс - Окулов

387 Часть четвертая

В таблице п е р е ч и с л е н ы все очередности вывода в е р ш и н дерева, приведенного на рисунке , полу-ч а е м ы е с п о м о щ ь ю перестановки операторов в

,С) процедуре Pnnt_Tree.

1 Способ Очередность вывода

1,2,3 ВАС 1,3,2 В С А 2,1,3 ABC 2, 3, 1 АСА 3,1,2 СВА I 3, 2, 1 CAB |

Наиболее интересны п е р в ы х три способа, их называют , соот-ветственно, слева направо , снизу вверх и сверху вниз . Д л я вы-р а ж е н и я (А+В/С)*(D-E*F), представленного в виде дерева (на рисунке) , они дают и н ф и к с н у ю (A+B/C*D-E*F — без скобокЛ постфиксную (ABC/+DEF*-*) и п р е ф и к с н у ю (*+A/BC~D*EF) ф о р м ы записи .

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

Procedure Step_Tree(t:pt;h:Integer);(*h -количество выводимых пробелов, вставляемых перед выводом значения информационного поля узла. *}

Var 1:Integer; Begin

I f toNil Then With t A Do Begin

Page 389: Pascal 8 Класс - Окулов

Динамические структуры дачных 389

Step_Tree (left,h+5) ; For i:=l To h Do Write С V/WriteLn (data : 3) ; Step_Tree (right,h+5) /

Ends-End;

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

Экспериментальный раздел работы

1. Рассмотрим работу с двоичным деревом, если для его пред-ставления используются обычные массивы. Для хранения значений ключей (Key), левых (Left) и правых (Right) ссы-лок выделим свои массивы. Номер ячейки с корнем дерева является значением переменной Root. Для того, чтобы не ис-кать свободную ячейку, адрес первой свободной записывает-ся в HeadFree, а сами ячейки связаны адресами в отдельном массиве Next. Описание данных программы и процедура In.it имеют вид: ($R+} Program Tree__Array; Const NMax=20; Type MyArray=Array[1..NMax] Of Integer; Var Key, L e f t , Right, Next: MyArra у ;

N,Root,HeadFree:Integer; Procedure Imt;

Var i: Integer; Begin

FillChar (Key, SizeOf (Key) ,0) ; FillChar (Left,SizeOf (Left) , 0) ; FillChar (Right, SizeOf (Right) ,0) ; ReadLn(N); For i:=l To N-1 Do Next[i]:=i+l; Next[N]:=0;Root:=0;HeadFree:=1;

End; Для примера, рассмотренного выше по тексту, после фор-

мирования дерева значения элементов массива представлены в таблице. Значение переменной Root равно 1, HeadFree — 12.

Page 390: Pascal 8 Класс - Окулов

390 Часть четвертая

Номер элемента массива Key Left Right Next 1 100 3 2 0 2 120 0 0 0 3 20 4 5 0 4 15 J 0 0 0 5 50 6 7 0 6 30 8 9 0

7 55 0 10 0 g 25 0 0 0 9 35 11 0 0 10 60 0 0 0 11 33 0 0 0 12 0 0 0 13 13 0 0 0 14

0 0 0

I 20 0 0 0 0

Р а с с м о т р и м в с т а в к у э л е м е н т а в д е р е в о п о и с к а .

Procedure Inset_Tree{х: Integer); Var t , q, w: Integer; Begin

I f Root=0 Then Begin {"Вставка первого элемента в дерево - корня. * }

Root:=HeadFree;HeadFree:=Next[HeadFree]; Key[Root]:=x;Next[Root]:=0;

End Else Begin

t:=Root; While t<>0 Do Begin {"Находим место элемента в дереве. *}

q:=t;{"Номер предыдущего элемента. *) I f х<Кеу[t] Then t:=Left[t] Else t:=Right[t];

{"Идем по правой или по левой ссылке. *) End; W:=HeadFree;HeadFree:=Next[HeadFree] ; Next[w]:=0;{"Берем ячейку иэ списка свободных. *) Key [w]:=х; I f x<Key[q] Then Left[q]:=w Else Right[q]:=w; {"Изменяем ссылку у предыдущего элемента.")

End; End;

Page 391: Pascal 8 Класс - Окулов

Динамические структуры дачных 390

С удалением элемента из дерева чуть сложнее (много част-н ы х случаев) . П р е ж д е чем перейти к их разбору, приведем про-стую л о г и к у возврата я ч е й к и с номером t в список свободных. В о з в р а щ а е м а я я ч е й к а становится первой в списке свободных.

Procedure Return(t:Integer} ;{"Возврат ячейки в список свободных. ") Begin I f tRoot Then Begin Left[t]:=0; Right[t]:=0; Key[t]:=0; Next[t]:=HeadFree; HeadFree:=t; End;

End;

Приведем «шаблон» процедуры удаления . Прежде чем рас-сматривать его, попытайтесь «прорисовать» все возможные слу-чаи у д а л е н и я элемента из дерева и выполняемые при этом дей-ствия .

Procedure Del_Tree(х:Integer); Var t,q,w,z:Integer; Begin

t: =Root;q:=t;{"Поиск удаляемого элемента.*} While (t<>0) And (xOKey [t]) Do Begin q:=t;{*Запоминаем адрес предыдущего элемента . *} I f x<Key[t] Then t:=Left[t] Else t:=Right[t];

End; I f t<>0 Then Begin{*Если элемент найден, то выполняем удаление. *}

I f Left[t]=0 Then{"Левого поддерева у удаляемого элемента нет.*}

I f t=Left[qj Then Left[q] —Right[t] {"Удаляемый элемент — левый потомок родителя?"}

Else I f t o Root Then Right[q]:=Right[t]

Else Root:=Right[t] {"Удаляется корень дерева и он не имеет левого потомка."}

Else I f Right[t]=0 Then {"Правого поддерева у удаляемого элемента нет.*)

I f t=Left[q] Then Left[q]:=Left[t] {"Удаляемый элемент - левый потомок родителя?"}

Else I f toRoot Then

Page 392: Pascal 8 Класс - Окулов

391 Часть четвертая

Right[q]:=Left[t] Else Root:=Left[t] {"Удаляется корень дерева, и он не имеет правого потомка. ")

Else Begin {"Удаляемый элемент имеет два потомка. Находим самый правый элемент в левом поддереве. *}

w:=Left[t];z:=t;q:=z;{"Запоминаем сам элемент, его родителя и еще одного родителя (дедушку). Дайте объяснение этому факту.*) While w<>0 Do Begin q:=z;z:=w; w:=Right[w];End;

I f Left[q]= z Then Left[q]:=Left[z] {"Родитель удаляемого элемента является левым потомком своего родителя.")

Else Right[q]:=Left[z]; Key[t]:=Key[z];("Переписываем значение, например 35, на место удаляемого элемента, равного 50 (в примере) . *) t:=z;("B список свободных ячеек заносится самый правый элемент из левого поддерева. Вся информация из этого узла дерева переписана в как бы удаляемый элемент. ")

End; Return (t);("Возвращаем элемент в список свободных. *)

End; End;

Проверьте правильность работы рассмотренных процедур. Все ли случаи учтены? Разработайте динамическую модель ра-боты с двоичным деревом. Вставка и удаление элементов дерева д о л ж н ы выполняться по некоторому случайному закону. При этой работе Вам может оказаться полезной вспомогательная процедура просмотра элементов массивов.

Procedure Print; Var 1 :Integer; Begin

Page 393: Pascal 8 Класс - Окулов

Динамические структуры дачных 392

F o r г : =1 То N Do Write (1: 4) ;Writeln; For i:=l To N Do Write (Key [l ] : 4) ;Writeln; For i:=l To N Do Write(Left[i]:4);Writeln; For i:=l To N Do Write (Right [ I ] : 4) ; Writeln ; For i:-l To N Do Write(Next[l]:4);Writeln; WriteLn (Root,' ',HeaDFree);

End; 2. Вернемся к теме пирамидальной сортировки (занятие 20).

Данные во входном файле представлены в формате: N — чис-ло элементов (в первой строке), а затем со второй строки сами данные, естественно, не отсортированные. Например:

7 8 10 3 13 9 5 12

Н а ш е й первой задачей является чтение данных из файла и представление их в виде правильного двоичного дерева (макси-мальное количество узлов дерева имеет два потомка). Затем это дерево необходимо преобразовать в пирамиду, так к а к это пока-зано на рисунке . Элементы дерева образуют пирамиду, если значение информационного поля в каждом узле дерева больше значений у узлов потомков. Очевидно, что после этого преобра-зования в корне дерева записан минимальный элемент. Запи-сываем его в результирующий файл, а на его место записываем число, заведомо большее (а после первого шага и равное) всех значений в узлах дерева. После этого «проталкиваем» верхний элемент вниз по дереву. В корне дерева окажется вновь следую-щ и й м и н и м а л ь н ы й элемент. Записываем и заменяем его на максимально возможное значение. Процесс продолжается N раз.

После первого «проталкивания» дерево имеет вид, показан-ный на следующем рисунке (до и после «проталкивания»).

15—452

Page 394: Pascal 8 Класс - Окулов

393 Часть четвертая

Program Tree_Pyramid; Type pt="elem;

elem=Record data:Integer; l e f t , r i g h t : p t ; End; Var head:pt;("Указатель на корень дерева.*)

N:Integer; f:Text;

Begin Assign (Input,'Input.Txt')/Beset (Input); {"Стандартные действия no открытию рабочих файлов программы. *) A s s i g n ( O u t p u t , ' O u t p u t . T x t ' ) ; R e w r i t e ( O u t p u t ) ; Assign ( f , ' R e s u l t . T x t ' ) ; R e w r i t e ( f ) ; ReadLn (N);head:=Nil;("Значение N требуется для построения «правильного» дерева.") head:=Тгее(N);{"Функция Tree обеспечивает построение «правильного» дерева.") Step_Tree (head,0);{"Вспомогательная процедура вывода дерева. Рассмотрена ранее. Можно исключить, она не играет принципиальной роли в данном решении.") Change(head);{"Строим пирамиду."} Step_Tree(head,0); Solve(1);("Записывает значение из корня дерева в результирующий файл и проталкивает максимальное значение вниз по дереву. Параметр процедуры указывает на номер проталкивания. *} Close(Input);Close(Output);Close ( f ) ;

End. Рассмотрим функцию построения «правильного» дерева. Она

возвращает в основную программу указатель на корень дерева и я в л я е т с я , естественно, рекурсивной . Параметр функции (t) определяет количество элементов, из которых строится дерево.

Page 395: Pascal 8 Класс - Окулов

Динамические структуры дачных 395

Создается новый узел, а затем рекурсивно строятся левое и пра-вое поддеревья с количеством узлов в два раза меньшим, чем значение t (для правого поддерева уменьшаем количество узлов на единицу — минус, строящийся узел).

Function Tree (t: Integer) :pt; Var x: Integer ;

w:pt; Begin

I f t=0 Then Tree:=Nil{"Нулевое количество узлов определяет ссылку типа Nil. "} Else Begin

Read(x) ; l "Читаем число из входного файла. *) New(w);("Выделяем место в «куче» для нового узла дерева.") With w' Do Begin

data:=х; left:=Tree(t Div 2) ; {"Формируем значения левой и правой ссылок узла.")

right: =Tree (t-t Div 2 -1) ; End;

Tree:=w; Ends-

End; При построении пирамиды необходимо поменять значения в

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

P r o c e d u r e Swap(Var х,у:Integer);{"Вспомогательная процедура. *}

Var z : I n t e g e r ; B e g i n

I f x>y Then Begin z . - = x ; x : = y ; y : = z ; E n d ; E n d ;

P r o c e d u r e Change f t : p t ) ; B e g i n

I f t o N i J Then Begin Change ( f . l e f t ) ; { "Идем в левое поддерево. "} Change ft".right;;("Затем в правое поддерево.") I f t" . leftONil Then Swap

(t".data,t*.left*.data); ("Записываем минимальное значение в

Page 396: Pascal 8 Класс - Окулов

395 Часть четвертая

корень поддерева.*} I f t" . rightoNil Then Swap

(t".data, t".right".data) ; End;

End;

Основная процедура п и ш е т с я по т р а д и ц и о н н о м у п р и н ц и п у « к а к говорим, так и п р о г р а м м и р у е м » .

Procedure Solve (k:Integer); Begin

I f k<=N Then Begin {*Пока данные не отсортированы, выполняем . . .*)

Write(f,head".data,' ' ) ;{*3аписываем значение из корня дерева в результирующий файл. *)

head".data:=MaxInt;(* Присваиваем значение 32767. *)

Push (head);{*Проталкиваем значение из корня дерева вниз по дереву. *) Step_Tree (head,0);(*Вспомогательная процедура. Можно исключить. *} Solve(k+1);(*Переходим к обработке следующего элемента. *)

End; End;

И наконец , процедура «проталкивания» элемента по дереву.

Procedure Push (t:pt); Var v,w:pt;(*Вспомогательные переменные. Введены для того, чтобы текст процедуры помещался на экране, был обозримым. Вспомним о структурном программировании. *) Begin

I f t o Nil Then Begin v:=t".left;w:=t".right;(*Запоминаем адреса корневых элементов левого и правого поддеревьев. *) I f (vONil) And (wONil) Then {*Если оба поддерева есть, то выбираем поддерево с наименьшим значением в корне.*)

I f v".data<w".data Then Begin Swap (t".data,v" .data);Push(v);End Else Begin Swap(t".data,w".data);Push(w);End

{*Обработка случаев, когда узел имеет одно

Page 397: Pascal 8 Класс - Окулов

Динамические структуры дачных 397

поддерево или является листом. *} Else I f vONil Then Begin Swap (t" . data, v". data) ; P u s h (v) ;End

Else I f wONil Then Begin Swap (t* . data, w".data) ;Push (w) ;End;

End; End;

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

Задания для самостоятельной работы

1. Разработать логическую функцию: • проверки н а л и ч и я заданного элемента в дереве; • подсчета суммы элементов дерева; • определения наибольшего (наименьшего) элемента в де-

реве; • подсчёта количества элементов в дереве; • сравнения двух деревьев Tj и Т2; • определения максимальной высоты дерева; • проверки н а л и ч и я в дереве хотя бы двух одинаковых

элементов. 2. Д а н текст (в текстовом файле) . Определить частоту вхожде-

н и я каждого из слов в текст. П р и м е ч а н и е Тип элемента в узле дерева String, кроме того, необходимо ввести счетчик числа повторений каждого слова.

3. В задаче 2 определить количество вершин дерева, содержа-щ и х слова: • н а ч и н а ю щ и е с я на одну и ту же букву; • я в л я ю щ и е с я палиндромами; • содержащие все гласные буквы латинского алфавита.

4. Написать процедуру удаления из созданного двоичного дере-ва поиска всех отрицательных элементов.

5. Разработать функции, возвращающие значение указателя на второй минимальный (максимальный) элемент в двоичном де-реве поиска .

6. Двоичное дерево считается идеально сбалансированным, если для каждой его вершины количество вершин в левом и правом

Page 398: Pascal 8 Класс - Окулов

397 Часть четвертая

поддеревьях различается не более чем на 1. Написать функцию проверки идеальной сбалансированности двоичного дерева.

7. В описании вершины двоичного дерева поиска добавьте два поля типа Integer для фиксации высот (глубин) левого и пра-вого поддеревьев. Разработайте процедуры (вставки, удале-ния элемента) работы с таким деревом. Измените два поля на одно, в котором фиксируется разность высот правого и лево-го поддеревьев. Проведите модификацию программы для это-го случая.

8. В файле даны N целых чисел в двоичной системе счисления (М бит каждое) . Построить двоичное дерево, в котором чис-лам соответствуют листья дерева, а путь по дереву определя-ет значение информационного поля этого листа.

9. В текстовом файле записаны целые числа. Построить двоич-ное дерево, элементами которого являются числа из файла. Написать процедуру: • определяющую количество нечетных чисел: • вычисляющую сумму элементов дерева, кратных 3; • определяющую число вершин дерева на каждом уровне; • число вершин в правом и левом поддеревьях:

10. Построить двоичное дерево из букв строки и написать проце-д у р у :

• определения количества повторяющихся букв в дереве; • определения, каких букв больше — гласных или соглас-

ных; • вывода самого правого элемента левого поддерева; • вывода самого левого элемента правого поддерева.

Page 399: Pascal 8 Класс - Окулов

Динамические структуры дачных 399

Занятие № 31. Сбалансированные деревья

• основные понятия; • операции по восстановлению балансировки при вставке и

удалении элемента в АШГ-дерево; • Б-деревья, вставка элемента; • выполнение самостоятельной работы. Основные понятия. Двоичное дерево считается идеально

сбалансированным, если для каждой его вершины количество вершин в левом и правом поддеревьях различается не более чем на 1. Для одних и тех же данных, например целых чисел от 1 до N (в зависимости от порядка их поступления на обработку), структура двоичного дерева поиска будет различной. Возмож-ны варианты как от идеально сбалансированного дерева, так и простого линейного списка, когда или все левые, или все пра-вые ссылки узлов равны Nil. Реализация операций восстанов-ления идеальной сбалансированности при случайной вставке или удалении элемента из двоичного дерева поиска, разумеет-ся, возможна, однако она не рассматривается в учебной литера-туре из-за ее сложности.

Изменим определение сбалансированности. Эта трактовка понятия сбалансированности предложена Г. М. Адельсоном-Ве-льским и Е. М. Ландисом в 1962 году.

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

Деревья, удовлетворяющие этому условию, называют ABJI-деревьями. Введем в описание каждого узла поле bal, значение которого определяет разность высот его правого и левого подде-ревьев. На рисунке в узлах указано значение bal. Дерево слева не удовлетворяет условию АБЛ-сбалансированности, справа — удовлетворяет. Ни то, ни другое дерево не удовлетворяет усло-вию идеальной сбалансированности.

Page 400: Pascal 8 Класс - Окулов

400 Часть четвертая

Рассмотрим операции, выполняемые по восстановлению ба-лансировки АВЛ-дерева. Каждый тип изменения рассмотрим на определенном наборе исходных данных. Так проще. Отсле-живание изменений на случайных файлах, когда выполняются различные типы балансировок, затруднительно.

Пусть входной файл имеет вид: 8 6 1 0 5 7 9 1 3 3 1 . До вставки 1 балансировка дерева не нарушалась, ибо элементы вставлялись в той очередности, в которой они записаны во входном файле. Мы «находимся» в левом поддереве, и увеличивается (по высо-те) левое поддерево. Выполняем «малый правый поворот» для восстановления балансировки дерева. Его суть показана на сле-дующем рисунке.

6 га [3]

в 6 га [3] -1

У I »

После действий, выделенных жирными линиями, в той по-следовательности, которая указана в квадратных скобках, из левого фрагмента дерева получается правый, из не сбалансиро-ванного фрагмента — сбалансированный. Значения указателей

Page 401: Pascal 8 Класс - Окулов

Динамические структуры дачных 401

t vi v определены до выполнения описанных действий, и их из-м е н е н и я передаются обратно в вызывающую логику. Первый вариант программного кода малого правого поворота имеет вид:

Procedure Turn_right_small(Var t,v:pt); Begin

tЛ. left:=r".right;I *Первое действие. *) v".right:=t;('Второе действие. ' ) t".bal:=0/ t:=v;('Третье действие. *)

End; Аналогичный тип поворота может возникнуть и при удале-

нии элемента дерева. Пусть у нас есть фрагмент дерева, изобра-ж е н н ы й на следующем рисунке (левая часть), и удаляется эле-мент 7. Измененное дерево приведено на правой части рисунка. Используется м а л ы й правый поворот. Отличие в том, что изме-няется логика работы с полем bal. Проверьте ее на следующем фрагменте программного кода.

Procedure Turn_right_small (Var t,v:pt;Var h: Boolean) ;

Begin WriteLn ( 'Right small ';t".key); t".left:=v".right;{'Первое действие. *} v". right:-t;{'Второе действие.') I f v".bal=0 Then Begin t * . b a l : = - 1 . b a l : = 1 ; h:=False; End

Else Begin t A . b a l : = 0 ; v " . b a l ; = 0 ; E n d ; t:=v;{'Третье действие.'}

End;

Page 402: Pascal 8 Класс - Окулов

402 Часть четвертая

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

Первый вариант малого левого поворота осознаем на дан-ных: 8 6 1 0 5 7 9 1 3 1 5 1 7 . После вставки элемента 17 требуется балансировка. Выполняем очевидные действия, показанные на следующем рисунке.

Page 403: Pascal 8 Класс - Окулов

Динамические структуры дачных 402

И первый вариант программного кода, реализующего малый левый поворот:

Procedure Turn_left_small(Var t,v:pt); Begin

t".right:=v".left;{*Первое действие. ' } v".left:=t;{*Второе действие. *} t".bal:=0; t:=v;{'Третье действие. *}

End; А н а л о г и ч н а я ситуация возникает и при удалении элементов

из дерева. Пример . Пусть удаляется элемент 12 из дерева, изоб-раженного на левой части рисунка . Нарушается балансировка. Выполняем м а л ы й левый поворот. Вид дерева после поворота на правой части рисунка . Если работа с полями ссылок совпа-дает с разобранными выше, то изменение поля bal осуществля-ется чуть иначе .

P r o c e d u r e T u r n _ l e f t _ s m a l l (Var t,v:pt;Var h:Boolean) ; Begin

WriteLn ( 'Left small \ t ".key); t" . right:=v*.left;(*Первое действие. *) v". left:=t;{*Второе действие. *} I f v".bal = 0 Then Begin t " . b a l : = 1 ; v * . b a l : = - 1 ; h:=False;End

Else Begin t " . b a l : = 0 , т " . b a l : = 0 ; E n d ; t:=v;('Третье действие.'}

End;

Это не единственные случаи нарушения балансировки дере-ва. Их еще два: «большой правый» и «большой левый» поворо-ты. Изменим входной файл: 10 6 14 5 8 12 16 2 4. После встав-к и элемента 4 балансировка дерева нарушается. Оно имеет вид, показанный на очередном рисунке.

Page 404: Pascal 8 Класс - Окулов

М а л ы й п р а в ы й поворот п р и м е н и т ь н е л ь з я — нарушается п р и н ц и п дерева поиска . В ы п о л н я е м ы е действия по восстанов-лению балансировки п о к а з а н ы на следующем рисунке .

смысленны. Однако надо иметь в виду тот факт , что рассматри-ваемые т и п ы поворотов в ы п о л н я ю т с я над любым фрагментом дерева, в котором нарушена балансировка . Узел со значением 4 в общем случае может иметь потомков.

Procedure Turn_right_big (Var t,v:pt); Var w:pt;

Begin WriteLn ('Right big ',t".key); w:=v".right;

Page 405: Pascal 8 Класс - Окулов

Динамические структуры дачных 405

v* . right:=ы".left;{*Первое действие. *} к" . left:=v;{*Второе действие. *} t" . left:=w".right;{'Третье действие. *} w" . right:=t;{'Четвертое действие. *} I f w".bal=-l Then f.bal:=l Else t".bal:=0; {'Приведите примеры для каждого случая изменения значения поля bal. *} I f w".bal=l Then v".bal:=-l Else v".bal:=0; t:=w;{*Пятое действие. *} w*.bal:=0;

End; Если работа с п о л я м и ссылок достаточно очевидна, то о дей-

ствиях с полем bal этого не с к а ж е ш ь . В процедуре приведен код, повторяющий логику действий с полем bal в тех книгах , где рассматриваются АВЛ'-деревья в полном объеме, их очень не много. Это книги Н. Вирта (Алгоритмы + Структуры данных = Программы. М.: Мир, 1985) и С. Д. Кондратьевой (Введение в структуры данных. М.: Издательство Ml ТУ им. Н. Э. Баумана, 2000). Вспомним наше «рабочее» определение программы — «а может быть, это не так, а может быть, это не верно»? Может быть, верен следующий программный код:

I f ы". bal=-l Then v".bal:=l Else v".bal:=0; I f w".bal=l Then f.bal:=-l Else t".bal:=0; Нахождение истины — вопрос отладки программы. Требует-

ся найти те возможные входные данные, создать такую ситуа-цию в работе программы, при которой можно говорить об ис-тинности того и л и другого варианта программного кода. А это у ж е «высший класс (пилотаж)» в освоении материала.

Последний тип поворота рассмотрим на следующих данных: 10 6 14 5 8 12 16 20 18. После вставки 18 нарушается баланси-

Page 406: Pascal 8 Класс - Окулов

406 Часть четвертая

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

Procedure Turn_left_big(Var t , v : p t ) ; Var w: pt; Begin

Wri teLn ( 1 Left big \ t " . k e y ) ; w : . l e f t ; vA.left:=w".right;{*Первое действие. *} wn.right:=v;{*Второе действие. *} tright:=w".left;(*Третье действие. *} w".left:=t;("Четвертое действие. *} I f ыл.Ьа1=-1 Then t".bal:=l Else t".bal:=0; I f ыл.Ьа1=1 Then v".bal:=-l Else v".bal:=0; t:=w;l"Пятое действие. *} w".bal:=0;

End;

О т к р ы т ы й вопрос — работа с полем bal. П р е д л о ж и м другой вариант из к н и г и Н . В и р т а [], выбор верного — Ваша задача.

I f .bal=-l Then v".bal:=l Else v".bal:=0; I f w".bal=l Then f.bal:=-l Else t".bal:=0;

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

Page 407: Pascal 8 Класс - Окулов

Динамические структуры дачных 407

(вставка, удаление элементов). Общая логика процедур включе-ния , исключения элементов аналогичны соответствующим про-цедурам работы с обычным двоичным деревом поиска за иск-лючением одной детали. В логической переменной Л (значение True) фиксируется факт вставки (удаления) элемента в (из) де-рево (а), и это значение передается в вызывающую логику. Оно, а т а к ж е значение поля bal узла дерева служат основанием д л я п р и н я т и я р е ш е н и я о том, требуется ли балансировка относите-льно рассматриваемого узла. Так к а к процедуры поворотов об-щие д л я режимов вставки и удаления элементов, то вводится еще одна дополнительная логическая переменная. Далее с не-значительными изменениями (они выделены «жирным» курси-вом) повторим процедуры работы с обычным двоичным деревом поиска .

Procedure Search (х:Integer; Var t:pt; Var h:boolean); Begin

I f t=Nil Then Begin New(t) ; t".key:=x;t'.bal:=0; t".left:=Nil; t".right:=Nil; h:=true;{*Элемент вставлен. *}

End Else I f x<t".key Then Begin I*Идем в л е в о е п о д д е р е в о . * )

Search (х, tЛ . l e f t , h) ; Bal_left(t,h,False);{*Проверяем необходимость балансировки при вставке элемента в левое поддерево. Логическая константа False говорит о том, что обращение идет в режиме вставки

End Else I f x>t".key Then Begin{*Идем в правое поддерево. *}

Search (х, t" . right,h) ; Bal_right (t,h,False) ; { *Проверяем необходимость балансировки правого поддерева. Логическая константа False говорит о том, что обращение идет в режиме вставки элемента. *)

End

Page 408: Pascal 8 Класс - Окулов

408 Часть четвертая

Else h:=False;("Элемент (ключ) повторяется. Проверка нарушения балансировки не требуется. "}

End;

Процедура у д а л е н и я . Procedure Del^tree(х:integer; Var t:pt; Var h:Boolean);

Var v:p t; Procedure Del_el(Var w:pt); Begin

I f w" . rightONil Then Begin Del_el (w".right) ; Bal_left(w,h,True);("Выполняем после удаления балансировку левого поддерева. Константа True говорит о том, что балансировка происходит в режиме удаления.")

End Else Begin

vЛ.key:=w".key; v: =w ; w:=w".left; h :=truer-

End,: End;

Begin i f t=Nil then WriteLn ( 'There is no such element here')

else i f x<t".key then begin Del_tree(x,t".left,h) ; Bal_right(t,h,True);{"Удаляем из левого поддерева, выполняем правую балансировку. В этом отличие от режима вставки. Ключевой момент для осознания работы с АВЛ-деревьями. *)

end else i f x>t".key then begin

Del_tree (x,t".right,h); Bal_left(t,h,True);{"Удаляем из правого поддерева. Делаем левую балансировку.")

end else begin

Page 409: Pascal 8 Класс - Окулов

Динамические структуры дачных 408

v : = t ; i f v*. right=Nil then begin

t:=v".left; h:=true; end else i f v". left=Nil then begin

t:=v".right; h:=true; end else begin Del_el ( v ^ . l e f t ) ; Bal_right (t,h, True) ;

end; end;

End; П р и м е ч а н и е В процедуре нет оператора Dispose. Найдите возможность его «без-болезненной» вставки. Логика почти детализирована. Есть процедуры поворотов

(это проделано как бы по технологии «снизу — вверх»), есть об-щ а я часть (это проделано как бы по технологии «сверху — вниз») . Остался ключевой фрагмент, связывающий и то, и дру-гое — процедуры Bal_right и BalJLeft. Но и он детализирован частично, ибо строго определены как входные, так и выходные параметры этих процедур.

Procedure Bal_left(Var t:pt; Var h:Boolean; Del:Boolean);

Var v:pt; Begin

I f h Then Case t".bal Of {*Необходимость балансировки анализируется относительно узла t" .

1 :Begin I f Not Del Then h:=false; (*B режиме вставки элемента правое поддерево имело большую высоту. Левое поддерево увеличилось на единицу. Балансировка на требуется. *} t".bal:=0;

End; О : Begin

I f Del Then h: =False;{ "Удалили элемент из правого поддерева. Балансировка не требуется. Критерий АВЛ-дерева сохраняется. *}

Page 410: Pascal 8 Класс - Окулов

410 Часть четвертая

t" . bal:=-1;{"Высота левого поддерева увеличилась. *}

End; -1 .-Begin ( "Мы в левом поддереве {в режиме вставки) и увеличилась высота левого поддерева. Требуется балансировка. Аналогичная ситуация возникает при удалении элемента из правого поддерева — высота левого поддерева увеличивается."}

v:=t".left;("Левый потомок рассматриваемого узла. *} I f (v".bal=-l) Or ((v".bal=0) And Del) Then Turn_right_small (t,v,h) Else Turn_right_big(t,v);{"Если высота левого поддерева левого потомка была больше высоты правого поддерева левого потомка, то требуется малый правый поворот, иначе — большой правый поворот. Малый правый поворот в режиме удаления требуется и в том случае, если левый потомок узла t л имеет поддеревья равной в ы с о т ы . " }

End; End;

End;

По аналогии п и ш е т с я и процедура восстановления баланси-ровки правого поддерева.

Procedure Bal_right (Var t:pt; Var h:Boolean; Del:Boolean);

Var v:pt; Begin

I f h Then Case tn.bal Of

-1:Begin I f Del Then h:=false;

t".bal:=0; End;

0: Begin I f Del Then h:=False; t".bal:=1;

End; 1:Begin

v:=t".right;("Правый потомок узла.")

Page 411: Pascal 8 Класс - Окулов

Динамические структуры дачных 410

I f (v*.bal=l) Or ((v*. bal=0) And Del) Then Turn_left_small(t,v,h)(*Если высота правого поддерева правого потомка была больше высоты левого поддерева правого потомка, то требуется малый левый поворот, иначе — большой левой поворот. В режиме удаления аналогичные действия выполняются и при равных высотах дерева. *) Else Turn_left_big(t, v) ;

End; End;

End;

Д л я справки приведем оставшуюся часть программного кода, связанного с организацией ввода исходных данных и вывода результатов манипуляций с ABJI-деревом.

Procedure Imt (Var t:pt); Var x:Integer; h:Boolean;

Begin Assign (Output, ' avl_ans. t x t ' ) ;ReWrite (Output) ; l'Результат обработки выводится в файл. Это удобнее для анализа, чем вывод на экран, ибо есть предыстория изменения структуры дерева. *} ClrScr; Assign (Input, ' 1 . t x t ' ) ; ReSet (Input) ; While Not EoLn Do Begin Read (x) ; Search (x,t,h) ; WriteLn; WriteLn ('After input: ' ,x) ; PrintTree (t,0) ;

End; Close (Input) ;

End; Удаляемые элементы дерева берутся из другого файла. Procedure Imt_Del (Var t:pt);

Var h:Boolean; x:Integer; Begin

Assign(Input,'1 del.txt');ReSetflnputj; While Not Eof Do Begin Read (x) ; Del_ Tree(x,t,h); WriteLn; WriteLn (' After delete: ' ,x) ;

Page 412: Pascal 8 Класс - Окулов

412 Часть четвертая

PrmtTree ( t , 0) ; End; Close (Input); Close(Output);

End;

И н а к о н е ц , о с н о в н а я п р о г р а м м а . Begin head:=Nil; Init (head) ; P r m t T r e e (head, 0) ; Ini t_Del (head) ;End.

В о з н и к а е т в п о л н е естественный вопрос, а не у п р о с т и т с я л и л о г и к а , если в к а ж д о м у з л е ввести адрес с в я з и к родителю? Ис-следуйте его. Н а п и ш и т е в а р и а н т п р о г р а м м ы д л я этого с л у ч а я .

Д р у г и м т и п о м с б а л а н с и р о в а н н ы х деревьев я в л я ю т с я Б-дере• въя (B-tree). Они п р е д л о ж е н ы Р . Б э й е р о м в 1972 году и относят-ся к к л а с с у с и л ь н о в е т в я щ и х с я деревьев . О т л и ч и е з а к л ю ч а е т с я в том, что в у з л е х р а н и т с я не одно з н а ч е н и е к л ю ч а , а н е с к о л ь к о и, соответственно, это к о л и ч е с т в о к л ю ч е й о п р е д е л я е т количест-во п о т о м к о м у з л а . П р и этом узел у ж е н а з ы в а е т с я страницей . Б-дерево о п р е д е л я е т с я х а р а к т е р и с т и к о й , н а з ы в а е м о й п о р я д к о м и обозначаемой N (Б-дерево п о р я д к а Лт).

Определение Б-дерева: • к а ж д а я с т р а н и ц а с о д е р ж и т не более 2*N элементов (клю-

чей) ; • к а ж д а я с т р а н и ц а , к р о м е к о р н е в о й , с о д е р ж и т не менее N

элементов ; • к а ж д а я с т р а н и ц а я в л я е т с я либо листом , то есть не имеет

потомков , л и б о и м е е т т+1 п о т о м к о в , где т — число нахо-д я щ и х с я в ней к л ю ч е й ;

• все л и с т ь я н а х о д я т с я н а одном уровне .

Page 413: Pascal 8 Класс - Окулов

Динамические структуры дачных 413

Д л я входных данных: 45, 55, 65, 75, 85, 10, 20, 70, 90, 100, 68, 80, 95, 105, 110, 72, 15 (на обработку поступают именно в такой очередности) Б-дерево порядка 2 имеет вид, представлен-н ы й на рисунке . По своему виду это дерево поиска, только бо-лее сложное по структуре узла.

Описание узла (страницы), содержащей т . ключей, должно иметь место к а к д л я хранения значений ключей, так и т+1 ссылок на потомков. Все это «навязывает» следующие типы данных программного решения задачи работы с -Б-деревом (пока рассматривается только обход и вставка элемента в 5-дерево).

P r o g r a m В _ Т г е е ; Const N=2;("Порядок дерева. "}

Typept="page;("Указатель на узел (страницу).*! index=0. .NMax; item=Record key:Integer; p:pt; count:Integer ; End;l'Значение ключа; указатель на правого потомка, если он есть; Счетчик числа повторений ключа (предполагаем, что в файле с исходными данными есть повторяющиеся ключи). *} page=Record(*0писание страницы. *}

m:index;("Количество ключей."} рО :pt;("Указатель на самого левого потомка."} A:Array[1..NMax] Of item;{"Массив, каждому ключу страницы соответствует элемент массива. *}

End; Var root:pt;("Единственная (явная, если так можно выразиться) переменная нашей программы — указатель на корень дерева."}

Рассмотрим вставку нового элемента в Б-дерево. Два случая из вышеприведенного примера.

NMax=2"N;("Максимальное количество ключей."}

85 65

45, 55, 65, 75

45, 55 75, 85

Вставка элемента 85 приводит к расщеплению одного узла — к о р н я на два и к образованию нового узла (корня). Значение

Page 414: Pascal 8 Класс - Окулов

414 Часть четвертая

к л ю ч а в н о в о м у з л е р а в н о с р е д н е м у и з з н а ч е н и й к л ю ч е й рас-щ е п л я е м о г о у з л а .

В с т а в к а э л е м е н т а 100 п р и в о д и т к р а с щ е п л е н и ю узла и до-б а в л е н и ю среднего э л е м е н т а в у з е л р о д и т е л я .

Д в о и ч н ы й п о и с к э л е м е н т а в у п о р я д о ч е н н о м м а с с и в е ц е л ы х ч и с е л (поле key з а п и с е й т и п а item) о с у щ е с т в л я е т с я к а к обыч-но. Он о ф о р м л е н в в и д е ф у н к ц и и , з н а ч е н и е к о т о р о й определяет р е з у л ь т а т п о и с к а . Е с л и э л е м е н т х н а й д е н , то з н а ч е н и е ф у н к ц и и у к а з ы в а е т на то место в м а с с и в е з а п и с е й , где он н а х о д и т с я .

Function Find (t:pt;x:Integer):index;{*t — указатель на узел, в массиве записей которого осуществляется поиск. *)

Var i,j,к:Integer; Begin

i : =1; j:=tЛ.m; While i<j Do Begin

к : = (i +j+l) Div 2; I f x<t".A[k].key Then j:=k-l Else I f x>t".A[k].key Then i:=k+l

Else Begin i:=k; j:=k;End; End; I f i>t".m Then Find:=t".m Else I f i<l Then Find:=0

Else I f t".A[i] . key>x Then Find:=i-1 Else Find:=i;

End; С а м ы й простой т и п в с т а в к и — это добавление элемента в су-

щ е с т в у ю щ и й узел , к о г д а к о л и ч е с т в о э л е м е н т о в в последнем не п р е в ы ш а е т м а к с и м а л ь н о г о з н а ч е н и я Nmax. В х о д н ы м и пара-м е т р а м и л о г и к и я в л я ю т с я :

Page 415: Pascal 8 Класс - Окулов

Динамические структуры дачных 414

• t — указатель на узел, в который вставляется элемент; • г+1 — место в массиве записей узла , на которое вставля-

ется элемент; • и — значение элемента. Результатом работы я в л я е т с я не только изменение узла Г ,

но и изменение значения переменной h (вспомните АВЛ-дере въя) — структура дерева (как нечто целого) сохранилась, изме-нение коснулось только одного узла.

P r o c e d u r e Ins_Simple (t :pt; Var h .-Boolean ;u : item ; r:Index);

Var 1: Integers-Begin

With t " Do Begin m:=m+l;{"Изменяем количество элементов в узле. *) For i:=m DownTo r+2 Do A[1]:=A[l-l];{"Сдвигаем элементы в массиве записей. *} A[r+1]:=u;{"Вставляем элемент."}

End; h:=False;{"Структура дерева не изменяется.")

End; А чем отличается поиск по В-дереву от поиска по бинарному

дереву? В принципе только деталями перехода к узлам нижне-го уровня , к потомкам и тем, что при выходе из поиска, то есть при возврате к родителям, передается не только значение пере-менной Л, но и запись и, вставляемая в один из узлов предков. Действительно, если родителю передана запись для вставки от потомка, а сам родитель имеет максимальное количество клю-чей, то формируется новая запись, которая передается дальше вверх по дереву. И так до корня, если это необходимо.

Procedure Search(t:pt; х:Integer; Var h:Boolean; Var и .-item) ;

Var i:Integer; v:item;

Begin I f t=Nil Then Begin{"ДОШЛИ ДО листа."}

h:=True; With u Do Begin {"Формируем запись. "} key:=x ; count:=l; p:=Nil;

Ends-End

Else Begin

i :=Fmd(t,x) ;

Page 416: Pascal 8 Класс - Окулов

416 Часть четвертая

I f tл.A[l].кеу=х Then Begin{"Находим ключ х в узле t". Он найден, есть совпадение. Данные повторные.*}

Incft".A[г].count);{*Изменяем значение счетчика числа повторений. "} h:=False;{"Структура дерева не изменяется.*)

End Else Begin{"Элемент х не найден. Продолжаем поиск. Значение переменной i определяет адрес потомка. *}

I f i<l Then Search(t".pO,x,h,v) Else Search (t".A[1].p,x,h,v); {*Продолжение поиска.*) {*3начение переменной h определяет необходимость вставки элемента в дерево. *)

I f h Then I f t".m<Nmax Then Ins_Simple ( t , r , h , v , l ) {* Простой случай вставки

элемента в узел. *) Else Ins_Div(t,h,u,v,1);{*Сложный случай вставки, требующий деления узла, его разбивки.*)

End; End;

П р о д о л ж и м р а с с м о т р е н и е . Л о г и к а с л о ж н о г о с л у ч а я вставки элемента в дерево:

Procedure Ins_Div(t:pt; Var h:Boolean ;Var u:item; v:item; i:Integer;);

Var w.pt; J: index;

Begin New(w);{"Создается новый узел. *} I f K=N Then Begin

I f i=N Then u:=v Else Div_Left(t,u,v,i) {"Содержимое нового узла формируется из левой части ключей и ссылок узла Г . "} For j:=l То N Do w".A[j] :=t".A[j+N] ; End

Else Div_Right (t,w,u,v, i ) ; {"Содержимое нового узла формируется из правой части ключей и ссылок узла t"."}

t*.m:=N;{"Изменяем количество ключей в узлах t " И W. "}

Page 417: Pascal 8 Класс - Окулов

Динамические структуры дачных 417

w*-. m:=N; W.pO:=u.p; u,p:=w;{*Запись u - передается вверх по дереву вплоть до корня. *}

End; П р и м е ч а н и е Если этот фрагмент логики не очень понятен при первом чтении, то сделайте шаг вперед — рассмотрите процедуры Dw_Left и Div_ Right, а затем вернитесь обратно.

Procedure Div_Right f t , w:pt;Var u:item;v:item;1: index);

Var j:Integer; Begin

l:=1-N;{*Ключ x принадлежал правой части массива ключей узла t". Работаем только с зтой частью. *)

u : = t " . A [ N + 1 ] ; { ' З а п о м и н а е м з а п и с ь на месте N+1, ибо она пойдет дальше вверх по дереву. *) For j:=l То 1-1 Do w".A[j] :=t*.A[j+N+l] ; {"Переписываем эту часть узла t " в новый узел w". На месте i должна быть запись и, полученная этой процедурой. *} w".A[l]:=v; For у: =1+1 То N Do w". A [ j ] : =tЛ. Л [ j +N] ; I"Дописываем оставшуюся часть. *}

End; Следующая процедура была бы чуть сложнее, если логика об-

работки элемента (вставляемого) на границе не была «вынесена» в процедуру Ins Div. В этом случае элемент уже является значе-нием ключа записи и. Необходимо в переписать N записей из Г и на этом закончить обработку на этом уровне дерева.

Procedure Div_Left(t:pt;Var и:item;v:item;i: index) ;

Var j -.Integers-Begin

и:=t".A[N] ;{"Запоминаем запись, которая пойдет к родителю. *} For j ; =N DownTo 1+2 Do t л . A [ j ] :=tл. A[j-1 ] ; { "Сдвигаем элементы массива. "} t". A [i + 1]:=v;{"Вставляем запись, соответствующую ключу х. "}

End;

Page 418: Pascal 8 Класс - Окулов

418 Часть четвертая

И т а к , основная часть работы (разбора) сделана . Остались почти п р и в ы ч н ы е детали : основная программа ; вспомогатель-н а я процедура вывода дерева и , конечно , процедура организа-ц и и ввода данных из файла . Д л я вывода элементов В-дерева ис-пользуем его обход «сверху — вниз» (разобран ранее) . Его м о д и ф и к а ц и я связана только с и з м е н е н и я м и структуры дерева.

Procedure PrmtTree (р :pt ; level: Integer) ; Var i:Integer;

Begin I f pNil Then

With p" Do Begin For l: =1 To level Do Write (' V/ For l: =1 To m Do Write(A[l].key:4); WriteLn; PrintTree(pO,level+l) ; For i:=l To m Do PrintTree(A[l].p,level+1);

End; End;

Основная программа . Begin

Assign(Input,'Input.Txt');ReSet(Input); Assign(Output,'Output.Txt');ReWrite(Output); root:=Nil; Solve(root); Close(Output);Close(Input);

End.

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

Procedure Solve(Var root:pt); Var q:pt;

h -.Boolean; и: Item; i,x,NI np:Integer ;

Begin ReadLn(NInp);(*Количество э л е м е н т о в в файле Input.Txt. *) For i:=l To NInp Do Begin Read(x);{*Считываем элемент. *) Search(x,root,h,u);("Вставляем в дерево.*) I f h Then Begin{*Необходимо изменить корень дерева. *}

Page 419: Pascal 8 Класс - Окулов

Динамические структуры дачных 419

q ^roots-New (root); With rootл Do Begin m:=l; pO:=q; A[l]:=u;(*B и запись, которая содержит и необходимое значение указателя. *)

Ends-End; PrintTree (root,l) ; ( "Выводим дерево. *) WriteLn;

Ends-End;

Задания для самостоятельной работы

1. В файловой системе каталог файлов организован в виде сба-лансированного дерева. Каждый узел обозначает файл, содер-ж а щ и й и м я и атрибуты файла , в том числе и дату последнего о б р а щ е н и я к файлу . Написать программу, которая обходит дерево и удаляет все файлы, последнее обращение к которым происходило до определенной даты, при этом сбалансирован-ность дерева должна сохраняться .

2. Н а й т и сбалансированноеАВЛ-дерево и короткую последова-тельность удалений, при которых используются все четыре вида балансировки хотя бы по одному разу.

3. Разработать логику и программу удаления элементов из Б-де-рева.

4. 2 - 3 деревом называется дерево, в котором каждый узел, не яв-ляющийся листом, имеет двух или трех сыновей, а длины всех путей из корня в листья одинаковы. Пример дерева приведен на рисунке. Разработать логику и программу работы (вставку, удаление элементов) с 2 - 3 деревом.

Page 420: Pascal 8 Класс - Окулов

420 Часть четвертая

5. Д в о и ч н о е дерево п о и с к а н а з ы в а е т с я к р а с н о - ч е р н ы м деревом, е с л и оно о б л а д а е т с л е д у ю щ и м и с в о й с т в а м и : • к а ж д а я в е р ш и н а л и б о к р а с н а я , л и б о ч е р н а я ; • к а ж д ы й л и с т (Nil) — ч е р н ы й ; D е с л и в е р ш и н а к р а с н а я , оба ее п о т о м к а ч е р н ы е ; • все п у т и , и д у щ и е в н и з от к о р н я к л и с т ь я м , с о д е р ж а т

о д и н а к о в о е к о л и ч е с т в о ч е р н ы х в е р ш и н .

О п и с а н и е у з л а к р а с н о - ч е р н о г о дерева и м е е т вид :

Type pt="Node; Node=Record color:-1..1;{*Цвет вершины, -1 — красный, +1 — черный*} key: Integer;{*3начение ключа.*} left,right,parent:pt;(*parent - ссылка на родителя. *} End;

П р и м е р к р а с н о - ч е р н о г о дерева .

Р а з р а б о т а т ь п р о г р а м м ы в с т а в к и и у д а л е н и я э л е м е н т о в в

Page 421: Pascal 8 Класс - Окулов

Заключение

В первой части учебника рассмотрены основные операторы системы программирования Турбо Паскаль. Предметной обла-стью для выбора задач является «Теория чисел». Знание этого раздела математики — необходимая компонента математиче-ской культуры специалиста по информатике. В материалах для чтения приводится краткий обзор развития технологий про-граммирования. Цель — дать представления о сложнейшей от-расли деятельности — программировании и том месте в этом развитии, которому посвящен учебник.

Во второй части учебника начато первое знакомство с одно-мерными массивами. Более детально работу с массивами пред-полагается рассмотреть в третьей части. Затем изучаются про-цедуры и функции, методы написания рекурсивных программ. Таким образом создаются предпосылки для формирования струк-турного стиля мышления. Весь дальнейший материал по сим-вольному, строковому, вещественному типам данных и тек-стовым файлам написан с использованием этих элементов структуризации путем последовательного уточнения задачи по технологии «сверху вниз». Предметной областью для выбора задач по-прежнему является «Теория чисел», а также «Комби-наторика» и ряд других разделов математики.

В материалах для чтения приводятся сведения по структуре ЭВМ, начальные факты из «Теории вероятностей», а также рассматриваются традиционные темы «Алгоритмы», «Инфор-мация» и «Кодирование».

Третья часть учебника, в основном, посвящена работе с мас-сивами. При этом в полной мере используется аппарат проце-дур и функций, рассмотренный во второй части. Достаточно по-дробно рассмотрены задачи сортировки и поиска элементов массива. Эти алгоритмы — лучшая база для освоения темы «массивы». На занятии по технике работы с двумерными мас-сивами используются задачи, являющиеся составными элемен-тами задач на перебор с возвратом. Занятие по комбинированно-му типу данных требуется для изучения проблематики четвертой части учебника «динамические структуры данных».

В материале для чтения к первому занятию приводятся основные понятия по схемам оценки времени работы алгорит-мов.

Технология обучения работе с массивами дается в учебной литературе обычно в очень компактном виде, поэтому рекомен-

Page 422: Pascal 8 Класс - Окулов

422 Основы программирования

довать что-либо затруднительно. Причина — то ли материал считается простым, то ли он не достаточно оценен по своим ди-дактическим возможностям. Автор считает его одним из клю-чевых в системе обучения программированию.

По методам же сортировки и поиска, наоборот, огромное ко-личество публикаций. В любой книге по алгоритмам эти мето-ды, так или иначе, рассматривают, и считается обязательным при этом ссылаться на классику. К классике, в первую очередь, относится энциклопедическое издание Д. Кнута (Кнут Д. Искус-ство программирования для ЭВМ. Сортировка и поиск. Т. 3. — М.:Мир, 1978. — 844 е.). Но если быть честным, то автор не любил работать с этой книгой и в 1978 году, не говоря уже о настоящем времени. Сверхтяжелый язык написания книги и слово MIX вызывают легкий трепет и очередное откладывание этой трехтомной монографии в дальний угол. Затем по попу-лярности идут книги А. Ахо (Ахо А., Хопкрофт Дж. , Уль-ман Дж. Построение и анализ вычислительных алгоритмов. — М.:Мир, 1979. — 536 е.), Э. Рейнгольда (Рейнгольд Э., Нивер-гельт Ю., Део Н. Комбинаторные алгоритмы. Теория и практи-ка. — М.: Мир, 1980. — 476 с.) и не так давно появившиеся книга Т. Кормена (Кормен Т., Лейзерсон Ч. , Ривест Р. Алго-ритмы. Построение и анализ. — М.: МЦНМО, 1999. — 960 е.), количество ссылок на которую еще «не набрало силу». Книги за-мечательные, каждая со своим взглядом (подходом) на изучае-мый предмет. Ни одну из них нельзя назвать простой, они на-писаны по курсам, читаемым в классических зарубежных университетах. Возьмем, например, последнюю книгу, весьма объемную, но очень неоднозначную (последние главы, особенно по приближенным алгоритмам, видимо, написаны уже на по-следнем дыхании). После глав с математической преамбулой рассматривается сортировка с помощью кучи, использующая понятие дерева и т. д. То есть динамические структуры данных лежат в основе всего последующего изложения. Прекрасно. Но опыт работы показывает, что для того, чтобы «довести» школь-ника, студента до понимания динамических структур, необхо-димо съесть совместно «не один пуд соли».

При подготовке занятия по комбинированному типу данных автор использовал одну из лучших книг своей библиотеки как по содержанию, так и по языку написания (Дагене В. А., Гри-гас Г. К., Аугутис К. Ф. 100 задач по программированию. — М.: Просвещение, 1993. — 255 е.).

Четвертая часть учебника посвящена динамическим струк-турам данных. При этом в полной мере используется аппарат

Page 423: Pascal 8 Класс - Окулов

Заключение

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

При написании этой части учебника автор чаще всего загля-дывал в замечательную книгу человека, вклад которого в Compu-ter Science, в образовательную информатику, по мнению автора, просто огромен. Это книга Никлауса Вирта (Вирт Н. Алгоритмы + Структуры данных = Программы. — М.: Мир, 1985. — 406 е.). В книге А. Ахо (Ахо А., Хопкрофт Дж., Ульман Дж. Построение и анализ вычислительных алгоритмов. — М.:Мир, 1979. — 536 с.) этот материал не полный и приведен в весьма своеобразном виде. Работать с ним очень трудно, и попытка изложить его в та-ком же стиле у автора не получилась. В не так давно появившей-ся и красиво изданной книге Т. Кормена (Кормен Т., Лейзер-сон Ч., Ривест Р. Алгоритмы. Построение и анализ. — М.: МЦНМО, 1999. — 960 с.) нет, например, АВЛ-деревьев.

Для дополнительной работы можно порекомендовать следу-ющие книги.

1. О системах счисления и представлении чисел в памяти компь-ютера написано прекрасное учебное пособие. (Андреева Е., Фалина И. Информатика: Системы счисления и компьютер-ная арифметика. — М.: Лаборатория Базовых Знаний, 1999. — 256 с.)

2. О математической логике можно почитать во второй главе учебно-справочного издания А. И. Гусевой. (Гусева А. И. Учимся информатике: задачи и методы решения. — М.: «Диа-л о г — М И Ф И » , 1998. — 320 с.)

3. По теории чисел лучше классической книги И. М. Виногра-дова трудно что-либо найти. Но, конечно, читать ее достаточ-но сложно, если особенно у Вас не очень основательная друж-ба с математикой. (Виноградов И. М. Основы теории чисел. — М.: Наука, 1972. — 167 с.)

4. О профессии программиста с большим юмором написана кни-га А. Н. Венц. В ней Вы найдете и экскурсы в историю про-граммирования. (Венц А. Профессия — программист. — Ро-стов н/Д: Изд-во «Феникс», 1999. — 384 с.

5. О технологиях программирования лучше всего читать в клас-сической книге Г. Буча, настольной книге любого программи-ста. (Буч Г. Объектно-ориентированное проектирование с при-мерами применения: Пер. с англ. — М.: Конкорд, 1992. — 519 с.)

Page 424: Pascal 8 Класс - Окулов

424 Основы программирования

6. Много полезных примеров из истории программирования м о ж н о найти в уникальной книге компьютерного провидца (так его называют) Д. Васкевича. (Васкевич Д. Стратегия кли-ент/сервер. Руководство по выживанию для специалистов по р е о р г а н и з а ц и и бизнеса. — Киев: « Д и а л е к т и к а » , 1996. — 384 с.)

7. Д л я первоначального знакомства с теорией вероятности луч-ше блестящей книги Б . В. Гнеденко и А. Я. Хинчина, выдер-ж а в ш е й многочисленные издания , вряд ли что-либо можно найти. (Гнеденко Б . В., Хинчин А. Я. Элементарное введение в теорию вероятностей. — М.: Наука , 1976. — 168 с.)

8. О комбинаторике написана прекрасная книга Наума Яковле-вича Виленкина . Автор очень её любит и не расстается с ней с момента ее выхода. (Виленкин Н. Я. Комбинаторика. — М.: Наука , 1969. — 328 с.)

9. Если не очень сильно обращать внимание на традиционную запись алгоритмов в виде блок-схем, то о них написана очень неплохая к н и г а для ш к о л ь н и к о в Ю. А. Макаренкова и А. А. Столяра. (Макаренков Ю. А., Столяр А. А. Что такое алгоритм? — Мн.: Нар. асвета, 1989. — 127 с.)

10. Интересна для школьников и книга Льва Васильевича Тарасо-ва. В ней можно узнать о том, что такое информация, вероят-ностный подход к её определению. (Тарасов Л. В. Мир, постро-енный на вероятности. — М.: Просвещение, 1984. — 191 е.).

11. Д л я дальнейшего изучения кодирования лучше всего по-дойдет книга М. Н. Аршинова и Л . Е. Садовского (Арши-нов М. Н. , Садовский Л . Е. Коды и математика (рассказы о кодировании). — М.: Наука , 1983. — 144 с.)

К сожалению, рекомендовать книги по Турбо Паскалю очень трудно, несмотря на их огромное количество. Часть из них но-сит описательный характер, т. е. может использоваться только как справочный материал. Другие претендуют на обучение, но как это делать, трудно понять (мало задач, не разработан «стык» задач с изучаемыми конструкциями и возможностями системы программирования). Поэтому позволим себе воздержаться от рекомендаций.