131
Елабужский государственный педагогический университет Кафедра информатики и дискретной математики Язык программирования PASCAL Часть II Лабораторный практикум по основам программирования Елабуга 2007

Document2

Embed Size (px)

Citation preview

Page 1: Document2

Елабужский государственный педагогический университет

Кафедра информатики и дискретной математики

Язык программирования PASCAL

Часть II

Лабораторный практикум по основам программирования

Елабуга 2007

Page 2: Document2

УДК 681.3.06 Печатается по решению Ученого совета Елабужского ББК 32. 923 государственного педагогического университета, И 21 протокол № от 2007 г.

Составитель: Иванова Л.В., ст. преподаватель кафедры информатики и дискретной математики ЕГПУ Рецензенты: Конюхов М.И., Зав. кафедрой ОДН Филиала КГТУ им. А.Н. Ту-

полева, кандидат технических наук. Лизунова Е.М., ст. преподаватель кафедры информатики и дискретной математики ЕГПУ

В учебно-методическом пособии основное внимание уделяется алгорит-

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

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

Иванова Л.В. Язык программирования PASCAl. Часть II. Лабораторный прак-тикум по основам программирования. Уч.-метод. пос. для студентов педаг. вузов Елабуга, ЕГПУ, 2007

Page 3: Document2

КНИГА 2

Page 4: Document2

Содержание

1. Тема: Знакомство с интегрированной средой Turbo Pascal ............................... 5 2. Тема: Целый тип данных ........................................................................................ 9 3. Тема: Команды редактора для работы с блоками, работа с окнами ................ 13 4. Тема: Логический тип данных ............................................................................. 18 5. Тема: Составной и условный операторы ............................................................ 24 6. Тема: Оператор For ............................................................................................... 29 7. Тема: Оператор While ........................................................................................... 35 8. Тема: Оператор Repeat... Until.............................................................................. 39 9. Тема: Вложенные циклы ...................................................................................... 44 10. Тема: Одномерные массивы. Работа с элементами ......................................... 50 11. Тема: Процедуры ................................................................................................. 59 11. Тема: Функции..................................................................................................... 69 12. Тема: Рекурсия..................................................................................................... 77 14. Тема: Символьный и строковый типы данных ............................................. 87 15. Тема: Вещественный тип данных ...................................................................... 94 16. Тема: Двумерные массивы. Работа с элементами.......................................... 101 17. Тема: Двумерные массивы. Вставка и удаление............................................ 104 18. Тема: Множественный тип данных ................................................................. 110 19. Тема: Текстовые файлы .................................................................................... 113 20. Тема: Комбинированный тип данных (записи) ............................................. 120 21. Тема: Типизированные файлы ........................................................................ 128 Литература ............................................................................................................... 131

Page 5: Document2

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

5

1. Тема: Знакомство с интегрированной средой Turbo Pascal План занятия: 1. загрузка Турбо Паскаля (см. «Справочное руководство»: стр. 71 - 74); 2. краткое знакомство с интегрированной средой; 3. первая программа - нахождение произведения двух целых чисел:

• компиляция программы (сообщения об ошибках см. «Справочное руководство»: стр. 49 - 68);

• сохранение программы; • запуск программы; • разбор программы.

Первая программа. В рабочем окне редактора интегрированной среды набе-

рем текст первой программы. Program prl_l ; Var a, b, rez : Integer; Begin WriteLn ('Введите два числа через пробел'); ReadLn (a, b); rez :=a*b; WriteLn ('Их произведение равно ', rez); WriteLn ('Нажмите <Enter>'); ReadLn End.

Программа начинается с заголовка, который имеет следующий вид: Program <имя программы>;

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

После раздела описаний идет раздел операторов, который начинается со служебного слова Begin и заканчивается служебным словом End. В этом разде-ле задаются действия над объектами программы, введенными в разделе опи-саний. Операторы, посредством которых эти действия производятся, разде-ляются точкой с запятой. После последнего слова End ставится точка.

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

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

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

Page 6: Document2

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

6

инициализированных переменных (т.е. переменных, значения которым не были присвоены явно) часто является причиной ошибок.

После раздела описаний расположен раздел операторов, начинающийся со служебного слова Begin, после которого записываются операторы языка. Пер-вый оператор в нашей программе - это WriteLn ('Введите два числа через пробел') - записать (вывести) на экран текст, заключенный между апостро-фами, Ln за словом Write означает, что после вывода следует перевести курсор на следующую строку.

Следующий оператор - ReadLn (а, b) - читать данные с клавиатуры. В дан-ном случае необходимо ввести два целых числа через пробел. Переменной а присваивается значение, равное первому введенному числу, а переменной b - значение, равное второму введенному числу. Например, при вводе чисел 12 и 45, а = 12, а b = 45. В конце этого оператора Ln имеет тот же смысл.

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

Следующий оператор - это снова оператор вывода WriteLn ('Их произведе-

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

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

Произведение равно … Нажмите <Enter> Вместо точек будет выведено значение переменной rez, то есть число, рав-

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

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

Page 7: Document2

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

7

• выйти в главное меню и выбрать режим File; • нажать <Enter>, в вертикальном меню выбрать пункт Save as… и нажать клавишу

<Enter>; • в появившемся окне ввести имя файла (например, prim1_1.pas) и нажать клавишу

<Enter>. Мы будем сохранять свои программы в каталоге (папке) STUDENT, где соз-

дадим свою папку – номер группы, (например, 622): C:\STUDENT\622\PRIM1_1.PAS. Здесь C:\ логическое имя диска, на кото-

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

Примечания: 1. В именах файлов нельзя употреблять следующие символы: «*», «=», «+», «[«, «]», «\», «|»,

«:», «.», «<», «>», «/», «?», символ пробела и буквы русского алфавита. 2. Для быстрого сохранения файла можно воспользоваться командами Save <F2> или Save

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

кончить работу, необходимо: 1. выйти в главное меню и выбрать пункт File; 2. нажать <Enter> и в вертикальном меню выбрать пункт Quit, после чего нажать либо

<Enter>, либо просто нажать комбинацию клавиш <Alt> + <Х>. Эксперименты с программой

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

2. Вместо числа введите какой-нибудь символ. Убедитесь, что компьютер вы-дает сообщение об ошибке "Error 106: Invalid numeric format" (см. «Спра-вочное руководство», стр. 66).

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

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

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

6. Наберите следующую программу: Program pr1_2; Var a : Integer; Begin

Page 8: Document2

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

8

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

Выясните, что вычисляет Abs. Выполните аналогичное исследование для выражений sqr (a), Ord (a) , Succ (a) , pred (a).

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

переменных а, d, с и k вводятся с клавиатуры. 4. Нарисуйте схему (взяв за образец рисунок, приведенный в тексте занятия), иллюстри-

рующую выполнение следующего фрагмента программы: х:=13; у:=25; t:=x; х:=у; y:=t; 5. Измените рисунок из предыдущего примера, заменив три последних оператора на: х:=х -

у; у:=х+у; х:=у-х; 6. Напишите программу вывода на экран нескольких чисел в виде 13 100 14 101 15 или 102 16 103

1000 1001 или 1003 1004

Материал для любознательных

1. Язык программирования Паскаль был разработан Н. Виртом в 1968 -1970 годах и полу-чил широкое распространение благодаря наглядности программ и легкости при изуче-нии. Он послужил основой для разработки других языков программирования (например, Ада, Модула - 2). Турбо Паскаль появился на рынке программных продуктов в 1983 го-ду и совершил настоящую революцию в программировании и обучении этой дисципли-не. До этих пор предпочтение отдавалось Бейсику - простому, дешевому и массовому. Паскаль же был аппаратно - зависимым, дорогим и сложным в обращении. С появлени-ем Турбо Паскаля положение изменилось. Удобство Турбо Паскаля в том, что он пре-доставляет интегрированную среду - редактор, компилятор и среду выполнения программы. Первая версия Турбо Паскаля использовалась не очень долго - она поя-вилась в 1983 году, а уже в 1984 году ее заменила вторая версия, которая получила ши-рокое распространение. К осени 1985 года появляется третья версия, более удобная в работе. Четвертая версия (1988 год) представила Турбо Паскаль в новом виде (в част-ности, компилятор стал встроенным). Осенью этого же года разработана пятая версия, в которой появился встроенный отладчик. В 1989 году появилась версия 5.5, позволив-шая перейти к объектно - ориентированному программированию. Шестая версия уже обеспечивала многооконный и многофайловый режимы работы, использование мыши, применение объектно-ориентированного программирования, обладала встроенным ас-семблером и имела другие возможности. В 1992 году фирма Borland International выпус-тила два пакета программирования на языке Паскаль - Borland Pascal 7.0 и Turbo Pascal 7.0. Пакет Turbo Pascal 7.0 использует новейшие достижения в программирова-нии. Язык этой версии обладает широкими возможностями, имеет большую библиотеку модулей. Среда программирования позволяет создавать тексты программ, компи-лировать их, находить и исправлять ошибки, компоновать программы из отдель-ных частей, использовать модули, отлаживать и выполнять отлаженную програм-му.

Page 9: Document2

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

9

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

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

2. Тема: Целый тип данных План занятия: 1. Типы данных для представления целых величин и операции над ними (см. «Книга 1», стр.

20 - 27). 2. Разбор программы выделения цифр целого числа. 3. Эксперименты с программой:

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

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

Представление целых величин. Имеются пять типов данных для представле-ния целых чисел: ShortInt, Integer, Longlnt, Byte, Word. Они отличаются диапазо-ном значений, а значит, и размером памяти, отводимой для их представления. Над величинами целого типа определены следующие операции: сложение (+), вы-читание (-), умножение (*), деление нацело (Div), получение остатка от деле-ния (Mod). Так как на данных целого типа определено отношение порядка, то можно использовать стандартные функции Ord, Succ и Pred.

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

Разбор программы. Рассмотрим примеры выполнения операций Div и Mod над различными (положительными и отрицательными) числами:

19 div 4 = 4; -19 div 4 = - 4; 19 div (-4) = - 4; -19 div (-4) = 4

19 mod 4 = 3 -19 mod 4 = - 3; 19 mod (-4) = 3; -19 mod (-4) = - 3

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

Page 10: Document2

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

10

Program pr2_1; Var а,one,dec,hun,rez: Integer; Begin WriteLn ( ' Введите число ' ) ; ReadLn (a) ; one :=a Mod 10; WriteLn ( 'Цифра единиц числа - ' , one) ; dec := (a Div 10) Mod 10; WriteLn ( ' Цифра десятков числа - ', dec) ; hun := a Div 100; WriteLn ( 'Цифра сотен числа -' ,hun ); rez := hun*100+dec*10+one; WriteLn( a, ' это тоже число ' ,rez) ; Write ( 'Нажмите <Enter> ' ); ReadLn End.

Введете число 137 и запишите результат в тетрадь (one будет равно 7, dec - 3 и hun – 1). Примечание. Не забудьте сохранить программу под именем PRIM2_1. PAS.

Экспериментальный раздел работы 1. Измените программу pr2_1 для нахождения цифр двузначного числа. Сохраните

ее под именем PRIM2_2.PAS. 2. Измените программу pr2_1 для нахождения цифр четырехзначного числа. Со-

храните ее под именем PRIM2_2.PAS. 3. Деление на 10 и нахождение остатков от деления мы рассмотрели выше. Рас-

смотрите (самостоятельно в тетради) пример деления столбиком на 2, напри-мер, числа 137 (остатки от деления или 0, или 1). Наберите следующую программу, отладьте ее и попробуйте дать объясне-

ние полученному результату. Измените программу так, чтобы она правильно работала, например, с числом 115. Program pr2_2; Var rez : Integer; Begin WriteLn ( '137 ' ) ; WriteLn ( '10001001' ); Rez := l*128+0*64+0*32+0*l6+1*8+0*4+0*2+1*1; WriteLn (rez) ; End. 4. Наберите следующую программу: Program pr2_3; Uses Crt; Var a : Integer; b : Word; r1 : Integer; r2 : LongInt; Begin ClrScr;{Очистка экрана, процедура модуля Crt} a:= 32000; b:= 64000; r1 := a+b; WriteLn (r1); r2 := a+b; WriteLn (r2); End.

После запуска вы увидите, что значение переменной r1 равно 30 464, а зна-чение переменной г2 - 96 000. Если изменить тип переменной r1 на Word, то ре-зультат не изменится. Измените программу так, чтобы проделать аналогичные эксперименты с данными других целых типов. Попробуйте понять, как полу-чаются эти результаты.

Page 11: Document2

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

11

5. Добавьте в программу pr2_3 перед ReadLn следующие два оператора: WriteLn (LongInt (100*a) ); WriteLn (100*LongInt (a) );

Функция LongInt преобразует переменную типа Integer в тип LongInt. В пер-вом случае преобразование осуществляется после умножения, а во втором - пе-ред умножением. В первом случае получается результат, далекий от истины, - отрицательное число - 11 264, во втором - правильный, 3 200 000.

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

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

2. Выражение в правой части оператора присваивания вычисляется независимо от размера или типа переменной в левой части!

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

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

1. Чему равны значения переменных a и b после выполнения последовательности действий

(вычислить устно и записать результаты в тетради): а:= 15 Div (16 Mod 7); b:= 34 Mod a * 5 - 29 Mod 5*2; a:= 4 * 5 Div 3 Mod 2; b:= 4 * 5 Div ( 3 Mod 2 ).

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

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

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

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

5. Любой член арифметической прогрессии вычисляется по формуле ап= а1 + d (п - 1), где d - разность прогрессии, п - номер взятого члена. Даны а1 и d. Найти (эксперименталь-ным путем) п, при котором значение ап выходит за диапазон типа Integer.

6. Сумма первых n членов арифметической прогрессии вычисляется по формуле Sn= (a1 + an) • n / 2. Даны а1 и d. Найти (экспериментальным путем) n, при котором значение Sn вы-ходит за диапазон типа Integer.

Материал для любознательных

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

Page 12: Document2

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

12

между операндами), унарные - в префиксной (знак унарной операции предшествует операнду). Порядок выполнения операций в выражении определяется их приоритетами, которые приведены в таблице (см. Книга 1, стр. 33 - 40).

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

Например, в десятичной записи числа 3377 = 3 • 1000 + 3 • 100 + 7 • 10 + 7 = 3 •103 + 3 • 102

+ 7• 101 + 7 • 100 вклад как цифр 3, так и цифр 7 различен. Это же число в пятеричной системе счисления записывается как 102002: 3377 = 1• 55 + 0 • 54 + 2 • 53 + 0 • 52 + 0 • 52 + 2 • 50 , (55= 3125, а 53 = 125). Это же число в двоичной системе счисления записывается как 110100110001: 3377 = 1 • 211 + 1 • 210 + 0 • 29 + 1 • 28 + 0 • 27 + 0 • 26 + 1 • 25 + 1 • 24 + 0 • 23 + 0 • 22+ 0 • 21 + 1 • 20, (211 = 2048, 210= 1024, 29= 512, 28 = 256, - и т.д.)

Алгоритм перевода целого числа из десятичной системы счисления в двоичную: делим исходное число на 2 нацело в десятичной системе счисления и считаем новым значением числа целую часть частного, остаток от деления, а это 0 или 1 , запоминаем; продолжаем процесс деления до тех пор, пока не будет получен 0. Результат получаем, выписывая остат-ки в порядке, обратном их нахождению. Пример: 3377 : 2 = 1688 (1) 1688 : 2 = 844 (0) 844 : 2 = 422 (0) 422 : 2 = 211 (0) 211 : 2 = 105 (1) 105 : 2 = 52 (1) 52 : 2 = 26 (0) 26 : 2 = 13(0) 13 : 2 = 6 (1) 6 : 2 = 3 (0) 3 : 2 = 1 (1) 1 : 2 = 0 (1) В скобках указано значение остатка от деления. Итак, 337710= 1101001100012. 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, 11О, 111 (23). Если перевести эти 8 последовательностей из двоичной системы счисления в деся-тичную, то получим десятичные цифры от 0 до 7. Если у нас есть 4 бита, а это 16 различных двоичных последовательностей, то мы можем хранить числа из интер-вала от 0 до 15. Проверьте.

Что делать, если необходимо хранить и отрицательные числа? У нас по-прежнему 4 бита памяти компьютера. Естественным шагом является выделение одного бита для хранения знака числа. Получаем, что, например, - 5 хранится как 1101, а 5 - 0101. И диапазон пред-ставления чисел в этом случае от -7 до 7, т.е. 15 различных значений, а у нас есть воз-можность хранить в 4 битах 16 различных последовательностей. Последовательность 1000 не задействована, или, что еще более неприятно, она может трактоваться как (-0), а это уже из разряда необъяснимых фактов. Необходимо научиться выполнять следующие действия: инверсию и прибавление 1 к двоичному числу.

Пример.

Число Двоичное представление Инверсия Прибавление 1 Отрицательное

число 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

Page 13: Document2

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

13

6 0110 1001 1010 -6 7 0111 1000 1001 -7 8 1000 0111 1000 -8

0 0000 1111

10000 (пятый, старший, разряд "отбрасы-

ваем")

0

В предпоследнем столбце мы получаем двоичное представление отрицательных чисел.

Все 16 двоичных последовательностей задействованы, диапазон представления от - 8 до 7 и представление 0 однозначно. Представление отрицательных чисел в том виде, который при-веден в таблице, называется дополнительным кодом. Естественно, что представление поло-жительных чисел в дополнительном коде совпадает с их обычным представлением. Сравните полученный результат с диапазоном значений величин целого типа из таблицы, приведенной на странице 26 (Книга 1). 3. Тема: Команды редактора для работы с блоками, работа с окнами План занятия: 1. знакомство с командами редактора для работы с блоками; 2. работа с окнами в интегрированной среде; 3. программа вычисления степеней двойки; 4. программа перевода чисел из десятичной системы счисления в двоичную; 5. выполнение самостоятельной работы.

Знакомство с командами редактора для работы с блоками

Блок - выделенный фрагмент текста программы. В каждый момент вре-

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

Основные команды редактора (пункт Edit главного меню) для работы с бло-ками (см. «Справочное руководство», стр.74) и: <Ctrl> + <K><R> - загрузить с диска ранее записанный блок, начиная с позиции курсора; <Ctrl> + <K><W>- записать отмеченный блок на диск.

Примечание 1. Последовательность выполнения команд:

• устанавливаем курсор в требуемое место текста; • нажимаем клавишу <Ctrl>; • не отпуская клавишу <Ctrl>, нажимаем клавишу с буквой <K>; • отпустив клавишу с буквой <K>, не отпуская клавишу <Ctrl>, нажимаем клавишу со

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

пользуйте их при работе. Структура задания

Page 14: Document2

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

14

1. Набор любого содержательного текста. Например. "На счетах десятичное число кодируется положением костяшек на спицах, а в арифмометре (меха-ническом вычислителе) - положением диска с 10 зубцами. С помощью свя-занных между собой дисков можно построить суммирующее устройство. Идея такого устройства принадлежит Леонардо да Винчи (1452—1519). А

впервые сделал его в 1642 году французский философ и математик Блез Пас-каль (1623—1662).

Выдающийся американский математик Джон фон Нейман (1905—1957) при работе в составе группы исследователей над машиной "ЭНИАК" сформулиро-вал принципы, лежащие в основе функционирования современных вычисли-тельных машин. Суть этих принципов: в памяти ЭВМ хранятся не только дан-ные, но и обрабатывающая их программа; представление в памяти данных и программы совпадает; программа должна выполняться последовательно, ко-манда за командой". 2. Выделение фрагментов текста как блоков. 3. Копирование фрагментов текста (выделяется блок, курсор устанавливается в

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

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

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

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

Tile последовательное размещение окон; Cascade каскадное размещение окон; Close All закрытие всех окон. Size / Move (<Ctrl>+ F5>)

изменение размера, перемещение окна. При выполнении команды изменяется цвет рамки окна.

Для изменения размера окна используются комбинации клавиш <Shift> + < >, < ↓ >, < ← >, < → >. Для перемещения — < >, < ↓ >, < ← >, < → >. Для за-вершения изменения параметров окна следует нажать клавишу <Enter>. При этом цвет рамки окна изменяется на первоначальный.

Zoom (<F5>) распахнуть активное окно на полный экран; Next (<F6>) переход к следующему окну ; Previos (<Shift> + <F6>) переход к предыдущему окну; Close (<Alt> + <F3>) закрытие активного окна; List просмотр списка открытых окон.

Page 15: Document2

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

15

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

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

1. Выполните обычные действия с программой pr3_1 (набор, компиляцию, за-пись на диск, запуск). Program pr3_1; Uses Crt; Var r0, r1, r2, rЗ, r4, r5, r6 : Integer; Begin ClrScr; WriteLn ( ' Вычисляем степени числа 2 ' ); r0:=1; r1:=2; r2:=r1*r1; r3:=r2*r1; r4:=r2*r2; r5:=r3*r2; r6:=rЗ*rЗ; WriteLn ( ' Нулевая степень = ', r0 ) ; WriteLn ( ' Первая степень = ' , r1 ); WriteLn ( ' Вторая степень = ', r2 ); WriteLn ( ' Третья степень = ', rЗ); WriteLn ( ' Четвертая степень = ' , r4); WriteLn ( ' Пятая степень = ' , r5); WriteLn ( ' Шестая степень = ' , r6); ReadLn End.

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

Для вычисления 5-й степени двойки в нашей программе требуется 4 операции: для вычисления r3 требуются 2 операции, r2 - одна операция плюс заключительное умножение, итого - 4 операции. Можно ли вычислить 25 за меньшее количество операций?

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

Примечание. При наборе программы рекомендуется использовать изученные команды рабо-ты с блоками и окнами. Program prЗ_2; Uses Crt; Var r0,r1,r2, rЗ , r4 , r5 , r6 : Integer; s0,s1,s2,s3,s4,s5,s6 : Integer; a,b,rez : Integer; one,dec,hun : Integer; Begin ClrScr;{Очистка экрана} {Вычисление степеней двойки} r0:=1; r1:=2; r2:=r1*r1; r3:=r2*r1; r4:=r2*r2; r5:=rЗ*r2; r6:=rЗ*rЗ; WriteLn ( ' Введите число, меньшее 128 и большее 64');ReadLn (а); b:=a; {Перевод числа в двоичную систему счисления} s0:=b Mod 2; b:=b Div 2; s1:=b Mod 2; b:=b Div 2;

Page 16: Document2

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

16

s2:=b Mod 2; b:=b Div 2; s3:=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; {Выделение десятичных цифр в записи числа} one:=b Mod 10; b:=b Div 10; dec:=b Mod 10; b:=b Div 10; hun:=b Mod 10; WriteLn ( ' Мы правильно выделили десятичные цифры ' ); Write('Вывод числа в десятичной системе счисления - '); WriteLn(hun*100+dec*10+one); WriteLn (hun,dec,one); WriteLn ( ' Это же число в двоичной системе счисления ' ); WriteLn (s6,s5,s4,s3,s2,s1,s0); WriteLn ( ' Переводим число из двоичной системы счисления в десятичную' ); rez:=s6*r6+s5*r5+s4*r4+s3*r3+s2*r2+s1*r1+s0*r0; {Перевод} WriteLn { ' Все сделано правильно, числа совпадают ' ) ; Writeln (a,' ', rez); Readln End.

Попробуйте задать в качестве исходных данных число, большее 128. Что у вас получилось? Модифицируйте программу так, чтобы и в этом случае она была ра-ботоспособна.

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

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

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

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

ленного интервала в троичную систему счисления и обратно. 4. На предыдущем занятии в материале для любознательных мы рассмотрели представле-

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

Page 17: Document2

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

17

5. Пусть вам дано 6 разрядов для представления целых чисел. Измените предыдущие опе-рации так, чтобы они выполнялись и для этого случая. Разберите еще несколько анало-гичных примеров.

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

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

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

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

10. Сравните числа в различных системах счисления 3124 и 728. Изменится ли результат, ес-ли вычесть из чисел соответственно 124, и 128?

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

12. Может ли в какой-нибудь системе счисления с основанием х выполняться следующее равенство: 600x= 211x + 252x + +53х? Ответ обоснуйте.

Материал для любознательных

Попытаемся ответить на вопрос: что такое программа? Существуют различные научные

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

Оставив в стороне нюансы терминологии, вспомним разобранные программы и ответим на вопросы. «Откуда взять?» - мы пока вводили данные с клавиатуры компьютера. «Что сделать?» - например, в нашей первой программе один оператор присваивания. «Куда положить?» - наши программы выводят результаты на монитор.

Самый главный вопрос – «а если это не так?». Мы убедились в том, что даже в первой программе ответ не очевиден. Она работает не при всех исходных данных. Если вы отком-пилировали и несколько раз запустили программу и получили правильные результа-ты, то это еще не значит, что у вас есть работающая программа.

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

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

А.Н. Венц в своей книге приводит формулу великого программиста (ВП), выведенную экспериментальным путем: ВП = 50%К + 30%Т + 10%О + 5%З + 5%ТЛ,

где К - знать, как это делать, Т- трудолюбие, О - опыт, 3 - знание, ТЛ - талант. На то, что относится к вундеркиндам, приходится только 5%, остальное — труд, еже-

дневный труд.

Page 18: Document2

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

18

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

ры данных); 2. определить операции по обработке данных (операторы); 3. выполнить вывод результатов работы программы (вывод).

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

(подпрограммы). Итак, эти семь элементов — ввод, данные, операторы, вывод, условия, циклы, подпро-

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

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

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

4. Тема: Логический тип данных План занятия: 1. обсуждение логического типа данных (см. Книга 1, стр. 22 – 23; 38 - 40); 2. обсуждение побитовых операций; 3. эксперименты с программами (построение таблицы истинности, выполнения операций

сдвига и логических операций над целыми числами); 4. выполнение самостоятельной работы:

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

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

мощью идентификатора Boolean. Они могут принимать два значения: False (ложь) или True (истина) (False и True - стандартные константы), под них выде-ляется 1 байт памяти. Логический тип является перечислимым: False < True, Ord(False)=0, OrdTrue)=1, Succ(False)=True, Pred(True)=False. В Турбо Паскале имеются четыре логические операции: логическое сложе-ние, или, дизъюнкция, - or; логическое умножение, или конъюнкция, - and; отрицание - not, исключающее " или " (сложение по модулю два) - хor. Ре-зультаты выполнения данных операций над переменными логического типа приведены в таблице (см. стр. 39). Фактически приведены четыре таблицы ис-

Page 19: Document2

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

19

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

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

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

Операции сдвига. Рассмотрим две операции над данными целого типа: shl - сдвиг влево и shr - сдвиг вправо.

В результате выполнения операции m shl п значение m сдвигается влево на n разрядов; в результате выполнения операции m shr п значение m сдвигается вправо на n разрядов. При выполнении этих операций в Турбо Паскале разря-ды, вышедшие за пределы области памяти, выделяемой для типа данных, теря-ются, а с другой стороны добавляются нули. Например, если т равно 32, то сдвиг влево на один разряд дает 64, а сдвиг вправо - 16. Операции сдвига на один разряд равносильны умножению и делению нацело на два.

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

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

запись на диск, запуск). Program Pr4_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. Выполните обычные действия с программой Pr 4_2 (набор, компиляцию, за-

пись на диск, запуск). Program Pr4_2; Uses Crt; Var m, n : Integer;

Page 20: Document2

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

20

Begin ClrScr; WriteLn (' Введите число и величину, сдвига ' ); ReadLn (m, n); WriteLn ('При сдвиге на ', n, ' разрядов влево числа ' ,m, ' получаем число ' , m shl n); WriteLn (' Введите число и величину сдвига ' ); ReadLn (m, n); WriteLn ('При сдвиге на ' , n, ' разрядов вправо числа, ' ,m, ' получаем число ' , m, shr n); ReadLn End.

При вводе m = 32, n = 1 получаются числа 64 (результат сдвига влево) и 16 (результат сдвига вправо). Сдвиги вправо отрицательных чисел приводят к ин-тересным результатам. Например, если вы введете -1 и 1 для того и другого сдвигов, то получите -2 и 32 767. Если первый результат вполне объясним, то для понимания второго требуется вспомнить о представлении отрицательных целых чисел в дополнительном коде. Пусть у нас не 16 разрядов для представ-ления чисел (тип Integer), а 4. Представление -1 в дополнительном коде есть 11112, Сдвиг вправо на один разряд приводит к числу 01112, а это не что иное, как 710. 3. Выполните обычные действия с программой pr4_3, набрав только первые

три оператора WriteLn. Program Pr4_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.

С величинами типа Integer можно выполнять логические операции, причем они выполняются поразрядно над двоичными представлениями чисел. Почему выбраны числа 1365 и 2730? Переведите их в двоичную систему счисления: 136510 = 0101010101012, 273010 = 1010101010102 (рассматриваются только 12 младших разрядов). Операция and дает в результате число 0, а операции or и хоr - 4095. Поэкспериментируйте с программой. Убедитесь, например, что -256 and 255 = 0 -256 or 255 = -1 -256 xor 255 = -l. Объясните полученные результа-ты.

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

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

1. Организация ввода/вывода. Изучение стандартных типов данных

Page 21: Document2

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

21

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

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

личных строках. При этом следует помнить, что ввод со следующей строки осуществляет-ся в том случае, если предыдущим оператором является READLN.

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

3. При работе в диалоговом режиме следует перед оператором ввода использовать оператор вывода на экран приглашения - подсказки о том, что наступило время ввода информации и какой именно информации. Например: WRITELN(‘ВВЕДИ ЦЕЛЫЕ ЧИСЛА K, L, M ’).

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

Вариант 1

Целые числа Вещественные числа символы булевская пе-

ременная Количест-

во ширина по-

ля количест-

во ширина поля

количество знаков после запятой

количество значение

2 5 4 6 2 4 TRUE

Вариант 2

Целые числа Вещественные числа символы булевская пе-ременная

количест-во

ширина по-ля

количест-во

ширина поля

количество знаков после запятой

количество значение

3 6 2 7 3 5 FALSE

Вариант 3

Целые числа Вещественные числа символы булевская пе-ременная

количест-во

ширина по-ля

количест-во

ширина поля

количество знаков после запятой

количество значение

3 6 2 6 3 3 TRUE

Вариант 4

Целые числа Вещественные числа символы булевская пе-ременная

количест-во

ширина по-ля

количест-во

ширина поля

количество знаков после запятой

количество значение

2 5 4 6 2 6 TRUE

Вариант 5

Целые числа Вещественные числа символы булевская пе-ременная

количест-во

ширина по-ля

количест-во

ширина поля

количество знаков после запятой

количество значение

4 4 3 7 4 4 FALSE

Вариант 6

Целые числа Вещественные числа символы булевская перемен-ная

Page 22: Document2

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

22

количество ширина поля количество ширина поля

количество знаков по-сле запятой

количество значение

2 3 3 5 1 3 TRUE

Вариант 7

Целые числа Вещественные числа символы булевская перемен-ная

количество ширина поля количество ширина поля

количество знаков по-сле запятой

количество значение

3 2 4 8 3 2 TRUE

Вариант 8

Целые числа Вещественные числа символы булевская перемен-ная

количество ширина поля количество ширина поля

количество знаков по-сле запятой

количество значение

3 5 4 6 2 5 FALSE

Вариант 9

Целые числа Вещественные числа символы булевская перемен-ная

количество ширина поля количество ширина поля

количество знаков по-сле запятой

количество значение

4 7 3 5 1 6 FALSE

Вариант 10

Целые числа Вещественные числа символы булевская перемен-ная

количество ширина поля количество ширина поля

количество знаков по-сле запятой

количество значение

2 6 5 8 4 7 TRUE

2. Вычисление выражений. Использование стандартных математических

функций Паскаля Постановка задачи:

1. Найти значение функции y = f(x) при заданном x и вычислить trunc(y), frac(y), round(y), int(y).

2. Записать выражение, зависящее от координат точки (x,y) и принимающее значение TRUE, если точка принадлежит заштрихованной области, и FALSE, если не принадлежит. Для заданной точки вычислить значение этого выражения и результат вывести на экран дис-плея.

Содержание отчета: 1. Постановка задачи. 2. Описание используемых стандартных математических функций. 3. Текст программы. 4. Результаты счета.

Методические указания:

1. Функции, отсутствующие в списке стандартных математических функций языка ПАС-КАЛЬ, следует выразить через имеющиеся.

2. Вывод значения логического выражения в данной точке (x,y) организовать, используя процедуру WRITELN.

3 .Результаты каждого задания вывести на экран дисплея с соответствующими подсказками.

Задания: Вариант 1

1. 42 xxY x += − при X = 4,741;

Page 23: Document2

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

23

2. Координаты исследуемой точки: (0,5; 0,5). Область изображена на рис.1. Вариант 2 1. 3 sin xeY x −= при X = 2,312; 2. Координаты исследуемой точки: (1,5; 0,5). Область изображена на рис.2. Вариант 3 1. xxY sin1 +−= при X = 12,7409; 2. Координаты исследуемой точки: (0,2; 0,9). Область изображена на рис.3. Вариант 4 1. Y= x*cosx + sin3x при X=32,872; 2. Координаты исследуемой точки: (0,75; – 0,3). Область изображена на рис.4. Вариант 5 1. Y= tgx + |x| при X= – 2,6312; 2. Координаты исследуемой точки: (0,2; 0,45). Область изображена на рис.5. Вариант 6

1. 2111xx

Y ++= при X= – 0,387;

2. Координаты исследуемой точки: (0,5;– 2,5). Область изображена на рис.6. Вариант 7 1. Y=ch |x+1| при X= 4,352; 2. Координаты исследуемой точки: (0;0). Область изображена на рис.7. Вариант 8 1. Y=sinx + x2 при X= 0,112; 2. Координаты исследуемой точки: (1; 1,5). Область изображена на рис.8. Вариант 9 1. Y= sin arctgx при X= – 0,7129; 2. Координаты исследуемой точки: (– 0,5; 0,9). Область изображена на рис.9. Вариант 10 1. Y= 5arctgx при X= – 4,4172; 2. Координаты исследуемой точки: (1,5; 0). Область изображена на рис.10.

x

y

2 1 0 -1 -2 1

2

Рис. 1

x

y

-2-10-1-212

Рис. 2

x

y

-1

-1

1

Рис. 3

10

Page 24: Document2

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

24

5. Тема: Составной и условный операторы План занятия: 1. эксперименты с программами нахождения наибольшего (наименьшего) из двух и трех чи-

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

• Решение уравнений и неравенств (по вариантам).

Экспериментальный раздел работы 1. Вывести на экран наименьшее и наибольшее из двух данных чисел (см. Кни-

га 1, стр. 54: дадим имя программе pr5_1, сохраним под именем Prim5_1.Pas). Поставьте ‘;’ после оператора end перед else. Убедитесь, что появилась ошибка 'Error 113: Error in statement’. Конструкция (оператор) If - Then - Else неделима, поэтому разделитель ";" в ней недопустим.

x

y

21 0 1 1

2

Рис. 4

x

y

20-2

-1

1

Рис. 5

x

y

0

3

3-3

Рис.6

x

y

2 0 -2

1

Рис. 7

x

y

20

1

Рис. 8

x

y

0 3-3

Рис.9

x

y

2 1 0 -1 -2 1

2

Рис. 10

x

y

-2-10-1-212

Рис.11

x

y

-1

-1

1

Рис.12

10

Page 25: Document2

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

25

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

Программа определения значения наибольшего из трех чисел имеет вид: Program pr5_2; Var x, у, z : Integer; Begin WriteLn ( ' Введите три числа через пробел ' ); ReadLn (x, у, z);

(* Первый способ *) If (x>y) And (x>z) Then WriteLn (x) Else If (y>x) And (y>z) Then WriteLn (y) Else WriteLn (z);

(* Второй способ *) { If x>y Then If x>z Then WriteLn (x) Else WriteLn (z) Else If y>z Then WriteLn (y) Else WriteLn (z) ; }

(* Третий способ *) { ????????? } End. Вторая версия решения заключена в фигурные скобки. Уберите их, заключите первую часть программы в фигурные скобки и убедитесь в правильности второго решения. Измените про-грамму так, чтобы анализировался и случай равенства чисел. Обратите внимание на то, что при написании сложных условий простые условия заключаются в скобки. Это связано с тем, что операции сравнения имеют более низкий приоритет, чем логические. Третья, четвертая и т.д. версии решения – не реализованы. Заключите первую и вторую версии решения в фигурные скобки и допишите (* Третий способ *) фрагмент программы.

Задания для самостоятельной работы 1. Запишите условный оператор, в котором значение переменной с вычисляется по форму-

ле:

c = ⎩⎨⎧

•+

четное - а если b, a нечетное - a если , ba

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

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

Page 26: Document2

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

26

• оканчивается ли число цифрой 5. 4. Написать фрагмент программы, в котором подсчитывается сумма только положительных

чисел из трех данных. 5. Написать фрагмент программы, в котором подсчитывается количество четных чисел сре-

ди трех данных. 6. Дано трехзначное число. Написать программу определения, является ли оно палиндро-

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

7. Даны два целых числа М и N. Если М делится нацело на N, то вывести на экран частное от деления, в противном случае - сообщение "М на N нацело не делится".

8. Даны два вещественных числа. Уменьшить первое число в пять раз, если оно больше второго по абсолютной величине.

9. Вычислить значения функций: Y = ⎪⎩

⎪⎨

=

=>

0 xпри , 0 xпри , 5 0 xпри 12, -x

2x; Z =

⎩⎨⎧

<=>+

3 xпри , 8 -x 3 xпри 5,x 2

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

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

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

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

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

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

16. Числа х, y, z вводятся с клавиатуры. Составьте программу вычисления выражений: max(x + y + z, xyz) + 3; min (x2 + y2, y2 + z2) – 4.

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

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

19. Написать программу, определяющую, • попадает ли точка с данными координатами в заштрихованную область (Рис. 1). • принадлежит ли точка заштрихованной области (Рис. 2).

Рис. 1 Рис. 2

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

Page 27: Document2

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

27

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

Решение уравнений и неравенств

Цель работы: получение навыков в использовании условного оператора в программе. Постановка задачи:

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

Содержание отчета: 1. Постановка задачи. 2. Блок-схема алгоритма решения задачи. 3. Текст программы. 4. План отладки, тесты, результаты отладки на тестах.

Методические указания: 1. Программа должна правильно выполняться при любых значениях начальных данных. 2. Правильность работы всех ветвей программы должна быть проверена на тестах. 3. При отсутствии решения или бесчисленном множестве решений должен быть напечатан

соответствующий текст. Вариант 1: Решить уравнение ax2 + b = 0. Вариант 2: Решить уравнение ax2 + bx + c = 0 . Вариант 3: Решить неравенство ax2 + b > 0. Вариант 4: Решить неравенство ax2 + b ≤ 0 . Вариант 5: Решить неравенство ax2 + bx + c > 0 . Вариант 6: Решить неравенство ax2 + bx + c≤ 0.

Вариант 7: Решить систему уравнений ⎩⎨⎧

=++=++

0 c y b xa0 c y b xa

222

111

Решить систему неравенств

Вариант 8 ⎩⎨⎧

≥+≥+

0 b xa0 b xa

22

11 Вариант 9 ⎩⎨⎧

<+>+

0 b xa0 b xa

22

11 Вариант 10 ⎩⎨⎧

<+<+

0 b xa0 b xa

22

11

Материал для любознательных

1. Приведем одно из определений: «Программирование - теоретическая и практическая

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

Page 28: Document2

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

28

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

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

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

Простая 1000 Средней сложности 10 000

Сложная 100 000 Сверхсложная 1 000 000 Гиперсложная 10 000 000 и более

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

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

Page 29: Document2

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

29

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

Этот этап развития технологий программирования характерен для ЭВМ первого поколе-ния (с 1945-го по 1959 год). Быстродействие ЭВМ этого поколения составляло до 50 тысяч арифметических операций, объем оперативной памяти - в лучшем случае несколько кило-байт ячеек. Ресурсы минимальны. Если сравнивать эти характеристики с современными компьютерами: быстродействие - миллиарды операций в секунду, объемы памяти - мегабай-ты, - то различие поразительно. ЭВМ того времени понимала только цифровые команды, и программа состояла из множества строк, состоящих из цифр, интерпретируемых цен-тральным процессором. Например, 05 825 631 трактовалось как команда сложения двух чисел (код 05), записанных в ячейки с номерами 825 и 631. Минимальные ресурсы ЭВМ требовали строжайшей экономии оперативной памяти и эффективных алгорит-мов обработки. Программы по взаимосвязи составных частей обычно напоминали спа-гетти. Надо отдать должное программистам того времени. Производительность их труда бы-ла очень невелика, так как вручную необходимо было распределить все переменные про-граммы в оперативной памяти (!). Следующий этап развития технологий программирования мало отличается от первого. Он связан с ЭВМ второго поколения. Появились языки программирования типа ассемблера и автокода. Теперь команда сложения записывалась с использованием мнемоники - ADD (сложить) PR1, ZET, где ADD - код команды, PR1, ZET - имена ячеек. Перевод программы (трансляция), записанной таким образом в цифровое представление, а только такое понимает ЭВМ, осуществлялся с помощью специальных программ, назы-ваемых ассемблерами. Программа "собирается" из мелких деталей, отдельных операций и имеет достаточно простую структуру. Уровень абстрагирования - отдельное действие, прин-ципы декомпозиции задачи отсутствуют, во всяком случае, о них не говорят. Существует разрыв между требованиями практики и возможностями программирования. Круг решаемых с помощью ЭВМ задач достаточно ограничен - в основном это расчетные задачи. 6. Тема: Оператор For План занятия: 1. разбор программ:

• проверить, является ли данное число палиндромом; • выяснить, есть ли в записи числа ровно три одинаковые цифры.

2. выполнение самостоятельной работы: • Построение таблиц функций одной переменной.

Рекомендации по использованию оператора For. Оператор For применяют

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

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

Page 30: Document2

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

30

1. Дано натуральное число п (п <= 9999). Определить, является ли оно па-

линдромом («перевертышем» ), с учетом четырех цифр (например, палин-дромами являются числа: 2222; 6161; 0440). Имеем четырехзначное число, поэтому переменная оператора For изменяет-

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

i т R - 3994 0 1 399 0 • 10 + 3994 mod 10 = 0 + 4 = 4 2 39 4 • 10 + 399 mod 10 = 40 + 9 = 49 3 3 49 • 10 + 39 mod 10 = 490 + 9 = 499 4 0 499 • 10+3 mod 10 = 4990 + 3 = 4993

Program pr6_1; Var n, m, r, i : Integer; Begin WriteLn ('Введите целое число, меньшее 10 000'); ReadLn (n); m: =n; r := 0; For i:=1 To 4 Do Begin {так как число четырехзначное} r:=r*10+m Mod 10; m:=m Div 10 End; If r=n Then WriteLn ('ДА') Else WriteLn ('НЕТ'); ReadLn End.

Измените программу так, чтобы можно было обрабатывать целые числа из диапазона LongInt. 2. Даны натуральные числа п, k (п, k <= 9999). Из чисел от п до k выбрать те,

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

Пусть в качестве п и k введены числа 3732 и 3740. В переменных a1, a2

a3, a4 будем хранить значения цифр текущего числа i.

Номер цифры 1 2 3 4 1 X X X первое условие 2 X X X второе условие 3 X X X третье условие 4 X X X Четвертое условие

Page 31: Document2

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

31

i а1 а2 аЗ а4 Результат срав-

нения 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 pr6_2; Var n, k, i, a1, a2, a3, a4, m :Integer; Begin WriteLn ('Введите два числа, не больших 9999'); ReadLn (n, k) ; For i:=n To k Do Begin m : = i ;

{выделение цифр: a1 - первая, a2 - вторая, аЗ - третья, а4 - четвертая} a4:=m Mod 10; m:=m Div 10; a3:=m Mod 10; m:=m Div 10; a2:=m Mod 10; a1:=m Div 10; If ((a1=a2) And (a1=a3) And (a1<>a4)) {1-е условие}

Or ((a1=a2) And (a1=a4) And (a1<>a3)) {2-е условие} Or ((a1=a3) And (a1 = a4) And (a1<>a2)) {3-е условие} Or ((a2=a3) And (a2=a4) And (a2<>a1)) {4-е условие} Then WriteLn (i : 5) End; End. Измените программу для обработки 4, 5 или 6-значных чисел. Если ваше

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

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

1. Найти все двузначные числа, которые делятся на N или содержат цифру N. 2. Составить программу вычисления значения выражения у = ((...(202 - 192)2 - 182)2 - ... -

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

закономерность: 12= 1; 22 = 1 + 3; 32 = 1 + 3+ 5; 42 =1+3 + 5 + 7; ….n2= 1 + 3+ 5+ 7+ 9+ ... + (2n - 1)

4. Определить количество трехзначных натуральных чисел, сумма цифр которых равна заданному числу N.

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

Page 32: Document2

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

32

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

вию: если к сумме цифр числа прибавить квадрат этой суммы, то получится само чис-ло.

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

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

10. Найти сумму положительных нечетных чисел, меньших 100. 11. Найти сумму целых положительных чисел из промежутка от А до В, кратных 4 (значе-

ния переменных А и В вводятся с клавиатуры). 12. Найти сумму целых положительных чисел, больших 20, меньших 100, кратных 3 и за-

канчивающихся на 2, 4 или 8. 13. В трехзначном числе зачеркнули старшую цифру, когда полученное двузначное число

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

такие числа. 15. Среди четырехзначных чисел выбрать те, у которых все четыре цифры различны. 16. Среди двузначных чисел найти те, которые делятся на число q, а сумма их цифр равна

п (0 < п <= 18). 17. Дано четырехзначное число n. Выбросить из записи числа п цифры 0 и 5, оставив

прежним порядок остальных цифр. Например, из числа 1509 должно получиться 19. 18. Натуральное число из п цифр является числом Армстронга, если сумма его цифр, воз-

веденных в n-ю степень, равна самому числу (например, 153 = 13 + 53 + З3). Получить все трех- и четырехзначные числа Армстронга.

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

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

Построение таблиц функций Цель задания: получение навыков в использовании оператора FOR.

Постановка задачи: Cоставить таблицу значений функции f(x) на отрезке [a,b] в точках x=a+ih, где h=(b – a) / m, m - заданное целое число (i = 0, ... , m).

Содержание отчета: 1) постановка задачи; 2) текст программы: 3) таблица результатов. Методические указания:

1) Для задания значений x и соответствующих значений функции следует использовать простые переменные.

2) Значение шага h должно вычисляться один раз.

№ вар.

Функция отрезок Кол-во точек

1: f(x) = arcsinx, [0;1], m = 20. 2: f(x) = arccosx, [0.5;1], m = 10. 3: f(x) = ctgx, [π/4 ; π/2 ], m = 15. 4: f(x) = tgx, [0;π/4], m = 10. 5: f(x) = sinx + cosx, [0; π/4], m = 20. 6: f(x) = cosx + ctgx, [π/4; π/2], m = 10. 7: f(x) = tg(x/2), [0; 2π/3], m = 15. 8: f(x) = tg(x/2)+cosx, [π/2; π], m = 20. 9: f(x) = ctg(x/3)+sinx, [π/4; π/2], m = 10.

Page 33: Document2

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

33

10: f(x) = arctgx, [2;7], m = 15. 11: f(x) = arcsinx, [0;1], m = 20. 12: f(x) = arccosx, [0.5;1], m = 10.

Материал для любознательных

Из истории программирования. О нисходящем проектировании программ, струк-

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

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

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

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

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

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

Для реализации программ требуются три основных составляющих блока (см. «Алгоритмы и данные», стр. 26 -31). Функциональный блок, или конструкция следования (а); конструк-ция обобщенного цикла (б); конструкция принятия двоичного, или дихотомического, реше-ния (в).

Page 34: Document2

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

34

а) б) в)

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

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

ния; • содержательность имен переменных.

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

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

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

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

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

Page 35: Document2

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

35

7. Тема: Оператор While План занятия: 1. обсуждение оператора (см. стр. 56 – книга 1); 2. эксперименты с программами:

• определения количества цифр в числе; • преобразования натурального числа n в 1.

3. выполнение самостоятельной работы: • Построение таблиц функций одной переменной.

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

1. Дано натуральное число n. Подсчитать количество цифр данного числа.

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

k m 0 65387 1 6538 2 653 3 65 4 6 5 0

Значение переменной k равно 5, в числе 5 цифр. После этого работаем с про-граммой pr7_1 по традиционной схеме: набор, компиляция, сохранение, за-пуск и проверка работы на конкретных примерах. Program pr7_l; Var m, n : LongInt; k: integer; Begin WriteLn ('Введите натуральное число n<>0 ' ); ReadLn (n); m:=n; k:=0; While m<>0 do begin Inc(k); m:=m div 10; end; WriteLn ('В числе ' ,n, ' - ' ,k, ' цифр'); ReadLn End.

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

Page 36: Document2

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

36

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

2. В программе pr7_2 реализована «гипотеза Сиракуз». Осуществляется после-довательное преобразование натурального числа n в 1. Запустите программу, проверьте ее работу при нескольких значениях n. По-

лучив результат можем предположить, что при любом значении n работа про-граммы завершится, т.е. будет получен результат. Однако, доказательства факта завершения работы программы (алгоритма) при любом значении n до настоя-щего времени никем не получено. Проверка циклов типа While требует особо тщательной работы, так как циклы этого типа «потенциально бесконеч-ны по времени». Program pr7_2; Var n : Integer; Begin WriteLn ('Введите натуральное число n: ' ); ReadLn (n); Write(n); While n<>1 do begin If n mod 2 = 0 then n:=n div 2 Else n:=(3*n+1) div 2; Write(‘ – ‘,n); end; ReadLn End.

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

Задания для самостоятельной работы 1. Дана последовательность операторов:

a:=1; b:=1; While a+b<8 do begin a:=1; b:=b+2 end; S:=a+b Сколько раз выполняется проверка логического выражения в операторе While? Опреде-лите значения переменных a, b, и S после завершения этой последовательности операто-ров?

2. Определите значения переменных a и b после выполнения операторов: a:=1; b:=1; While a<=3 do a:=a+1; b:=b+1.

3. Определите значение переменной s после выполнения следующих операторов: • S:=0; I:=0; While I<5 do inc(i); s:=a+100 div i ;

Page 37: Document2

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

37

• S:=0; I:=1; While I>1 do begin s:=s+100 div I; dec(i) end; 4. Дан фрагмент программы с ошибками (их не больше 5) вычисления факториала f числа n:

k:=1; f:=0; While k<n do f=f*k k:=k+1; Найдите эти ошибки. 5. Найдите и исправьте ошибки в следующем фрагменте программы, определяющей для за-

данного натурального числа n число, записанное цифрами в обратном порядке. p:=n; While p>=0 do begin a:=a+p mod 10; p:=p div 10 end;

6. Найти минимальное число, большее 300, которое нацело делится на 19. 7. Приписать по 1 в начало и в конец записи числа n. Например, было n =3456, стало

n=134561. 8. Поменять местами первую и последнюю цифры числа. Например, из числа 8547 должно

быть получено число 7548. 9. Приписать к исходному числу n такое же число. Например, из числа 1903 должно быть

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

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

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

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

0 – «нет») 14. Выяснить, сколько раз в натуральном числе встречается его максимальная цифра. На-

пример, в числе 581088 – 3 раза, в числе 4537 – 1 раз. 15. Выяснить, является ли разность максимальной и минимальной цифр числа четной.

Построение таблиц функций

Постановка задачи:

Составить таблицу значений функции f(x,y) для значений х на отрезке [a,b] с шагом h1 и для значений y на отрезке [c,d] с шагом h2.

Содержание отчета: 1) постановка задачи; 2) текст программы; 3) таблица результатов. Методические указания: 1. Для задания значений x, y и соответствующих значений функции следует использовать

простые переменные. 2. Значения x расположить горизонтально, значения y - вертикально, а значения функ-

ции, округленные до четырех верных знаков, - на пересечении соответствующих строк и столбцов.

3. Если функция не определена в точке, то вместо значения функции вывести символ “*”. Задание 1: f(x,y) =x ln(x+y), [0;10], h1=1, [0;6], h2=2. Задание 2: f(x,y) =sinxy / xy, [-5;5], h1=1, [2;5], h2=1. Задание 3: f(x,y) = (ex+y – 1) /(x+y), [0;5], h1=0.5, [3;6], h2=1. Задание 4: f(x,y) = (x3 – 1) / (y3 – 1 ), [0;10], h1=1, [0;6], h2=2. Задание 5: f(x,y) =y lnxy, [1; 21], h1=2, [0;3], h2=1.

Page 38: Document2

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

38

Задание 6: f(x,y) =sinln(x2 + y2), [0;10], h1=1, [1;2.5], h2=0.5 . Задание 7: f(x,y) =y ln(1 +x2) / (x2 +y2), [-5;5], h1=1, [0;1.5], h2=0.5 . Задание 8: f(x,y) =cos(x+y) /(x2 – 1), [-10;10], h1=2, [0; 1.5], h2=0.5. Задание 9: f(x,y) = (x3 +y3) / (ex –1), [-2;2], h1=0.4, [-1;0.5], h2=0.5. Задание 10: f(x,y) =xy / ln(1 +x2+y2), [0;10], h1=1, [0;3], h2=1.

Материал для любознательных

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

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

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

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

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

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

Page 39: Document2

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

39

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

8. Тема: Оператор Repeat... Until План занятия: 1. обсуждение оператора (см. стр. 57 – книга 1); 2. эксперименты с программами:

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

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

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

Натуральное число р называется простым, если оно делится только на 1 и на себя. По со-глашению 1 не считают простым числом. Начало последовательности простых чисел имеет вид: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, ... 1. В программе pr8_1 определяется, является ли данное число простым. Мы

ищем делители числа п в интервале от 2 до п div 2, хотя можно было бы ог-раничиться интервалом от 2 до целой части n (почему?).

Program pr8_l; Var i, n : LongInt; Begin WriteLn ('Введите натуральное число'); ReadLn (n); i : = l; Repeat I:=I+1; Until (i > n div 2) Or (n mod i = 0); If i > n div 2 Then Writeln (' Число ',n, ' простое '} Else WriteLn ('Число ' ,i, ' - первый делитель числа ' ,n, ', больший 1'); End.

Эту задачу можно решить и с использованием оператора While (см. преды-дущее занятие). Сделайте это. Затем измените программу так, чтобы в ней осу-ществлялся вывод всех делителей числа п. Подсказка. Логическое выражение в операторе Repeat ... Until упростится, в нем останется только условие i > n div 2, а в теле цикла появится оператор If n mod i = 0 Then WriteLn (..., i).

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

Page 40: Document2

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

40

Выберите интервал чисел (не очень большой, например, от 101 до 120) и, используя модифицированную программу pr1_8, найдите разложения этих чи-сел на произведения простых. Ваши записи должны иметь вид: n = p1

a1 • p2a2 • ...

• pqaq - где (ai >= 0), рi - простые числа, например, 168= 23 • 31 • 50 • 71.

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

a1 • p2a2 • ... • pq

aq и b = p1b1 • p2

b2 • ... • prbr - а затем найти НОД (а,

b) в виде: p1min(a1,b1) • p2

min(a2,b2) • ... • pmmin(am,bm).

Рассмотрим способ, который называется алгоритмом Евклида (см. книгу «Алгоритмы и данные»). Пусть а и b одновременно не равные нулю целые не-отрицательные числа и a >= b. Если b = 0, то НОД (а, b) = a, а если b <>0, то для чисел а, b и r, где r - остаток от деления а на b, выполняется равенство НОД (а, b) = НОД (b, r). Действительно, r = a mod b = а - (a div b) • b. Если какое-то число делит нацело и а, и b, то из приведенного равенства следует, что оно де-лит нацело и числа r и b. Например, пусть а = 48, а b = 18. Ручная трассировка алгоритма Евклида:

a B Результаты 48 18 — 48 mod 18 =12 18 НОД (48, 18) = НОД (12, 18) 12 18 mod 12 = 6 НОД (12, 18) = НОД (12, б) 12 mod 6 = 0 6 НОД (12, 6) = НОД (0, 6) 0 6 НОД (0, 6) = 6

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

Repeat ... Until имеет вид: Program pr8_2; Var a, b: LongInt; Begin WriteLn ('Введите два числа <>0'); ReadLn (a, b) ; Repeat If a>b Then a:=a mod b Else b:=b mod a Until (a=0) or (b=0) ; WriteLn ('НОД=', а+b) ; ReadLn End.

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

Элементы еi последовательности е1 = 1 + 1 = 2, е2 = 2 + 1 = 3, е3 = 2 • 3 + 1 = 7, е4 = 2 • 3 • 7 + 1 = 43, е5 = 2 • 3 • 7 • 43 + 1 = 1807, е6 = 2 • 3 • 7 • 1807 + 1 = 3263443, е7 = 2 • 3 • 7 • 1807 • 3263443 + 1 = 547 • 607 • 1033 • 31051 и т.д.

Page 41: Document2

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

41

называют числами Евклида. По первым четырем числам кажется, что числа Евклида простые. Однако это не так, уже е5 является составным -1807 = 13 • 139. Известно, что евклидовы числа взаимно простые - НОД(ei, еj) = 1 при i≠j. Проверьте этот факт с помощью программы pr8_2 для первых шести чи-сел Евклида. Используя программу pr8_1, покажите, что число е6 простое. 3. В теории чисел доказывается следующая теорема. Если а и b одновременно

не равны нулю, то существуют целые числа х и у, такие, что НОД(а, b) = а • х + b • у. Теорема не утверждает, что х и у определены однозначно, в ней лишь говорится о том, что НОД(а, b) может быть выражен в таком виде. На-пример, 6 = НОД (12, -30) = 12 • 3 + (-30) •1 = 12*(-2)+(-30)*(-1).

Рассмотрим последовательность: * a0 = a , a0 = a • x0 + b • y0, где x0 = 1, y0 = 0; * a1 = b, a1 = a • x1 + b • y1, где x1 = 0, y1 = 1; * a2 = a0 - a1• q1, где q1 = a0 div a1; подставляя значения a0 и a1 получаем: a2 = a • x0 + b • y0 - (a • x1 + b • y1) • q1 = a • (x0 - x1 • q1) + b • ( y0 - y1 • q1) = a • x2 + b • y2

* a3 = a1 - a2 • q2, где q2 = a1 div a2 подставляя значения a1 и a2 получаем: a3 = a • x1 + b • y1 - (a • x2 + b • y2) • q2 = a • (x1 - x2 • q2) + b •(y1 - y2 • q2) = a • x3 + b • y3; * ... * ai = ai-2 - ai-1 • qi-1, где qi-1 = ai-2 div ai-1, ai = a • xi-2 + b • yi-2 - (a • xi-1 + b • yi-1) • qi-

1 = a • xi + b•yi; * ... * 0 = ak-1 - ak • qk, где qk = ak-1 div ak, ... 0 = a • xk+1 + b • yk+1.

Значения qi, ai, xi , yi, определяются рекуррентными соотношениями: qi-1 = ai-2 div ai-1; ai = ai-2 - ai-1 • qi-1; xi = xi-2 - xi-1 • qi-1; yi = yi-2 - yi-1 • qi-1;

Рассмотрим данный процесс на примере чисел а = 48 и b= 18. i ai xi

yi qi 0 48 1 0 - 1 18 0 1 - 2 12 1 -2 2 3 6 -1 3 2 4 0 - - -

d = НОД(48,18) = 6 и 6 = 48 • (-1)+18 • 3. Описанный алгоритм реализован в программе pr8_3. Program pr8_3; Var a, b, a0, a1, x0, x1, y0, y1, q, t : Integer; Begin WriteLn('Введите два числа, первое должно быть больше второго '); ReadLn(а, b) ; a0:=a; a1:=b; x0:=1; x1:=0; у0:=0; у1:=1; Repeat q:=a0 div a1; t:=a0; a0:=a1; a1:=t-a1*q; t:=x0; x0:=x1; x1:=t-x1*q;

Page 42: Document2

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

42

t:=y0; y0:=y1; y1:=t-y1*q Until a1=0; WriteLn (a0, '=',а, '*(', x0,') + ',b,' * (', y0, ') '); ReadLn End. Сравните результаты, полученные с помощью ручной трассировки, с резуль-татами работы программы при тех же исходных данных.

Задания для самостоятельной работы 1. Числа вида 2P - 1, где р - простое число, называются числами M.Мерсенна (1588 -1648гг.).

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

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

3. Если a и b - ненулевые числа, то их наименьшее общее кратное существует и справедли-во равенство НОК(а, b) = |а • b| div НОД(a, b). Написать программу нахождения НОК двух ненулевых чисел. Пусть а и b - ненулевые целые числа. Целое число т > 0 называ-ется наименьшим общим кратным (НОК) чисел а и b, если т делится и на а, и на b наце-ло, а также любое с, которое делится нацело и на a, и на b, делится нацело и на т.

4. Числа Фибоначчи (fn) определяются формулами: f0 = f1 = 1; fn = fn-1 + fn-2 при n >= 2. Нача-ло последовательности Фибоначчи имеет вид: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,... Соста-вить программы: o определения номера последнего числа Фибоначчи, которое входит в диапазон типа

Integer (LongInt); o вычисления s - суммы первых чисел Фибоначчи, таких, что значение s не превышает

диапазона типа Integer (LongInt). 5. Составьте программу, проверяющую, является ли заданное натуральное число со-

вершенным. Совершенным называется число, равное сумме всех своих делителей, меньших, чем оно само. Например: 6 = 1 + 2 + 3, 28 = 1 + 2 + 4 + 7 + 14. (Примечание. Евклидом доказано, что каждое число вида 2p-1• (2p - 1) является совершенным, если (2p – 1) - простое число. Л. Эйлер доказал, что все четные совершенные числа находятся по формуле Евклида, а относительно нечетных совершенных чисел ничего неизвестно до сих пор.

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

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

Page 43: Document2

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

43

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

Материал для любознательных

1. Познакомимся с понятием модели жизненного цикла программы. В настоящее время

понятие жизненного цикла программы является одним из базовых понятий в технологии программирования. Жизненный цикл программы начинается с момента принятия реше-ния о необходимости ее создания и заканчивается в момент полного изъятия программы из эксплуатации. Основным нормативным документом, регламентирующим жизненный цикл программы, является международный стандарт ISO / IEC 12207 (ISO - International Organisation of Standardisation - Международная организация по стандартизации, IEC - International Electrotechnical Commission - Международная комиссия по электротехнике). Он определяет структуру жизненного цикла, которая базируется натрех группахпроцес-сов: 1) основные процессы (приобретение, поставка, разработка, эксплуатация, сопро-вождение); 2) вспомогательные процессы, обеспечивающие выполнение основных про-цессов (документирование, управление конфигурацией, обеспечение качества, верифика-ция, аттестация, оценка, аудит); 3) организационные процессы (управление проектами, создание инфраструктуры проекта, обучение). К настоящему времени известны две ос-новные модели жизненного цикла: каскадная и спиральная. Каскадный подход хо-рошо зарекомендовал себя при построении программ, к которым в самом начале раз-работки можно достаточно точно и полно сформулировать все требования. Пример - расчетные задачи. В случае неточного изложения требований или их изменения получа-ется программа, не отвечающая потребностям пользователя, заказчика. Для преодоления недостатков каскадной схемы была предложена спиральная модель жизненного цикла программы. Каждый виток спирали соответствует созданию очередной версии програм-мы. При этом уточняются цели и требования, определяется качество и планируются ра-боты по созданию следующей версии. Таким образом, последовательно конкретизируют-ся детали программы и выбирается окончательная версия, которая доводится до реализа-ции. Сходящий вид спирали подчеркивает "размытость" первоначальных требований и их последующее уточнение.

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

3. Команда меню Функциональная

клавиша Назначение

Run/Run <Ctrl> + <F9> Запуск программы

Page 44: Document2

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

44

Run/Go to cursor <F4> Выполняет программу до строки, в которой находится курсор

Run/Trace into <F7> Выполняет оператор (операторы), записанный в теку-щей строке. Если в этой строке записан вызов проце-дуры или функции, то осуществляется вход в эту про-цедуру или функцию

Run/Step over <F8> Выполняется оператор, записанный в текущей строке без захода в процедуры или функции

Debug/Watch

Открывает окно для наблюдения за значениями пере-менных (окно Watches)

Debug/ Breakpoints

Открывает окно для работы с точками останова

Debug/Evaluate... <Ctrl> + <F4> Открывается окно Evaluate and Modify. В нем можно вычислить значение выражения (в выражении могут использоваться и переменные программы)

Debug/Add watch

<Ctrl> + <F7> Открывает окно Add watch для ввода отслеживаемых значений выражений или переменных. Эти действия можно выполнить и другим способом: сделать окно Watch активным и использовать клавиши <Insert> и <Delete> для вставки и удаления выражений и пере-менных

Debug/Add breakpoint

<Ctrl> + <F8> Текущая строка (в которой находится курсор) стано-вится точкой останова программы

9. Тема: Вложенные циклы План занятия: 1. обсуждение конструкции; 2. эксперименты с программами (с использованием отладчика системы программирования)

• вычисления выражения 1k + 2k + ... + nk ; • нахождения цифрового корня числа; • решения старинной задачи о быках, коровах и телятах; • нахождения натуральных чисел, удовлетворяющих определенному условию; сведение

чисел, кратных 3, к числу 153; 3. выполнение самостоятельной работы.

Для решения задачи достаточно часто требуется использовать две и бо-

лее циклические конструкции, одна из которых расположена внутри дру-гой (других). Такие конструкции называют вложенными циклами. Какие именно циклы при этом используются, роли не играет, они могут быть ор-ганизованы посредством любых рассмотренных ранее операторов (For, While, Repeat ... Until).

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

Page 45: Document2

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

45

1. Даны натуральные числа n и k. Составить программу вычисления выраже-ния 1 k + 2 k + ... + n k . Для вычисления указанной суммы целесообразно ис-пользовать оператор For с управляющей переменной i, изменяющейся от 1 до n. В теле цикла вычисляется очередное значение y = ik и накапливается искомая сумма s = s + y.

Program pr9_1; Var n, k, y, i, s, j : Integer; Begin WriteLn ('Введите n и k '); ReadLn (n, k); s:=0; For i:=1 To n Do Begin y:=1; For j:=1 To k Do y:=y*i; s:=s+y End; WriteLn ('Сумма: ',s) End.

Попробуем работать с отладчиком системы программирования. Вспомните материал для чтения предыдущего занятия, пункт 4. • Выполните команду Debug/Watch. ПоявитсяокноWatches. • Используя клавишу <Insert> (при этом открывается окно Add Watch), введи-

те переменные программы (n, k, s, i, j, y). • Измените положение и размер окна Watches на экране. Оно должно нахо-

диться в правом верхнем углу и быть таким, чтобы все введенные перемен-ные были видны. С этой целью выполните команду Window/Size/Move ( <Ctrl> + <F5> ) и используйте клавиши <Shift> + <←> ,<→> , <↓>, < > для изменения размеров окна и клавиши <←> ,<→> , <↓>, < > для его переме-щения.

• Выполните команду Run/Step over (<F8>). При этом строка с оператором Begin выделяется другим цветом. При нажатии на <F8> управление переда-ется на следующую строку (эта строка становится выделенной).

• Выполните программу в пошаговом режиме (посредством последовательных нажатий на клавишу <F8>), отслеживая изменение значений переменных в окне Watches. При выполнении оператора ReadLn (n,k) введите, например, значения 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 46: Document2

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

46

Методические рекомендации: Рекомендуется максимально использовать отладчик системы программирования при выполнении заданий этого и следующих занятий. 2. Измените программу так, чтобы в ней вычислялась сумма 11 + 22 + ... + nn . 3. Пусть значение k зафиксировано, например, равно 5. Требуется определить

значение n, при котором диапазона целого типа данных Integer окажется не-достаточно для хранения суммы степеней чисел.

4. Сложим все цифры какого-либо числа. Получим новое число, равное сумме всех цифр исходного числа. Продолжим этот процесс до тех пор, пока не получим однозначное число (цифру). Оно называется цифровым корнем исходного числа. Например, цифровой корень числа 34 697 равен 2 (3 + 4 + 6 + 9 + 7 = 29; 2 + 9 = 11; 1 + 1 = 2). Составьте программу нахождения цифрового корня натурального числа.

Program pr9_2; Var n, k, s : LongInt; Begin WriteLn ('Введите число'); ReadLn(n); s:=n; While s>9 Do Begin k:=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 голов скота.

Решение. Обозначим через b - количество быков, k - количество коров, t - ко-личество телят. После этого можно записать два уравнения: 10b + 5k + 0.5t = 100 и b + k + t = 100. Преобразуем их: 20b + 10k + t = 200 и b + k + t = 100. На 100 рублей можно купить:

• не более 10 быков, т.е. 0 ≤b ≤10, • неболее20коров,т.е.0≤k≤20, • не более 200 телят, т.е. 0 ≤t ≤200. Таким образом, получаем:

Program pr9_3; Var b, k, t: Integer; Begin For b:=0 To 10 Do For k:=0 To 20 Do For t:=0 To 200 Do If (20*b+10*k+t=200) and (b+k+t=100) Then WriteLn ('быков ',b,' коров ',k,'телят ',t) End.

Page 47: Document2

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

47

Сколько раз будет проверяться условие в данной программе (сколько раз будет выполняться оператор If)? Переменная b принимает 11 различных значений (от 0 до 10), для каждого значения переменной b переменная k изменяется от 0 до 20, а для каждого значения переменной k переменная t изменяется от 0 до 200. Таким образом, условие будет проверяться 11•21•201=46431 раз. Но если из-вестно количество быков и коров, то количество телят можно вычислить по формуле t = 100 - (b + k) и цикл по переменной t можно исключить. Program Pr9_3m; Var b, k, t: Integer; Begin For b:=0 To 10 Do For k:=0 To 20 Do Begin t:=100-(b+k); If (20*b+10*k+t=200) Then WriteLn ('быков', b,' коров ',k,' телят ',t) End End.

В этой программе условие проверяется 11•21 = 231 раз. Попробуйте еще уменьшить количество проверок. 6. Написать программу, которая находит все четырехзначные числа abcd (a, b,

c, d - цифры числа, причем все они различны), для которых выполняется ус-ловие: ab - cd = a + b + c + d. Другими словами, разность чисел, состав-ленных из старших цифр числа и из младших, должна быть равна сум-ме цифр числа. Эту задачу можно решать разными способами. Например, можно перебирать все четырехзначные числа и проверять выполнение ус-ловия. Попробуем сократить перебор: из равенства 10*a+b-(10*c+d)=a+b+c+d получаем 9•(a - c) = 2•(c + d) или (a-c)/(c + d) = 2/9, a = c + 2, d = 9 - c и 0 ≤ c ≤7.

Program pr9_4; Var a, b, c, d:Integer; Begin For c:=0 To 7 Do Begin a:=c+2; d:=9-c; For b:=0 To 9 Do If (b<>c)and(b<>a)and(b<>d) Then WriteLn (a,b,c,d) End End. 7. Дано натуральное число, кратное 3. Найдем сумму кубов цифр данного

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

Program pr9_5; Var n, m, t, s, q:LongInt; Begin WriteLn ('Введите число n=’); ReadLn(n);

Page 48: Document2

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

48

Writeln (‘оно умножается на 3 ‘); m:=3*n;Write(m,'':3); Writeln(‘ далее для полученного числа строится последовательность'); Repeat s:=0;t:=m; While m>0 Do Begin q:=m mod 10; s:=s+q*q*q; m:=m div 10 End; m:=s; Write (m,'':3) Until m=t; writeln; ReadLn End.

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

1. Что будет напечатано в результате выполнения следующего фрагмента программы: a:=1; b:=1; For i:=0 To n Do Begin For j:=1 To b Do Write ('*'); WriteLn; c:=a+b; a:=b; b:=c End; если n = 6? Решение какой задачи выражает этот фрагмент программы? 2. Что будет напечатано в результате выполнения следующего фрагмента программы: b:=0; While a<>0 Do Begin b:=b*10+a mod 10 a:=a div 10; End; Write (b); если а = 13305? Решение какой задачи выражает этот фрагмент программы? 3. Дано натуральное число q. Требуется написать программу для нахождения всех прямо-

угольников, площадь которых равна q и стороны выражены натуральными числами. 4. Составить программу для иллюстрации делимости чисел от 1 до n. В каждой строке надо

печатать число и столько плюсов, сколько делителей у этого числа. Например, если n = 4, то на экране должно быть напечатано: 1+ 2++ 3++ 4+++

5. Дано натуральное число n. Требуется выяснить, можно ли представить его в виде суммы квадратов трех натуральных чисел? Если можно, то: • указать тройку x, y, z таких натуральных чисел, что x2 + y2 + z2 = n; • указать все тройки x, y, z таких натуральных чисел, что x2 + y2 + z2 = n.

6. Найти натуральное число от 1 до 10 000 с максимальной суммой делителей. 7. Даны натуральные числа a, b (a < b). Получить все простые числа p, удовлетворяющие

неравенствам: a ≤ p≤ b. 8. Даны натуральные числа n, m. Получить все натуральные числа, меньшие n, квадрат

суммы цифр которых равен m. 9. Даны натуральные числа n и m. Найти все пары дружественных чисел, лежащих в диапа-

зоне от n до m. Два числа называются дружественными, если каждое из них равно сумме всех делителей другого (само число в указанную сумму не входит).

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

11. Составить программу, печатающую k-ю цифру последовательности: • 12345678910..., в которой выписаны подряд все натуральные числа;

Page 49: Document2

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

49

• 14916253649..., в которой выписаны подряд квадраты всех натуральных чисел; • 1123581321.…, в которой выписаны подряд все числа Фибоначчи.

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

13 = 1 23 = 3 + 5 33 = 7 + 9 + 11 43 = 13 + 15 + 17 + 19 53 = 21 + 23 + 25 + 27 + 29 13. Составить программу для нахождения всех натуральных чисел n, m, k , удовлетворяю-

щих соотношению n2 + m2 = k2 из интервала [1, 10] . Примечание. Решения, которые по-лучаются перестановкой n и m, считать совпадающими.

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

“да”, то найти это число; • определить, является ли заданное число степенью числа 3; • вычислить 1! + 2! + 3! + ... + n!; • вычислить xn , где n - целое положительное число; • вычислить значения x1 , x2 , x3 , ..., xn .

15. Дано трехзначное число. Найти все трехзначные числа, удовлетворяющие каждому из условий: • состоящих из тех же цифр, что и исходное число; • равные среднему арифметическому всех трехзначных чисел (включая данное), со-

стоящих из тех же цифр. 16. Стороны прямоугольника заданы натуральными числами M и N. Составить программу,

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

17. Даны натуральные числа N и p. Получить все натуральные числа, меньшие N и взаимно простые с p.

18. Даны целые числа p и q. Получить все делители числа q, взаимно простые с p. 19. Сумма квадратов длин катетов a и b прямоугольного треугольника равна квадрату длины

гипотенузы с: a2 + b2 = c2 . Тройка натуральных чисел, удовлетворяющих этому равенст-ву, называется пифагоровой. Составить программу нахождения пифагоровых троек, ис-пользуя следующие формулы: a = u•v; b = (u•u - v•v) div 2, c = (u•u + v•v)div 2, - где u и v - взаимно простые нечетные натуральные числа и u > v.

20. Найти наименьшее натуральное число N, представимое двумя различными способами в виде суммы кубов двух натуральных чисел x и y (x ≥y).

21. Даны натуральные числа m, n1 , n2 , ..., nm (m ≥2). Вычислить НОД (n1 , n2 , ..., nm ), вос-пользовавшись соотношением НОД (n1 , n2 , ..., nm ) = НОД (НОД (n1 , n2 , ..., nm-1 ), nm ) и алгоритмом Евклида.

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

Page 50: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

50

10. Тема: Одномерные массивы. Работа с элементами План занятия: 1. раздел описания типов данных (см. книга 2, стр. 7 - 25); 2. простой пример нахождения суммы чисел; 3. экспериментальная работа с программами:

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

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

Описание типов данных На предыдущих занятиях мы рассматривали только три типа данных:

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

Type <имя_типа>=<тип_данных>; После этого в разделе описаний переменных можно описывать переменные

данного типа: Var <имя_переменной>:<имя_типа>; Рассмотрим простой пример. Необходимо найти сумму пяти целых чисел. Program Pr10_1; Var a1,a2,a3,a4,a5,s: Integer; Begin WriteLn ('Введите пять целых чисел'); ReadLn(a1,a2,a3,a4,a5); s:=a1+a2+a3+a4+a5; WriteLn ('Их сумма равна ',s) ; ReadLn End.

Аналогичное решение для нахождения суммы 30 целых чисел потребует введения 30 однотипных переменных.

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

Для решения нашей задачи потребуется массив из 30 целых чисел. Опишем новый тип — одномерный массив, состоящий из 30 целых чисел.

Type Vector = Array [1..30] Of Integer; Раздел описания типов начинается со служебного слова Type, далее пере-

числяются имена типов и их описания. Между именем типа и его описанием

Page 51: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

51

ставится знак “=” (в разделе переменных между именем переменной и ее опи-санием ставится двоеточие). В нашем случае Vector — имя нового типа данных; Array — служебное слово (в переводе с английского означает “массив”, “на-бор”); [1..30] — в квадратных скобках указывается номер первого элемента, за-тем, после двух точек, номер последнего элемента массива; Of — служебное слово; Integer — тип элементов массива. Решение нашей задачи с использова-нием массива выглядит следующим образом: Program Pr10_1m; Const n=30; Type Vector=Array[1..n] Of Integer; Var A : Vector; s,i: Integer; Begin WriteLn ('Введите ',n, ' чисел'); For i:=1 To n Do Begin write(‘A[‘,I,’]=’); ReadLn (A[i]); end; s:=0; For i:=1 To n Do s:=s+A[i]; WriteLn ('Их сумма равна ',s); End.

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

1. Найти сумму элементов массива, кратных заданному числу. Для этого потребуется внести незначительные изменения в решение предыдущей зада-чи. Добавляется описание еще одной переменной для хранения значения числа, на кратность которому проверяются значения элементов массива. По-являются операторы: WriteLn('Введите число '); ReadLn(k); и изменяется оператор из тела цикла If A[i] Mod k=0 Then s:=s+A[i]; Примечание. Не забудьте изменить значение n (вводить при каждом запуске про-

граммы 30 чисел — утомительное занятие). Изменим теперь программу так, чтобы в ней определялось количество

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

pos, neg:Integer; pos:=0;neg:=0; If A[i]>0 Then Inc (pos) Else If A[i]<0 Then Inc (neg); WriteLn(pos:4,neg:4); Модифицируйте программу таким образом, чтобы в ней находилось и ко-

личество нулевых элементов. Измените программу так, чтобы в массив B записывались номера четных

элементов массива A. Введение массива B и работа с ним требуют введения дополнительной пере-

менной для обращения к элементам массива B. Суть решения заключается в

Page 52: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

52

просмотре элементов массива A (это мы умеем), выявлении четных элементов и записи их номеров на очередную позицию в массив B.

Примечание. Обратите внимание на то, что мы записываем не значения элементов, а их номера! Program Pr10_2; Const n=10; Type Vector=Array[1..n] Of Integer;

Var A,B: Vector; i,j : Integer; Begin WriteLn ('Введите ',n, ' чисел');

For i:=1 To n Do Begin write(‘A[‘,I,’]=’); ReadLn (A[i]); end; j:=0; For i:=1 To n Do If A[i] mod 2 =0 Then Begin Inc(j); B[j]:=i End; For i:=1 To j Do Write (B[i]:3); WriteLn;

End. 2. Определить, есть ли в массиве элемент с определенными свойствами,

например, отрицательный (найдем номер последнего отрицательного эле-мента). Фрагмент очевидного решения:

k:=0; For i:=1 To n Do If A[i]<0 Then k:=i; If k=0 Then <отрицательных элементов в массиве нет> Else <последний отрицательный элемент имеет номер i >; Это решение плохое: если единственный отрицательный элемент в массиве находится на первом месте, то мы выполняем много лишних сравнений, про-сматривая массив до конца. Договоримся о том, что наш программный код должен экономно, рационально использовать ресурсы компьютера, в частности, и время его работы. Эффективный алгоритм значит в Computer Science боль-ше, чем сверхбыстродействующий компьютер.

Следующее изменение If A[i]<0 Then Begin k:=i; Break End; решает возникший вопрос, но…

Это изменение приводит к тому, что наш небольшой фрагмент кода имеет одну точку входа и две точки выхода. Тоже плохо, очень плохо. Обилие опе-раторов Break, Goto превратит нашу программу в “спагетти”. Замечание. При решении элементарной задачи использованы две перемен-

ные, хотя этого вовсе не требуется. Приемлемый вариант решения имеет вид: i:=n; While (i>=1) And (A[i]>0) Do Dec(i); If i<1 Then <отрицательных элементов в массиве нет> Else <последний отрицательный элемент имеет номер i >;

Изменим задачу. Найти значения и номера всех отрицательных эле-ментов. В этом случае можно использовать оператор For, поскольку нам необ-ходимо просмотреть все элементы массива. {$R+} Program Pr10_3; Const n=7; Type Vector=Array[1..n] Of Integer;

Page 53: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

53

Var A:Vector; i:Integer; Begin WriteLn ('Ввод элементов массива. Не забудьте об отрицательных элементах.'); For i:=1 To n Do Read (A[i]); WriteLn ('Вывод отрицательных элементов массива и их номеров (индексов)'); For i:=1 To n do If A[i]<0 Then WriteLn (A[i]:5,i:5); End.

В тексте программы поиска номеров всех отрицательных элементов в мас-сиве имеются строки: For i:=1 To n

Измените во второй строке n на n+1 и поставьте перед текстом про-граммы директиву компилятора {$R+}. При запуске программы появится ошибка периода выполнения: Error 201: Range check error. Она является сооб-щением о том, что значение переменной i (индекс элемента массива A) вышло за допустимый предел, т.е. за значение константы n.

Директивы компилятора управляют режимами компиляции. Все директивы начинаются с символа $ после открывающей скобки комментария, за которым следует имя директивы (состоящее из одной или нескольких букв), опреде-ляющее ее назначение. Контроль диапазонов включается / выключается по-средством директив {$R+}/{$R-}. В режиме {$R+} все индексы массивов и строк контролируются на выход за границы, а все присваивания значений скалярных переменных и ограниченных типов проверяются на вхождение в диапазон. При выходе за границы выполнение программы прекращается и выдается сообщение об ошибке.

Примечание. При работе с массивами настоятельно рекомендуется исполь-зовать эту директиву при отладке программ. 3. Формирование значений элементов массива путем ввода их с клавиатуры —

достаточно утомительное занятие. Будем использовать для этих целей гене-ратор случайных чисел. Это сложное название мы будем понимать очень просто. Есть нечто (“черный ящик”), и это нечто выдает числа, причем нам неизвестно, какое число выдается в очередной раз. Этим “черным ящиком” в Турбо Паскале является функция Random. Она возвращает случайное число.

{$R-} Program Pr10_4; Const n=10; Type Vector=Array[1..n] Of Integer; Var A:Vector; i:Integer; Begin {Randomize;} WriteLn ('Формирование значений элементов массива A'); For i:=1 To n Do A[i]:=Random (21) {-10}; WriteLn ('Вывод'); For i:=1 To n Do Write (A[i]:5); ReadLn End.

Запустите эту программу несколько раз. На экран будет выведена одна и та же последовательность чисел в диапазоне от 0 до 20.

Уберите фигурные скобки у (-10) и снова запустите несколько раз програм-му. Последовательность чисел на экране снова одна и та же, но числа теперь

Page 54: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

54

находятся в интервале от -10 до 10. Объясните этот факт. Попробуйте получать числа из любого интервала, например, от -17 до 25. Продолжим наши экспери-менты. Уберите фигурные скобки у процедуры Randomize. Повторите много-кратный запуск программы. Теперь последовательности чисел на экране раз-ные. В чем дело? Наш “черный ящик”, функция Random, начинает генериро-вать в первом случае числа (каким-то не известным нам образом) от фиксиро-ванного начального числа. Во втором случае эти начальные числа меняются от запуска к запуску (процедурой Randomize), и последовательности получаются разные. Следующим изменением является включение контроля на выход за до-пустимый диапазон {$R+}. После запуска мы видим уже знакомую ошибку: Error 201: Range check error. Прочитайте еще раз внимательно описание функции Random и попробуйте найти объяснение причины возникновения ошибки. Рассмотрим еще одну программу: {$R+} Program Pr10_4m; Const n=10; Var i:Integer; Begin WriteLn ('Вывод 10 случайных чисел в диапазоне от –10 до 10'); For i:=1 To n Do WriteLn (Random (21)-10); ReadLn End.

После запуска этой программы на экране появятся не числа из интервала от -10 до 10, а какая-то странная последовательность чисел. Она может быть, например, такой: 65530, 1, 2, 4, 65527, 2, 65528, 65534, 3, 2. Почему? Напомним, что диапазон для целых чисел типа Word от 0 до 65535 и число 6553510 = 216 -1 = 11111111111111112 , а это -1 в дополнительном коде. Число 65534 соответст-вует -2 в дополнительном коде. В первой версии программы результат функции Random имеет тип Word. Из этого числа вычитается целое число типа Integer. Поскольку типы различные, выполняется преобразование типов по правилу, описанному в занятии 2, то есть к типу LongInt. А элемент массива имеет тип Integer. Вот здесь-то и «собака зарыта». Измените тип элементов массива A на LongInt и сравните результаты работы программ. Во второй версии контроль на выход за диапазон не выполняется, может быть, потому что нет операции при-своения (контролировать нечего и некого)? Верните тип Integer в описании элементов массива и измените одну строку программы: For i:=1 To n Do A[i]:= Integer (Random (21)-10;

4. Вычислить факториал натурального числа N. Факториал числа N равен про-изведению чисел 1•2•3•…•(N-1)•N (обозначается как N!). Сложность задачи в том, что уже 8! = 40320, а 13! = 6227020800. Таким образом, типы данных Integer, LongInt можно использовать лишь для вычисления факториалов со-

Page 55: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

55

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

A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] 8 0 0 8 6 1 9 9 3

В массиве записано значение 11! = 39916800. В A[0] фиксируется число за-нятых элементов массива, в A[1] находится цифра единиц результата, в A[2] - цифра десятков результата, в A[3] - цифра сотен результата и т.д. Почему так, а не наоборот? Такая запись позволяет исключить сдвиг элементов массива при переносе значений в старший разряд.

Наберите текст программы, выполните компиляцию и, открыв окно Watch, выполните приведенную ниже программу в пошаговом режиме, отслеживая изменение значений переменных при не очень большом значении N. Добейтесь полного понимания логики работы программы. {$R+} Program Pr10_5; Const MaxN=300; Type Vector=Array[0..MaxN] Of Integer; Var A:Vector; i,j,r,w,N:Integer; Begin FillChar (A,SizeOf (A),0); {Заполняем массив А нулями} Write('Введите число, факториал которого необходимо найти '); ReadLn(N); A[0]:=1;A[1]:=1;j:=2;{Начальные присваивания, начинаем вычислять 2!} While (j<=N) аnd (A[0]<MaxN) Do Begin {Второе условие фиксирует факт выхода за пределы массива.} r:=0;i:=1;{r – перенос из разряда в разряд при выполнении умножения числа j на очередную цифру A[i] предыдущего результата} While (i<=A[0]) or (r<>0) Do

Begin {Пока не «прошли» все цифры предыдущего результата или есть перенос цифры в старший разряд} w:=A[i]*j+r;{Умножаем очередную цифру и прибавляем цифру переноса из предыдущего разряда} A[i]:=w mod 10;{Находим новую цифру с номером i} r:=w div 10;{Вычисляем значение переноса.} If A[A[0]+1]<>0 Then Inc(A[0]);{Изменяем, если необходимо, количество задействованных элементов массива A, длину полученного резуль-тата.} Inc(i) End;

Inc(j) End; For i:=A[0] DownTo 1 Do Write(A[i]);{Вывод результата.} WriteLn; ReadLn End.

Page 56: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

56

Найдите число N, при котором 300 элементов массива A окажется недоста-точно для хранения результата.

Измените программу так, чтобы выводились факториалы всех чисел в ин-тервале от 1 до N.

Измените программу так, чтобы в ней вычислялись числа вида an , например, 3100 .

Задания для самостоятельной работы Дан массив целых чисел. Найти: 1. сумму элементов массива, больших данного числа A (A вводится с клавиатуры); 2. сумму элементов массива, принадлежащих промежутку от А до В (А и В вводятся с кла-

виатуры); 3. максимальный элемент массива и его номер, при условии, что все элементы различны; 4. номера всех элементов массива с максимальным значением; 5. минимальный элемент массива; 6. cумму элементов массива с k1-го по k2-й, k1 и k2 вводятся с клавиатуры; 7. количество нечетных элементов массива; 8. количество отрицательных элементов массива; 9. сумму первых пяти элементов массива; 10. все элементы, кратные 3 или 5, и их количество; 11. сумму всех четных элементов массива, стоящих на четных местах, то есть имеющих чет-

ные номера; 12. сумму всех четных элементов массива (или сумму элементов, кратных заданному числу); 13. сумму положительных элементов массива; 14. сумму элементов, имеющих нечетное значение; 15. сумму элементов, имеющих нечетные индексы; 16. сумму положительных элементов, значения которых меньше 10; 17. удвоенную сумму положительных элементов; 18. сумму отрицательных элементов; 19. индексы тех элементов, значения которых больше заданного числа А; 20. количество элементов массива, значения которых больше заданного числа А и кратны 5; 21. индексы тех элементов, значения которых кратны 3 и 5; 22. индексы тех элементов, значения которых больше значения предыдущего элемента (на-

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

числа А; 24. пару соседних элементов с суммой, равной заданному числу. Определить: 1. сколько элементов массива превосходят по модулю заданное число А; 2. есть ли в данном массиве два соседних положительных элемента? Найти номера первой

(последней) такой пары; 3. есть ли в данном массиве элементы, равные заданному числу? Если есть, то вывести но-

мер одного из них; 4. есть ли в данном массиве положительные элементы, кратные k, k вводится с клавиатуры; 5. номер первого отрицательного элемента, дающего при делении на 5 остаток 2; 6. есть ли 2 пары соседних элементов с одинаковыми знаками; 7. номер последней пары соседних элементов с разными знаками. 8. Измените программу вычисления

• факториала числа N так, чтобы вычислялись: 1•3•5•…•(2•N-1), 2•4•6•8•…•(2•N).

Page 57: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

57

• степени числа так, чтобы была возможность вычислять степени отрицательных целых чисел.

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

Материал для любознательных

1. Одномерный массив — это конечное упорядоченное множество элементов. За первым элементом идет второй, за вторым — третий и т.д. В силу этого элементы массива можно пе-ренумеровать целыми числами — индексами элементов. Для доступа к элементу массива достаточно знать имя массива и индекс элемента. Память компьютера представляет собой последовательность ячеек, имеющих адреса, с возможностью обращения к ячейкам по их номерам. Для массива обычно выделяется сплошной участок памяти компьютера. Как вы-числяются адреса элемента массива в памяти? С этой целью массив имеет описатель (деск-риптор). Приведем структуру дескриптора для массива A: Array[1..10] Of Integer.

Имя A

Адрес начального элемента Определяется системой, обозначим — Addr(A[1])

Индекс начального элемента 1 Индекс последнего элемента 10

Тип элемента Integer Длина элемента 2 байта

Адрес элемента A[i] находится по формуле: Addr(A[i])=Addr(A[1])+(i-1)*2

2. Общие сведения по организации ЭВМ. Структура простой ЭВМ показана на рисунке. “Мозгом” вычислительной машины является центральный процессор. Его функция состо-ит в выполнении программ, находящихся в основной памяти, путем выборки, проверки и последовательного выполнения составляющих их команд. Центральный процессор состо-ит из нескольких частей. Устройство управления осуществляет выборку команд из основ-ной памяти и определение их типа. Арифметическо-логическое устройство осуществляет выполнение таких операций, как сложение, сдвиг и т.д. Центральный процессор содержит высокоскоростную память для запоминания промежуточных результатов и управляющей информации. Эта память состоит из регистров, каждый из которых имеет определенное назначение. Счетчик команд указывает адрес команды, которую необходимо выполнить следующей. Регистр команд содержит текущую выполняемую команду. Центральный процессор выполняет каждую команду в виде последовательности простых операций: 1. выбор очередной команды из основной (оперативной) памяти в регистр команд; 2. изменение счетчика команд таким образом, чтобы он указывал на адрес команды,

следующей за выбранной; 3. определение типа выбранной команды; 4. проверка, требуются ли для выполнения выбранной команды какие-либо данные, и

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

мяти; 6. выполнение команды; 7. запоминание результатов выполнения команды в заданных ячейках памяти.

Page 58: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

58

Память - это часть ЭВМ, где хранятся программы и данные. Память состоит из ячеек

(ячейка - минимальная адресуемая единица памяти), каждая из которых может хранить дан-ные. Каждой ячейке соответствует число, называемое ее адресом. Если в памяти N ячеек, то они имеют адреса от 0 до N-1. Все ячейки памяти содержат одинаковое число битов. Если в ячейке k битов, то она может содержать любую из 2k их комбинаций. Центральный процес-сор связан с основной памятью при помощи как минимум двух регистров — регистра адреса памяти и буферного регистра. Для того чтобы считать из памяти содержимое ячейки, цен-тральный процессор загружает адрес этой ячейки в регистр адреса памяти и посылает памяти сигнал чтения. Память помещает содержимое запрашиваемой ячейки в буферный регистр, где оно доступно центральному процессору для последующей обработки. Для того чтобы за-писать данное в память, центральный процессор записывает адрес ячейки памяти в регистр адреса памяти и записываемое данное в буферный регистр, а затем сигнализирует памяти о начале операции записи. 3. Принципы Джона фон Неймана. Несмотря на большие разнообразия существующих в

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

Page 59: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

59

ными. В принципе ЭВМ не различает, что именно хранится в данной ячейке памяти — число, символ или команда. Для решения другой задачи требуется смена в оперативной памяти программы и обрабатываемых данных. Итак, ЭВМ — универсальный инструмент обработки информации.

4. Возникновение информатики как науки связано с появлением ЭВМ и относится к 60-м го-дам ХХ столетия. В самом общем смысле под информатикой пони мают фундаменталь-ную науку, изучающую процессы передачи, накопления и обработки информации с по-мощью ЭВМ. Термин “Informatique” введен во Франции на рубеже 60—70-х годов. Од-нако еще раньше в США был введен в употребление термин “Computer Science” для обо-значения науки о преобразовании информации с помощью ЭВМ. В настоящее время эти термины практически эквивалентны. Содержание ядра информатики, на наш взгляд, оп-ределяют программные и технические средства. На этом занятии рассмотрено понятие массива, а в материале для чтения дан обзор структуры простой ЭВМ и принципов Джона фон Неймана. Понятие массива носит фундаментальный характер. Эта структура данных наиболее соответствует структуре ЭВМ. Образно выражаясь, оперативная память ЭВМ — это не что иное, как огромный одномерный массив.

11. Тема: Процедуры План занятия: • структура программы; • вызов процедур, передача данных; • глобальные и локальные переменные, формальные и фактические параметры процедур; • экспериментальная работа с программами:

перестановки значений переменных a, b, c в порядке возрастания; вычисления значения выражения y = an • xn + an—1 • xn—1 + … + a2 • x2 + a1 • x1 + a0;

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

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

Структура процедуры похожа на структуру программы. Отличия выделены жирным шрифтом. Procedure имя процедуры (<параметры>); Label <метки>; Const <описание констант>; Type <описание типов данных>; Var <описание переменных>; <вложенные процедуры и функции>; Begin <основное тело процедуры> End;

Page 60: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

60

Вызов процедур. Обратимся к примеру, приведенному на рисунке.

В приведенном фрагменте текста программы вызывается процедура вы-

числения суммы двух целых чисел. Процедура вызывается по имени. При вызове процедуры управление передается на соответствующий участок

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

При вызове процедуры в нее могут передаваться данные. В нашем примере в процедуру Sum передаются переменные a, b, c. Процедура имеет три параметра x, y и z. Причем x, y описаны без идентификатора Var, а z - с идентифика-тором Var. Поясним разницу следующей схемой: a→x, b→y, c↔z. При вызове процедуры Sum(a,b,c) из основной программы значение переменной a присваи-вается переменной x процедуры Sum, а значение переменной b - переменной y. Иначе обстоит дело с переменными c и z. Процедура Sum «знает» о том, где в памяти компьютера находится переменная c и что при изменении переменной z необходимо произвести соответствующее изменение переменной c. На это ука-зывает идентификатор Var в описании параметра z.

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

Локальные переменные существуют только в течение времени работы про-цедуры, определяются (создаются) при ее вызове и «исчезают» после заверше-ния работы процедуры (после выполнения оператора End). Параметры. При описании процедуры указывается список формальных па-раметров. Каждый параметр является локальным, к нему можно обращаться только в пределах данной процедуры (в нашем примере x, y, z - формальные параметры). Фактические параметры - это параметры, которые передаются процедуре при обращении к ней (a, b, c - фактические параметры). Количество и типы формальных и фактических параметров должны совпадать. Параметры-значения. В нашем примере фактические параметры a и b пере-давались “по значению”. При таком способе передачи параметров значение фактического параметра становится значением соответствующего формального параметра. Внутри процедуры можно производить любые действия с данным формальным параметром (допустимые для его типа), но эти изменения никак не отражаются на значении фактического параметра, то есть каким он был до вы-

Page 61: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

61

зова процедуры, таким же и останется после завершения ее работы (x,y – фор-мальные параметры-значения). Параметры-переменные. Это те формальные параметры, перед которыми

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

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

Концепция процедур и функций — один из механизмов второго витка развития технологий программирования, а именно, структурного проек-тирования.

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

1. Составить программу перестановки значений переменных a, b, c в порядке

возрастания, т.е. так, чтобы a ≤ b ≤ c. Program pr11_1; Procedure Swap (Var x,y:Integer); Var t:Integer; Begin t:=x; x:=y; y:=t End; {******* основная программа *******} Var a,b,c:Integer; Begin WriteLn ('Введите три числа '); ReadLn (a,b,c); If a>b Then Swap (a,b); If b>c Then Swap (b,c); If a>c Then Swap (a,c); WriteLn (a:5,b:5,c:5); End.

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

Page 62: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

62

2. Составить программу вычисления выражения y = an • xn + an—1 • xn—1 + … + a2 • x2 + a1 • x1 + a0 , где все данные - целые числа. Коэффициенты an , an—1 , …, a1 , a0 являются первыми членами арифметической прогрессии, определяемой первым элементом (q) и разностью между соседними элементами (d). Значения q, d, n, x вводятся с клавиатуры. Например, q = 2, d = 3, n = 5 и x = 4. Нам необ-ходимо вычислить выражение 2•45 + 5•44 + 8•43 + 11•42 + 14•41 + 17•40. Program pr11_2; Procedure Degree (x,y: Integer; Var st: Integer); Var i:Integer; Begin st:=1; For i:=1 To y Do st:=st*x End; {******* основная программа *******} Var q,d,n,x,t,w,s: Integer; Begin Writeln ('Введите исходные данные – четыре не очень больших числа'); Readln (q,d,n,x); s:=0;t:=n; For i:=1 To n+1 Do Begin Degree (x,t,w); s:=s+q*w; Dec (t); Inc (q,d) End; Writeln ('Результат ',s); Readln End.

Составьте таблицу изменения значений переменных q, t, w при работе про-граммы.

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

Зафиксируйте значения q, d, x и найдите экспериментальным путем (или модифицируя программу) то значение n, при котором диапазона типа Integer уже не хватает для хранения результата.

Примечание. Переменные с именем x в основной программе и в процедуре Degree — это разные переменные!

Запишем выражение по-другому: 17 + 4•(14 + 4•(11 + 4•(8 + 4•(5 + 4•2)))). Результат вычислений, естественно, тот же самый. В общем виде запись выгля-дит следующим образом: a0 + x•(a1 + x•(a2 + x•(… + x•(an—1 + x•an )))). Приве-денная запись называется схемой Горнера.

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

Что означает запись 4 2•5 + 4•8 + 4•11 + 4•14 + 4•17 + ? Можно ли вычислить это выражение? Оказывается, да. Берем два числа 4 и 2 и выполняем с ними операцию, записанную после них, т.е. умножение. Сохраня-ем результат и продолжаем вычисление. Берем 8 и 5 и выполняем сложение. Потренируйтесь. Напишите несколько примеров и преобразуйте их запись к та-кому виду. Она называется обратной польской записью, в честь польского математика Яна Лукашевича. Для общего случая в нашей задаче обратная польская запись имеет вид: an x •an—1 + … a2 x•a1 + x•a0 +. Пусть у нас есть про-

Page 63: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

63

цедура Part, выполняющая одно арифметическое действие (умножение или сложение) над двумя числами. Напишите программу для решения нашей задачи с использованием процедуры Part. Procedure Part (x,y:Integer; t:Byte; Var z:Integer); Begin If t=1 Then z:=x+y Else z:=x*y End;

У нас есть три способа вычисления значения одного и того же выражения. Сравните количество операций умножения и сложения, необходимых для по-лучения результата в каждом из случаев. Какой вывод вы можете сделать? 3. Задание значений элементам одномерного массива (с помощью генератора

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

Program Pr11_3; Const n=8; l=-10; h=21; Type Vector=Array[1..n] Of Integer; Var A:MyArray; Procedure Init (t,v,w:Integer; Var X:Vector); Var i:Integer; Begin Randomize; For i:=1 To t Do X[i]:=v+Integer (Random(w)) End; Procedure Print (t:Integer; X:Vector); Var i:Integer; Begin For i:=1 To t Do Write (X[i]:5); WriteLn; End; Begin WriteLn ('Формирование значений элементов массива A');Init (n,l,h,A); {Значения n элементов массива А формируются из интервала целых чисел от l до h} WriteLn ('Вывод'); Print (n,A) {n первых элементов массива А выводятся на экран (в данном случае)} End.

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

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

Изменим заголовок процедуры Print на: Procedure Print (n:Integer; X:Array[1..n] Of Integer) и запустим

программу. Результат не заставит себя ждать: Error 54: OF expected. Курсор находится на квадратной скобке, т.е. после слова Array ожидается Of.

Изменим описание на: Procedure Print (n:Integer; X:Array Of Integer). Ре-зультат изменился: знакомая ошибка Error 201: Range check error.

Отключим контроль на выход за пределы диапазона - {$R-}. Программа за-работала, точнее, она выдает результат. Итак, сплошные загадки. Попробуйте дать им разумное объяснение. 4. Даны два одномерных массива. Найти элементы, принадлежащие и тому и

другому массивам.

Page 64: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

64

Процедуру Init при решении задачи требуется использовать два раза с раз-личными параметрами, процедуру Print - три раза. Сделаем «костяк» програм-мы. Процедуры Init и Print почти универсальны и берем из предыдущего зада-ния.

Программа (ее «костяк») должна компилироваться. Сохраните ее. Набирать весь текст программы, а затем приступать к отладке — это дурной тон в программировании. Возьмите за правило и всегда его придерживайтесь – «в любой момент времени у нас должна быть компилируемая программа, сохра-ненная на внешнем носителе». Кроме того, текст любой процедуры и, естест-венно, основной программы должен помещаться на экране и, конечно, быть чи-таемым. {$R+} Program Pr_4; Const n=10; la=-10; ha=21; m=8; lb=-15; hb=31; Type Vector=Array[1..n] Of Integer; Var A,B,C:Vector; k:Integer; Procedure Solve (qx, qy:Integer; Var qz:Integer; X,Y:Vector; Var Z: Vector); Begin End; Begin Init (n,la,ha,A); WriteLn ('Вывод значений элементов массива A'); Print (n,A); Init (m,lb,hb,B); WriteLn ('Вывод значений элементов массива B'); Print (m,B); Solve (n,m,k,A,B,C); WriteLn ('Вывод тех целых чисел, которые есть и в А, и в B');Print (k,C); ReadLn End.

Уточним наше решение, а именно, процедуру Solve. Procedure Solve(qx,qy:Integer; Var qz:Integer; X,Y:Vector; Var Z: Vector); Var i,j:Integer; Begin qz:=0; For i:=1 To qx Do For j:=1 To qy Do If X[i]=Y[j] Then Begin Inc(qz); Z[qz]:=X[i] {j:=qy} End End;

После запуска программы окажется, что при некоторых исходных данных она выдает неправильный результат. Пусть, например, в первом массиве имеет-ся один элемент со значением 10, а во втором — пять таких элементов. Соглас-но формулировке задачи в ответе 10 должна присутствовать один раз, а она вы-водится пять раз. Уберем фигурные скобки у оператора j:=qy. Мы «насильно» изменяем управляющую переменную цикла For j:=1 … Выводится правильный

Page 65: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

65

результат. Однако этот прием считается признаком плохого стиля программи-рования. Изменим внутренний цикл. Procedure Solve(qx,qy:Integer; Var qz:Integer; X,Y:Vector; Var Z: Vector); Var i,j:Integer; Begin qz:=0; For i:=1 To qx Do

Begin j:=1; While (j<=qy) And (X[i]<>Y[j]) Do Inc(j); If j<=qy Then Begin Inc(qz); Z[qz]:=X[i] End

End End; 5. Даны массив A из n элементов и число m, где 1 < m < n. Не используя до-

полнительных массивов, переставить первые m элементов в конец массива, а элементы с (m + 1)-го по n-й — в начало, сохраняя порядок элементов.

Идея решения: переворачиваем первые m элементов, затем переворачиваем элементы, начиная с (m + 1)-го. Затем переворачиваем все элементы массива. Пример. Пусть n = 10, m = 6, массив A: 5 1 -4 3 7 2 -1 9 8 6 2 7 3 -4 1 5 -1 9 8 6 первое переворачивание m элементов 2 7 3 -4 1 5 6 8 9 -1 второе переворачивание элементов с m + 1 по n -1 9 8 6 5 1 -4 3 7 2 третье переворачивание элементов с 1 по n

В тексте решения не приводится реализация процедур Init и Print. Для про-цедуры Rev рекомендуется выполнить «ручную» трассировку при различных значениях t и l.

{$R+} Program Pr11_5; Const n=10;m=6; Type Vector=Array[1..n] Of Integer; Var A:Vector; Procedure Init (Var X:Vector); Procedure Print (X:Vector); Procedure Swap (Var a,b:Integer); {Из первого задания.} Procedure Rev (t,l:Integer; Var X:Vector); Var i:Integer; Begin For i:=t To t+(l-t) Div 2 Do Swap (X[i],X[l-i+t]) End; Begin Init (A); Print (A); Rev (1,m,A); Rev (m+1,n,A); Rev (1,n,A); Print (A); ReadLn End.

Задания для самостоятельной работы 1. Даны два различных выражения вида: y = an •xn + an-1 •xn—1 + … + a2 •x2 + a1 •x1 + a0 ,

z = bn •xn + bn-1 •xn—1 + … + b2 •x2 + b1 •x1 + b0 , где все коэффициенты - целые числа.

Page 66: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

66

Коэффициенты хранятся в массивах A и B. При заданном значении x найти максималь-ное значение из y и z.

2. Даны два одномерных массива из целых чисел. Найти элементы, которые есть в первом массиве и которых нет во втором.

3. Даны два одномерных массива из целых чисел. Найти элементы, которые входят только в один из массивов.

4. Решить задачу 4 из раздела экспериментальной работы для трех массивов. 5. Даны 3 одномерных массива из целых чисел. Найти элементы, которые есть в первом

массиве и которых нет во втором и третьем массивах. 6. Решить задачу 3 для трех одномерных массивов. 7. Даны два одномерных массива из целых чисел разной размерности. Найти среднее

арифметическое элементов каждого массива и их сумму. Решить задачу для трех масси-вов.

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

9. Даны два одномерных массива (A, B) одинаковой размерности n и число m. Поменять местами первые элементы массивов и выполнить перестановку элементов массивов в обратном порядке (задание № 5 раздела экспериментальной работы).

10. Дано четное число n. Проверить для этого числа гипотезу Кристиана Гольдбаха (1742 год). Эта гипотеза (по сегодняшний день не опровергнутая и полностью не доказанная) заключается в том, что каждое четное n, большее двух, представляется в виде суммы двух простых чисел. Ограничим диапазон проверяемых чисел интервалом 2 ≤ n ≤ 999. Фрагмент проверки числа на простоту оформить в виде процедуры. Примеры: 6 = 3 + 3 ; 12 = 5 + 7; 30 = 7 + 23; 308 = 31 + 277; 992 = 73 + 919… Исследовать гипотезу К.Гольдбаха для больших значений n.

11. Известны следующие признаки делимости числа n: • для делимости на 2 необходимо, чтобы последняя цифра числа делилась на 2; • для делимости на 3 требуется, чтобы сумма цифр числа делилась на 3; • для делимости на 4 необходимо, чтобы число из последних двух цифр делилось на 4; • для делимости на 5 необходимо, чтобы последняя цифра числа была 0 или 5; • для делимости на 8 необходимо, чтобы число из 4 последних цифр делилось на 8; • для делимости на 9 необходимо, чтобы сумма цифр числа делилась на 9; • для делимости на 11 необходимо, чтобы разность между суммой цифр, стоящих на

четных местах, и суммой цифр, стоящих на нечетных местах, делилась на 11. Написать процедуры проверки признаков делимости. Проверить их для различных значений n.

Материал для любознательных

1. Некоторые факты из элементарной теории вероятностей. Всем нам достаточно

часто приходится сталкиваться с ситуациями, когда, зная возможные исходы некоторого события (опыта), мы не можем точно предсказать его результат. Например, при броса-нии монеты - какой стороной она упадет вверх; при бросании игральной кости - какая из шести сторон окажется сверху; при вытаскивании карты из игральной колоды - какая карта будет вытянута и т.д. Главное, что присуще всем этим ситуациям, то, что исходы равноправны. Выпадет орел или решка; выпадет сторона игральной кости с числом от 1 до 6; вытянута одна из 52 карт. Как оценивать исходы, какую меру взять для оценки того, что произойдет то или иное

событие. Пусть, например, событие - это выпадение четного числа при бросании игральной кости. Общее количество исходов - 6, число благоприятных исходов - 3. Вытянута карта пи-

Page 67: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

67

ковой масти, общее количество исходов - 52, благоприятных - 13. Понятие меры в теории вероятности вводится следующим образом. Определяется множество элементарных событий S, его элементы считаются равноправными, равновероятными. Любое событие A - подмно-жество S также состоит из элементарных событий. Отношение |A| / |S|, где через | | обозначе-но количество элементов в множестве, называется вероятностью события A и обозначается P(A). Вероятность любого события А заключена между нулем и единицей: 0 ≤ P(A) ≤ 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. Событие, вероятность которого равна 1, называется достоверным - оно наступает всегда.

Монета упадет гербом или решкой, считаем, что на ребро она встать не может. Событие, ве-роятность которого равна 0, называется невозможным - оно не наступает никогда. В нашем примере бросания игральной кости (и других) есть то, что называют элементарным событи-ем - выпадение стороны кости с определенным числом. Остальные события составляются из элементарных событий, например, выпадение стороны кости с четным числом. События A и B называют несовместимыми, если появление одного из них исключает появление другого. При бросании монеты орел и решка не могут выпасть одновременно. Если A и B - несовмес-тимые события, то P(A + B) = P(A) + P(B). Утверждение обобщается на n несовместимых со-бытий. Говорят, что несколько событий A1 , A2 , …, An образуют полную группу, если веро-ятность того, что произойдет одно из них, является достоверным событием. Примеры:

Опыт бросание двух монет. События «два орла», «две решки» и «один герб, одна решка» образуют полную группу и являются несовместимыми.

Опыт - бросание игральной кости. События «1 или 2 очка», «2 или 3 очка», «3 или 4 оч-ка», «4 или 5 очков» и «5 или 6 очков» образуют полную группу, но не являются несо-вместимыми.

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

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

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

Page 68: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

68

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

Игра «Бар – Кохба». Один из игроков что-то загадывает, а второй должен отгадать зага-данное, задавая вопросы, которые предполагали бы только ответы типа «да», «нет». Предпо-ложим, что в классе 16 учеников и учитель загадал номер по журналу одного из учеников, они по какой-то причине пронумерованы числами от 0 до 15. В начальный момент времени у нас полное незнание того, какой номер загадан. Первая серия наших вопросов. 1-й вопрос. Находится ли загаданный номер в интервале от 8 до 15? Ответ - да (1). 2-й вопрос. Находится ли загаданный номер в интервале от 12 до 15? Ответ - нет (0). 3-й вопрос. Находится ли загаданный номер в интервале от 11 до 12? Ответ - да (1). 4-й вопрос. Загадан номер 11? Ответ - нет (0). Вторая схема формулировок вопросов. 1-й вопрос. Находится ли загаданный номер в интервале от 0 до 3? Ответ - нет. 2-й вопрос. Находится ли загаданный номер в интервале от 12 до 15? Ответ - нет. 3-й вопрос. Находится ли загаданный номер в интервале от 4 до 7? Ответ - нет. 4-й вопрос. Находится ли загаданный номер в интервале от 8 до 11? Ответ - да. 5-й вопрос. Находится ли загаданный номер в интервале от 10 до 11? Ответ - да. 6-й вопрос. Загадан номер 11? Ответ - нет. Чем отличаются серии вопросов? Мера нашего незнания в том и в другом случаях одина-кова, то есть получено одно и то же количество информации. Примем за единицу измерения информации (1 бит) количество информации, содержащееся в ответе, при условии, что отве-ты равновероятны. В первом случае каждый ответ содержит один бит информации, ибо от-веты “да” и “нет” были равновероятны. Во втором случае - нет, в среднем один ответ содер-жал 4/6 бита информации. Всего получено 4 бита информации. Неопределенность полностью устранена, мы знаем загаданный номер. Пусть есть N равновероятных событий и выделено одно из событий (t). Для того чтобы определить t, необходимо получить количество инфор-мации I, равное log2N. Эта формула для вычисления количества информации предложена американским инженером Р.Хартли в 1928 году. Если события не равновероятные, то ко-личество информации вычисляется по формуле американского математика К.Шеннона: I = -(p1 log2 p1 + p2 log2 p2 + … + pN log2 pN ), где pi - вероятности событий.

Из формулы К.Шеннона получается формула Р.Хартли при pi = 1/N для всех значений i. Действительно, I=-(1/N•log2(1/N)+…+1/N•log2(1/N))=log2N. В скобках первой серии вопро-сов указаны цифры 0 или 1. Если выписать ответы в виде двоичного числа 10102 и перевести его в десятичную систему счисления, то получим число 10 - номер загаданного ученика! Итак, каждая двоичная цифра несет в себе один бит информации, отсюда и название едини-цы измерения - бит (binary digit - двоичная цифра). В игре “Бар - Кохба” вопросы задаются последовательно, на основании ответов учителя. А можно ли задать сразу четыре вопроса, а затем получить четыре ответа учителя и определить номер загаданного ученика? Оказывает-ся, да. Вопросы формулируются так: “Вы загадали номер, двоичная запись которого содер-жит единицу в первом разряде?” Обратите внимание на то, что и в этом случае ответы “да” и “нет” равновероятны. Предположим, что учитель одновременно загадал номера двух учени-

Page 69: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

69

ков (t1 и t2) в разных классах. Количество учеников в классах - N1 и N2 . Необходимо отгадать пару (x1, x2), принадлежащую множеству всех пар - (N 1 × N 2). Согласно формуле Хартли, необходимо задать log2(N1•N 2) вопросов, или получить log 2 (N 1 • N 2) бит информации. С другой стороны, номера учеников можно отгадывать независимо, для отгадывания t1 требу-ется задать log2N1 вопросов, для отгадывания t2 - log2N2 вопросов. Следовательно, общее число вопросов равно log2N1 + log2N2 . Итак, мы получили log 2 (N 1 • N 2 ) = log 2N1 + log 2N2 , что совпадает с хорошо известным свойством логарифмической функции. Это закон адди-тивности информации.

Примеры. Имеется 27 монет: 26 настоящих и одна фальшивая, она легче настоящих. Сколько взвешиваний необходимо произвести, чтобы определить фальшивую монету?

Фальшивой может оказаться любая из 27 монет, следовательно, по формуле Хартли ко-личество недостающей информации равно log227 битов. Любое взвешивание имеет три исхо-да и может дать нам только log23 битов информации. Если мы производим X взвешиваний, то они дадут X•log23 битов информации. Итак, X•log2 ≥ log227 = log233 = 3•log23. Следовательно, X ≥ 3. На самом деле достаточно ровно 3 взвешиваний: первое по 9 монет, второе по 3 монеты из найденной группы и, наконец, по одной монете из найденной группы по 3 монеты. Чтобы найти элемент множества, состоящего из 7 элементов, необходимо за-дать три вопроса, а чтобы найти элемент множества, состоящего из 9 элементов, - 4 вопроса, то есть по формуле Хартли получить log27 + log29 битов информации. Но если необходимо отгадать пару элементов из этих множеств, то требуется получить log2(7•9) битов информа-ции, а это меньше 6 вопросов. Противоречия нет: log 2 (7•9) = log 2 7 + log 2 9 = 5,97728 < 6. Как понимать формулу Хартли, если log2N не является целым числом? Предположим, что мы выполняем отгадывание не один, а k раз, то есть строим последовательность из k неизвест-ных элементов — (x1 , x2 , …, xk ). Таких последовательностей Nk . Очевидно, что для числа вопросов (sk ) в этом случае выполняются следующие неравенства:

log 2Nk < s k < log 2 N k + 1 или log2N<sk/k<log2N+1/k. Число Sk / k показывает, сколько вопросов в среднем необходимо для того, чтобы отгадать один элемент множества, содер-жащего N элементов. Выбирая значение k достаточно большим, величину 1/k можно сделать сколь угодно малой. Итак, в том случае, когда N не совпадает со степенью двойки, число во-просов, которое в среднем необходимо задать для отгадывания одного элемента множества мощности N, будет отличаться от log 2 N на сколь угодно малую величину. 11. Тема: Функции План занятия 1. описание функции; 2. вызов процедур, передача данных; 3. экспериментальная работа с программами:

вычисления числа сочетаний из n по m; получения палиндрома числа путем последовательного его “перевертывания” и сло-жений;

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

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

Page 70: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

70

Function <имя функции> [(<список параметров>)]:<тип результата>; В теле функции обязательно должен быть хотя бы один оператор при-сваивания, где в левой части стоит имя функции, а в правой - ее значение. Иначе значение функции не будет определено.

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

1. Составить программу подсчета числа сочетаний - C(n, m). Пусть у нас есть функция подсчета факториала числа - Fact(n). Написание основной про-граммы сводится к программированию формулы C(n, m) = n! / m!(n - m)!.

Program Pr12_1; {******** алгоритм - функция вычисления факториала числа n ******* } Function Fact(n:Integer):Longint; Begin End; {******** основная программа *******} Var n,m: Integer; c: Longint; Begin WriteLn('Введите n и m :'); ReadLn(n,m); c:=Fact(n) / (Fact(m) * Fact(n-m)); WriteLn(c); Readln End.

При компиляции этой программы (функцию Fact мы пока не написали, но компилироваться программа должна и без нее) в строке c:=Fact(n) / (Fact(m) * Fact(n-m)); возникает ошибка - Error 26: Type mismatch. - несоответствие ти-пов. Вспомним, что типы данных определяют не только диапазон значений переменных, но и допустимые над этим типом операции. Результат опера-ции деления “/” нельзя присваивать переменным целого типа. Необходимо использовать операцию Div, т.е. соответствующая строка должна выглядеть следующим образом: c:=Fact(n) Div (Fact(m) * Fact(n-m));

Дополняем программу. Function Fact(n:Integer):Longint; Var i: Integer; rez: Longint; Begin rez:=1; For i:=1 To n Do rez:=rez*i; Fact:=rez End;

Запускаем программу для различных значений n и m. При n = 10, m = 5 ре-зультат 252 не вызывает сомнений. При n = 13, m = 5 результат 399, что-то не так... должно получиться большее значение. При n = 15, m = 7 результат 9, те-перь уже видно, что программа не работает. Объясните причину.

Можно ли упростить программу? Переменная с лишняя (попробуйте убрать ее). Сколько лишних вычислений (умножений) делает наша программа? Под-считаем C(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 71: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

71

Вычисляя значение С(13, 5) вручную, мы, конечно, не будем выполнять все эти умножения. Достаточно вычислить 9•11•13 = 1287. Напишите более эффективную функцию вычисления числа сочетаний. Например, даже ни-жеприведенный вариант программы даст более обнадеживающие результаты. Правильный результат при C(13, 5), а именно 1287, уже шаг вперед, но это не предел улучшений программы. Program Pr12_1m; Function S(n,m:Integer):LongInt; Var i:Integer; rez,cht:LongInt; Begin rez:=1;cht:=1; For i:=1 To m Do Begin rez:=rez*i; cht:=cht*(n-i+1) end; S:=cht Div rez End; {******** основная программа *******} Var n,m:Integer; Begin WriteLn('Введите два числа: '); ReadLn(n,m); WriteLn(S(n,m)); ReadLn End.

На восьмом занятии мы познакомились с основной теоремой арифметики. Пусть нам не требуется вычислять C(n, m), а необходимо представить это зна-чение в виде p1

a1 p2a2 - pg

ag, где ai ≥ 0, а pi - простые числа. Ограничения: 1 < n ≤ 100, 1 < m < n. Вычислять «в лоб» - безнадежное занятие. Числа получаются ог-ромные. Рассмотрим идею решения на примере. Вычисляем C(8, 5) = 8! / (5!•3!). Представим 8! как единицы в соответствующем массиве, например, с именем Sn, Sn[i] = 1, если число i есть в произведении. Просматриваем массив Sn, выбираем очередной элемент. Если число i составное, то находим его раз-ложение на простые сомножители. К элементам Sn, соответствующим сомно-жителям, прибавляем количество единиц, равное числу их вхождений в i, а Sn[i] обнуляем. Выполним эти действия для n!, m! и (n—m)!. После этого оста-нется только провести вычитание степеней одного и того же простого числа.

Числа \ i 2 3 4 5 6 7 8 8! 1 1 1 1 1 1 1 4 3 1 0 1 1 1 1 6 4 2 0 1 0 1 1

Sn

8 7 2 0 1 0 1 0 5! 1 1 1 1 0 0 0

Sm 4 3 1 0 1 0 0 0

Snm 3! 1 1 0 0 0 0 0 Результат вычисле-

ния 3 0 0 0 0 1 0

Page 72: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

72

Итак, C(8, 5) = 23•71.

Program Pr12_2; Const Q=100; Type Vector=Array[1..Q] Of Integer; Var n,m:Integer; P:Vector; Procedure Solve(sn,sm:Integer; Var Sp:Vector); Begin End; Procedure Print(pn:Integer;Pp:Vector); Var i:Integer; Begin For i:=2 To pn Do If Pp[i]<>0 Then WriteLn(i:5,Pp[i]:5) End; Begin WriteLn('Ввод чисел n и m'); ReadLn(n,m); Solve(n,m,P); Print(n,P); ReadLn End. Уточним процедуру Solve: Procedure Solve(sn,sm:Integer; Var Sp:Vector); Var i:Integer; Sni,Smi,Snmi:Vector; Begin Calc(sn,Sni); {Сформировали массив, соответствующий n!} Calc(sm,Smi); {Сформировали массив, соответствующий m!} Calc(sn-sm,Snmi); {Сформировали массив, соответствующий (n-m)!} For i:=2 To sn Do Sp[i]:=Sni[i]-Smi[i]-Snmi[i] End;

Сделав “заглушку” из процедуры Calc, содержащую только Begin и End, вы получите компилируемую программу. Вариант процедуры Calc, приведенный ниже, хотя и работает, но не очень хорошо читается. Выполните трассировку этой процедуры в режиме отладки, определите назначение каждого оператора. Ваша задача — улучшить процедуру. Вспомните о том, что в материале вось-мого занятия перечислены все простые числа до 100. Procedure Calc (n:Integer; Var X:Vector); Var i,j,t:Integer; Begin FillChar(X,SizeOf(X),0); {SizeOf – вычисляет размер области памяти, выделенной для массива X; FillChar записывает нулевые значения в эту область памяти.} For i:=1 To n Do X[i]:=1; {Признаки чисел, задействованных в вычислении факто-риала числа.} For i:=4 To n Do Begin t:=i; j:=2; While (j<=(t div 2)) Do If t Mod j=0 Then Begin {j делит t без остатка.} Inc(X[j]); t:=t div j If t=j Then Inc(X[j]) End

Page 73: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

73

Else Inc(j); If t<>i Then Begin {Найдены делители числа i.} X[i]:=0;If (t<>j) Then Inc(X[t]) End End End; Запустить эту программу при больших значениях n. Пусть n ≤ 10 000. Даже если изменить тип Integer на LongInt, появится ошибка Error 202: Stack oferflow error. Нам не хватает оперативной памяти для хранения данных программы. Действительно, массивов введено много. Попробуем обойтись одним глобаль-ным массивом. {$R+} Program Pr12_2m; Const Q=10000; Type Vector=Array[1..Q] Of Integer; Var n,m:LongInt; P:Vector; Procedure Inc_0(n:LongInt); Var i,j,t:Integer; Begin For i:=2 To n Do Begin t:=i; j:=2; While (j<=(t div 2)) Do If t Mod j=0 Then Begin Inc(P[j]); t:=t div j; If t=j Then Inc(P[j]) End Else Inc(j); If t<>i Then Begin If (t<>j) Then Inc(P[t]) End Else P[i]:=1 End End; Procedure Dec_0(n:LongInt); Var i,j,t:Integer; Begin For i:=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; If t=j Then Dec(P[j]) End Else Inc(j); If t<>i Then Begin If (t<>j) Then Dec(P[t]) End

Page 74: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

74

Else Dec(P[i]) End End; Begin WriteLn('Ввод чисел n и m'); ReadLn(n,m); Inc_0(n); Dec_0(m) Dec_0(n-m) End.

Мы видим, что код процедур Inc_0 и Dec_0 почти повторяется. Это плохо. Попробуйте написать программу изящнее. Кстати, значение константы Q мож-но увеличить, например, до 30 000. Учтите, что для больших значений n и m программа может работать довольно долго.

2. В задании № 11 (Лабораторная работа №7) требовалось определить, является

ли введенное число палиндромом. Оформим “перевертывание” числа в виде функции. Function Pal(n:LongInt):LongInt; Var x:LongInt; Begin x:=0; While n<>0 Do Begin x:=x*10+n Mod 10; n:=n Div 10 End; Pal:=x End; Пусть n = 59. “Перевернем” его, получим 95. Найдем сумму чисел 59 и 95, получим 154. Перевернем” это число, получим 451. Находим сумму: 605. Еще раз: 605 + 506 = 1111. Получили палиндром. Найдите для всех натуральных чисел из интервала от 50 до 80 количество шагов, необходимых для “све-дения” их к палиндромам, с помощью описанной схемы. Program Pr12_2; Const a=50;b=80; Function Pal(n:LongInt):LongInt; Begin … End; {******** основная программа *******} Var n,t:LongInt; cnt,i:Integer; Begin For i:=a To b Do Begin cnt:=0;n:=i; While Pal(n)<>n Do Begin t:=Pal(n); n:=n+t; Write(t,' ',n,' '); Inc(cnt) End; WriteLn(i,' ',cnt) End End. Попробуйте получить палиндром из числа 89. На 16-м шаге результат по-прежнему не является палиндромом, а сумма выходит за пределы типа LongInt.

Page 75: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

75

3. Как находить наибольший общий делитель двух чисел, мы знаем (см. Лабора-торная работа №8, «Алгоритмы и данные»). Оформим эти действия в виде функции.

Function Nod(a,b:longint):longint; Begin Repeat if a>b then a:=a mod b else b:= b mod a; until (a=0) or (b=0); Nod:=a+b End; 4. Рассмотрим следующую задачу. Дано n отрезков, длины которых целые числа.

Берем два отрезка и из большего вычитаем меньший. Разность - новый отре-зок. Если же длины отрезков совпадают, то один из них исключаем из даль-нейшего рассмотрения. Продолжаем процесс. В результате получается одно число, равное длине оставшегося отрезка. Пример: n = 4, длины отрезков: 6, 15, 3, 9.

Длины отрезков Номер шага 6 15 3 9

1-й шаг 6 9 3 9 2-й шаг 6 3 3 9 3-й шаг 3 3 3 9 4-й шаг - 3 3 9 5-й шаг - - 3 9 6-й шаг - - 3 6 7-й шаг - - 3 3 8-й шаг - - - 3

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

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

1. Даны действительные числа x, y, z. Найти max(x,y,z), min(x,y,z) и max(x,y,z) + min(x,y,z), используя алгоритм – функцию нахождения max/min двух чисел.

2. Даны действительные числа x, y, z. Вычислить max( , ) max( , )1 max( ,1,15)x x y x y z

x yz+ + +

+ +, используя ал-

горитм – функцию нахождения max/min двух чисел. 3. Даны N различных действительных чисел. Определить максимальное из них, используя

алгоритм – функцию нахождения максимального для двух чисел. 4. Вычислить Z=(SignX + SignY)Sign(X+Y), используя алгоритм – функцию нахождения

Sign(a)= ⎪⎩

⎪⎨

<−=>

0,10,00,1

aaa

Page 76: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

76

5. Используя алгоритм – функцию нахождения наибольшего общего делителя двух на-туральных чисел (НОД) найти НОД: а) трех натуральных чисел; б) четырех натураль-ных чисел; в) N натуральных чисел.

6. Используя алгоритм – функцию нахождения наименьшего общего кратного двух на-туральных чисел найти НОК: а) трех натуральных чисел; б) четырех натуральных чи-сел; в) N натуральных чисел.

7. Даны два натуральных числа. Выяснить в каком из них: а) больше цифр; б) меньше сум-ма цифр, используя алгоритм – функцию для нахождения количества цифр (или сум-мы цифр)

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

9. Вычислить x= 6 6 13 13 21 212 2 2+ + +

+ + , используя соответствующий алгоритм –

функцию. 10. Дано n целых чисел. Найти среди них число, у которого приведенная ниже характеристи-

ка имеет максимальное значение: a. сумма цифр; b. первая цифра; c. количество делителей; d. сумма всех делителей.

Page 77: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

77

11. Дано n целых чисел. Найти среди них пару чисел, для которых выполняется приведен-ное ниже условие: • наибольший общий делитель имеет максимальное значение; • наименьшее общее кратное имеет наименьшее значение; • есть ли среди заданных чисел “близнецы”, т.е. простые числа, разность между кото-

рыми равна 2? 12. Дано n целых чисел. Написать программу подсчета количества чисел, в записи которых

нет цифры 8. При этом требуется использовать функцию вида: Function Yes8 (x:LongInt) :Boolean;

Без использования компьютера решите эту задачу для всех трехзначных чисел. 13. Даны два числа - n и k (k < n). Число n есть основание системы счисления, а k - количе-

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

14. Объявлен конкурс на получение грантов. В конкурсе принимают участие n человек. Гранты выдаются первым пяти участникам, набравшим наибольшее количество баллов. Один участник не может одновременно получить два и более грантов. Написать про-грамму определения количества способов, которыми могут быть распределены гранты. Исследовать область ее применимости.

15. Последовательности длины n составляются из t нулей и k единиц, причем таким образом, что никакие две единицы не находятся рядом. Подсчитать количество таких последова-тельностей (ответ: C(t + 1, k)). Найти способ перечисления всех таких последовательно-стей для небольших значений t и k. Например, для t = 3, k = 2 это последовательности 00101, 01001, 01010, 10001, 10010, 10100.

12. Тема: Рекурсия План занятия: 1. понятие рекурсии (см. Книга 1, стр. 69 – 84); 2. короткие примеры для иллюстрации рекурсии; 3. экспериментальная работа с программами: перечисления всех последователь-

ностей длины п из чисел от 1 до k; генерации перестановок чисел от 1 до п; пе-речисления всех разбиений числа n на сумму слагаемых n=a1+a2+ ... +ak , где k, a1, a2, .... ak>0;

4. выполнение самостоятельной работы. Понятие рекурсии. Рекурсией называется ситуация, когда проце-

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

Procedure Rec(t:Integer); Begin <действия на входе в рекурсию>; If <проверка условия> Then Rec(t+l); <действия на выходе из рекурсии>;

End; Необходимо осознать два ключевых момента при написании рекур-

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

Function Factorial (n: Integer): Longint; Begin

Page 78: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

78

If n=l Then Factorial :=1 Else Factorial :=n* Factorial (n-1) ; End; Найдем 5!. Как же вычисляется факториал числа? Первый вызов

функции осуществляется из основной программы, например, a:=Factorial(5), переменной а присваиваем значение 5!.

Действий на входе в рекурсию нет. Этап вхождения выделен на ри-

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

можно считать приемлемым. Procedure Generate; (* t - глобальная переменная. *)

Begin If t=n Then Exit Else Begin <действия 1>; t:=t+1; Generate; t:=t-l; <действия >; End; End;

Приведем несколько коротких примеров, иллюстрирующих технику на-писания рекурсивной логики. 1. Сложение целых чисел (см. Книга 1, стр. 76). Рекурсивное определение

операции сложения двух чисел:

⎪⎩

⎪⎨

<++−>−++=

=+0),1()1(0),1()1(

0,

bеслиbabеслиba

bеслиaba

Function Sum (а,b: Integer): Integer; Begin

Page 79: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

79

If b=0 Then Sum:=a Else If b>0 Then Sum:=Sum( a+l, b-1) Else Sum:=Sum( a-1, b+1 ); End; 2. Перевод натурального числа из десятичной системы счисления в дво-

ичную. Procedure Rec (n: Integer ); Begin If n>l Then Rec(n Div 2) ; Wri te (n Mod 2); End; Как изменится процедура при переводе чисел в восьмеричную систему

счисления? 3. Определить, является ли заданное натуральное число простым (см. Книга

1, стр. 77-78). Сформулируем по-другому. Верно ли, что заданное на-туральное число n не делится ни на одно число, большее или равное m, но меньшее n. При этом значения числа m находятся в промежутке от 2 до n. Утверждение истинно в двух случаях:

если m=n; если n не делится на m, и истинно утверждение для чисел от m+1 до n-1.

Function Simple (т,п: Integer): Boolean; Begin If m=n Then Simple:=True

Else Simple: = (n Mod m <> 0) And Simple(m+1,n) ; End; Первый вызов функции - Simple(2,n), где n - проверяемое число. 4. Дано натуральное число n≥1. Определить число а, для которого выпол-

няется неравенство: 2a-1≤n<2а. Зависимость:

⎩⎨⎧

>+=

=1,1)2(

0,1)(

nеслиdivnanесли

na

Function a( n: Integer): Integer; Begin If n=l Then a:=l Else a:=a(n Div 2) +1 ; End;

5. Для вычисления наибольшего общего делителя двух чисел можно ис-пользовать рекурсивную функцию вида: Function Nod (a, b: Integer):Integer; Begin

If (a=0) Or (b=0) Then Nod:=a+b Else If a>b Then Nod:=Nod (a-b,b) Else Nod:=Nod(a,b-a);

End; 6. Нахождение максимального элемента в глобальном массиве А реализуется

с помощью следующей рекурсивной логики. Procedure Max_Vect_A(n:Integer; Var x:Integer); Begin If n=l Then x:=A[l] Else Begin Max_Vect_A(n-1,x); fA[n]>x Thenx:=A[n]; End; End; 7. Возведение целого числа а в целую неотрицательную степень п ре-

курсивно реализуется так. Function Pow(a,n:Integer):Integer;

Page 80: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

80

Веgin If n=0 Then Pow:=l Else If 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 n:Integer; Begin ReadLn (n); If n<>0 Then Solve; Write (n:5) ; End; 9. Каждое число Фибоначчи равно сумме двух предыдущих чисел при ус-

ловии, что первые два равны 1 (1, 1, 2, 3, 5, 8, 13,21,...). В общем виде n-е число Фибоначчи можно определить так:

⎩⎨⎧

>−+−==

=2),2()1(

21,1)(

nеслиnФnФnилиnесли

Function Fib(n:Integer):Integer; Begin If n<=2 Then Fib:=l Else Fib:=Fib(n-l)+Fib(n-2) ; End; 10. Найти сумму первых n членов арифметической (геометрической)

прогрессии. Арифметическая прогрессия определяется тремя пара-метрами: первым элементом (а), разностью между соседними элемен-тами (d) и количеством членов прогрессии (п). Очередной элемент про-грессии вычисляется из предыдущего прибавлением разности — aновое:=aстарое+d. Function Sa (n, a: Integer):Integer; Begin If n>0 Then Sa:=a+Sa (n-l,a+d) Else Sa;=0; End;

Примечание.Попробуйте убрать ветвь Else и объяснить полученный результат. Для приве-денных примеров прорисуйте схемы работы процедур и функций для конкретных входных данных (не очень больших, естественно).

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

п из чисел от 1 до k. Например, п=3, k=2. Количество последовательностей — kn. Действительно, на каждое из n мест разрешается записывать числа от 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. До какой-то позиции они совпадают, в данном случае до второй (несовпадение возможно и в первой позиции), а в этой позиции число во второй последовательности больше, чем число в первой последовательности. Такой порядок называется лексико-графическим. Как из очередной последовательности получать следующую в лексикографическом порядке? Идем справа. Находим первый элемент, не

Page 81: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

81

равный k. Увеличиваем его на единицу, а «хвост» последовательности мак-симально ухудшаем, т. е. записываем 1. В последней последовательности сделать такое преобразование, естественно, не представляется возможным. П р и м е ч а н и е . Одномерный массив из п элементов - очевидная струк-тура данных для хранения последовательности. Тип элементов в массиве можно определить как обычно Integer, но давайте чуть-чуть изменим: Type Vector = Array[l..n] Of 0..k; Мы ввели ограничение на тип Integer. Допусти-мыми значениями элементов массива А являются целые числа в интервале от 1 до k, и компьютер контролирует выход их за допустимый диапазон (ис-пользовался ограниченный тип данных ли на предыдущих занятиях?). Оп-ределим «костяк» программы и очевидную процедуру вывода элементов массива.

{$R+} Program Prl3_l; Const n=3; k=2; Type Vector =Array[l..n] Of 0. .k; Procedure Print; Var i:Integer; Begin For i:=l To n Do Write (A[i] : 3) ; WriteLn; End;

Procedure Solve(t:Integer); Begin End; {**************************************}

Var A:Vector; Begin FillChar(A,SizeOf(A),0){*0чистка. *} ; Solve (1); End.

Из первого вызова рекурсивной процедуры Solve возникает вопрос - что оп-ределяет параметр рекурсии t? Позицию в последовательности (номер эле-мента в массиве А). Следующий вызов - Solve(t+l), при значении t, большем n, необходимо выводить очередную последовательность, а иначе - записать очередной элемент, он определяется значением управляющей перемен-ной i, в позицию t. Procedure Solve(t:Integer); Var i:Integer; Begin If t>n Then Print Else For i:=l To k Do Begin A[t]:=i; Solve (t + 1) ; End; End;

Измените программу так, чтобы последовательности выводились в следующей очередности: 2 2 2, 2 2 1, 2 1 2, 2 1 1, 1 2 2, 121, 1 1 2, 1 1 1.

Предположим, что значение k больше значения п. Изменить программу. В пследовательности чисел i-й элемент не должен превосходить i. Это простое изменение программы. А сейчас, при этом же предположении (k>n), требуется сгенерировать возрастающие последовательности чисел. На месте t в последо-вательности допускается запись чисел из интервала от A[t-1]+1 до k-(n-t). Дей-

Page 82: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

82

ствительно, t=n, максимальное число в позиции n равно, естественно, k; t=n-l, максимальное число в позиции п-1 есть k-1 и т. д. Это мысль приводит к сле-дующему изменению процедуры Solve. Procedure Solve(t:Integer); Var i:Integer; Begin If t>n Then Print Else For i:=A[t-l]+l To k-n+t Do Begin A[t]:=i; Solve (t+1); End; End;

Однако только этим изменением в программе ограничиться нельзя. Первый вызов процедуры Solve(l) и описание типа Туре Vector =Аrrау[1..n] Of l..k; приводит к знакомой ошибке Error 201: Range check error, в строке For i;=A[t-lJ+l To k-n+t Do. Причина очевидна - выход индекса за пределы массива А. Изменим описание типа на Type Vector=Array[0..n] Of l..k;. Программа выда-ет результат, но содержит логическую неточность. При первом вызове Solve используется неопределенное значение A[t-l], т. е. 1 прибавляем неизвестно к чему. При изменении первого вызова на А[0]:=0; Solve(l); приходим к но-вой ошибке - Error 76: Constant out of range (значение константы выходит за до-пустимый диапазон). Еще одно изменение - Type Vector=Array[0..n] Of O..k; и можно, в первом приближении, закончить эксперименты с программой.

2. Что такое перестановки чисел от 1 до п, определено на предыдущем заня-тии. Необходимо написать рекурсивную процедуру генерации перестановок чисел от 1 до n. В чем здесь суть рекурсии? Фиксируем на первом месте оче-редное число и генерируем все перестановки этого вида. При этом посту-паем точно по такой же схеме. Фиксируем число на втором месте и генерируем все перестановки уже с фиксированными элементами на первом и втором мес-тах. Пусть n равно 3. Перестановки должны генерироваться в следующей последовательности: 1 2 3, 1 3 2, 2 1 3, 2 3 1, 3 2 1, 3 1 2. Если не очень понят-но, то рассмотрим при n=4. Вместо многоточия запишите генерируемые пе-рестановки 1234,1243,1324,1342,1432,1423, 2134, 2143, 2314, 2341,…, 4123. Необходимо осознать, что после генерации всех перестановок с фиксирован-ным элементом в позиции t перестановка возвращается в исходное состояние и осуществляется новый обмен значениями элемента из позиции с номером t+1 и i+1. Program Prl3_2; {Процедура Print совпадает с соответствующей процедурой из предыдущей програм-мы} Const n=4; Type Vector =Array[1..n] Of Integer; Procedure Swap (Var a,b:Integer); Var с:Integer; Begin с:=a; a:=b;b:=c; End; Procedure Solve(t:Integer) ; Var i:Integer;

Page 83: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

83

Begin If t>=n Then Print Else For i:=t+l To n Do Begin Swap(A[t+l] ,A[i]) ; Solve(t+1); Swap(A[t+l] ,A[i]) ; End; End; {********************} Var A:Vector,: i:Integer; Begin For i:=l To n Do A[i]:=I {*Первая перестановка — 1 2 3 ...n. *} Solve(0); {*Первый вызов. *} End.

Для простоты будем считать, что каждый рекурсивный вызов приводит к созданию «копии» процедуры. Это не так, но нам легче при этом предположе-нии формулировать вопросы. Сколько «копий» процедуры Solve создается при п=3? Сколько раз создаются копии с t=2? Сколько выполняется лишних обме-нов типа A[j]⇔A[j]7 На рисунке приводится часть схемы вызовов (обозначено стрелками) процедуры Solve (при п=3). Для ответа на сформулированные во-просы закончите рисунок.

Измените описание типа элементов у массива А на: Туре Vector=Array[l..n] Of l..n; (ограниченный тип). Возникает ошибка

(Error 26: Type mismatch) при вызове процедуры Swap. Устраните её. Не ис-ключено, что Вам придется ввести ещё один тип данных. 3. Дано натуральное число n. Необходимо получить все разбиения числа n

на сумму слагаемых n=a1+a2+ ... +ak, где k, a1, а2, ..., ak>0. Будем считать разбиения эквивалентными, если суммы отличаются только порядком слагаемых. Множество эквивалентных сумм представляем последо-вательностью а1, а2, ..., ak, где а1 >а2 > ... >ak. При n=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 определяют как обратный лексикогра-фическому. Наша задача - генерировать разбиения в последнем

Page 84: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

84

порядке. Рекурсивная схема реализации просматривается достаточно легко. Мы записали в позицию t число аt . Сумма чисел a1+a2+...+at=s, тогда с позиции t+1 мы генерируем все разбиения числа n—s, причем для элемента (и остальных) должно выполняться неравенство A[t+l]≤A[t]. Переход к следующему разбиению в позиции t сводится, если это возможно, к вычитанию 1. Уточним некоторые технические детали. Разбиение хранится в глобальном массиве А. Количество элементов в разбиении различно. Значение переменной t определяет текущий размер выводимой части массива А. Процедура Print претерпит небольшое изменение.

Procedure Print(t:Integer); Var i:Integer; Begin For i:=l To t Do Write (A[i] :3) ; WriteLn; End; Рекурсивная процедура Solve имеет два параметра: число п и позицию t, начи-ная с которой необходимо получить все разбиения числа п. Первым разбие-нием является разбиение, соответствующее записи числа п в позицию t, а за-тем ... Последовательно записываем в позицию t числа п-1, п-2 ..., и так до 1, а с позиции t+1 генерируем все разбиения чисел 1, 2, ..., п-1 соответственно. Приведем текст решения. {$R+} Program Pr3_3; Const п=4 ; Type Vector=Array[1..n] Of Integer; Var A:Vector,: Procedure Solve(n,t:Integer); Var i:Integer; Begin If 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+1); End; 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 (n, t -.Integer) ; Var I:.Integer; Begin If n=l Then Begin A[t]:=1;Print (t);End Else Begin If n<=A[t-l] Then Begin A[t]:=n; Print (t) End; For i:=l To n-1 Do Begin A[t]:=n-i; Solve(i,t+l); End; End; End;

Page 85: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

85

Появление индекса t-1 требует изменить описание типа массива на Type Vector =Array[0..n] Of Integer; и осуществлять начальное присвоение А[0]:=п перед первым вызовом Solve (n,1). Не внесение этих изменений приводит к из-вестной ошибке Error 201: Range check error. Что мы пытались сделать? Отсечь лишние разбиения при их выводе, но не генерации! Запуск программы нас очередной раз разочарует. Результат работы: 4, 3 1, 2 2, 2 1 1, 1 2 1, 1 1 1 1. Из-меним процедуру. Procedure Solve(n,t:Integer); Var i,q:Integer; Begin If n=l Then Begin A[t]:=1;Print(t)End Else Begin If n<=A[t-l] Then Begin A[t] :=n;Print (t) End; q:=Min (n-l,A[t-l]); For i:=q DownTo 1 Do Begin A[t]:=i; Solve (n-i, t+1) ; End; End; End; Она потребует функцию Min, но ничего страшного, вставьте ее в текст реше-ния.

Function Min (a,b:Integer):Integer; Begin If 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.

Примечание. При отладке программы и достижении понимания её работы Вам очень поможет отладчик системы программирования.

Задания для самостоятельной работы 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 3 (8 раз) 4 4 4 4 (4 раза) 3 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), причем за один раз можно переносить только од-но кольцо, кроме того, запрещается помещать большее кольцо над меньшим. Данная задача своим происхождением обязана индусской легенде, которая рассказывает, что в большом храме Бенареса бронзовая плита поддерживает три алмазных стержня, на один из которых бог нанизал во время сотворения мира 64 золотых диска. С тех пор день и

Page 86: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

86

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

Выполните трассировку приведенного ниже решения с использованием отладчика системы программирования. Убедитесь в правильности решения. Попробуйте написать не рекур-сивную программу решения задачи. Program Prl3_zl; Procedure MoveTown(High,FromI ,ToI ,WorkI: Integer); Begin If High>0 Then Begin MoveTown (High-1,FromI,WorkI, Tol) ; Writeln(‘Перенести кольцо №’,High,’ с иглы ‘,FromI,’ на иглу ‘ ,ToI) ;

MoveTown(High-1,WorkI,Tol,FromI) ; End; End; {***********************} Var n: Integer; Begin Write(‘Количество колец = ‘); ReadLn (n); MoveTown(n,l,3,2) ; End.

3. Составить рекурсивную функцию вычисления значения функции Аккермана для неот-

рицательных чисел n и m, вводимых с клавиатуры.

⎪⎩

⎪⎨

>>−−=≠−

=+=

0,0)),1,(,1(0,0),1,1(

0,1),(

mnmnAnAmnnA

nmmnA

Найдите одну пару значений n и m, при которых возникнет переполнение стека адресов возврата. 4. Функция F(n) определена для целых положительных чисел следующим образом:

⎪⎩

⎪⎨

==∑=

n

inidivnF

nnF

2

2),(

1,1)(

Верно ли следующее решение? Function F(n:Integer):Integer; Var i,s:Integer;

Begin If n=l Then s:=l Else For i:=2 To n Do s:=s+F(n Div 2) ; F:=s; End; Можно ли вычислить с помощью этой реализации функции значения F при n=5, 6, 7, ..., 20. 5. Число сочетаний С(п,т) вычисляется по формуле С(п,т)= =С(п-1,т-1)+С(п-

1,т), при этом С(п,0)=1 и С(п,п)=1. Напишите рекурсивную функцию для под-счета числа сочетаний. Определите область её применения (диапазон допустимых значений n и т).

Page 87: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

87

Материал для любознательных

1. Продолжим рассмотрение понятия алгоритма. В математике это строгое понятие и

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

2. Нормальные алгоритмы А. А. Маркова. А. А. Марковым дано строгое с математической точки зрения определение алгоритма. Что это дает? Тезис А. А. Маркова: «Если для ре-шения задачи есть алгоритм, то этот алгоритм может быть представлен в виде нормального алгоритма». Тезис не доказывается, ибо, с одной стороны, есть нечеткое, нестрогое по-нятие алгоритма, а с другой стороны, строгое определение нормального алгоритма. Те-зис может быть опровергнут только конкретным примером, которого пока не найдено и, видимо, не будет найдено. Далее. Есть задача. Для этой задачи доказывается (строгое математическое доказательство), что для неё не может быть построена, создана нормаль-ная схема. Вывод — данная задача не имеет алгоритма решения.

3. В заключение отметим , что нормальные алгоритмы А. А. Маркова не единст-венная схема уточнения понятия алгоритма, придания ему статуса строгого математиче-ского понятия со всеми вытекающими из этого факта последствиями. Известны алгорит-мическая система Э. Поста (1935 г.), алгоритмическая система А. М. Тьюринга (1937 г.), ап-парат частично рекурсивные функции и т. д .

14. Тема: Символьный и строковый типы данных План занятия: 1. cимвольный тип данных (см. Книга 1, стр. 23-25); 2. короткие программы иллюстрации работы с символьным типом данных; 3. cтроковый тип данных (см. Книга 2, стр. 25-29); 4. процедуры и функции работы со строковым типом данных(см. Книга 2, стр 27-

28); 5. экспериментальная работа с программами:

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

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

Символьный тип данных. Идентификатором типа является заре-зервированное слово Char. Значениями типа Char являются элементы конечного и упорядоченного множества символов, т. е. на множестве значение типа Char есть отношение порядка. Американский стандарт-ный код для обмена информацией (ASCII - American Standard Code for In-formation Interchange) есть система кодирования, в которой алфавитные,

Page 88: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

88

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

Символы с кодами от 0 до 127 составляют так называемую основную табли-цу кодов ASCII. Эта часть идентична на всех IBM-совместимых компьютерах.

Так, цифрам от 0 до 9 соответствуют коды от 4810 (001100002) до 5710 (001110012);

буквам латинского алфавита от А до Z - коды от 6510 (010000012) до 9010 (010110102);

буквам от а до z - коды от 9710 (011000012) до 12210 (011110102); буквам русского алфавита от А до Я - коды от 12810 (100000002) до 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(a)=Chr(Ord(a)+l).

Короткие программы

1. В первом примере выводятся символы и соответствующие им коды. Пе-

ременная k используется как счетчик для организации последовательного вывода — по 5 символов. Program pr14_1; Var ifk:Integer; Begin k:=0 ; WriteLn('Вывод порядковых номеров (кодов) символов - значение перемен-ной i и самих символов.'); For i:=l To 255 Do Begin Write (i:4,' Символ ',Chr(i)); Inc(k); If k=5 Then Begin WriteLn; k:=0; End; End; End.

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

WWW...WWW (23 раза) Program Pr14_2; Var i :Char; j: Integer; Begin

For i: = 'A' To 'W' Do Begin For j:=l To Ord(i)-Ord('A')+l Do Write (i); WriteLn; End;

End. Определите, что выводится на экран в результате следующей не очень

значительной модификации программы. Program Pr14_2m; Var i,j:Char;

Page 89: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

89

Begin For i:='a' To 'z' Do For j:='a' To i Do Write (j); ReadLn; End.

3. В следующем примере подсчитывается количество символов, введен-ных с клавиатуры. Ввод заканчивается символом '.' Вы вводите не-сколько символов, затем точку и нажимаете клавишу Enter. Программа выдает правильный результат. А если нажимать клавишу Enter после ввода каждого символа? Результат неверный, он как будто бы в три раза пре-вышает истинный результат. На самом деле все верно. Нажатие клавиши Enter генерирует ввод еще двух символов (управляющих) — возврата карет-ки (код 13) и перевода строки (код 10). Program pr14_3; Var i:Char; j:Integer; Begin Read (i) ; j :=0; While i<>'.' Do Begin Inc (j); Read(i) End; WriteLn(j) ; End. Другая версия этой простой программы позволяет отказаться от символа

точка как признака конца ввода данных. Символ # перед целым числом го-ворит о том, что это число следует рассматривать как ASCII код символа.

Program Pr14_3m; Var i:Char; j:.Integer; Begin

Read(i) ; j:=0; While i<>#13 Do Begin Inc (j) ;Read (i) ;End; WriteLn (j) ; ReadLn;

End. А если изменить строку на While i<>#10 Do Begin Inc(j); Read(i);End;, то

количество подсчитанных символов на единицу больше. Объясните резуль-тат. 4. В следующем примере подсчитывается количество цифр во вводимых с клавиатуры данных. Program Prl 4_ 4; Var ch:Char; k:Integer; Begin Read (ch); k:=0; While ch<>#13 Do Begin IF (ch>=’0’) And (ch<='9') Then Inc(k); Read(ch) ; End; WriteLn('Количество цифр равно ' ,k) ; End.

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

целого числа; • вычисляла сумму цифр введенного числа. Функция Upcase(ch) преобразует значение переменной ch символьного

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

Program Prl4_4m; Var ch:Char;

Page 90: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

90

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. При-меры описания переменных типа String: Var Strl: String[10]; Str2: String; Str3:String[13]. В квадратных скобках указывается максимальный размер (длина) строки. Если он не указан, то длина строки считается равной 255 символам. Заметим, что строку можно рассматривать как одномерный массив из символов - к каждому символу строки допустимо обращение по его номеру (Strl[i] - это обращение к i-му элементу строки Strl). Первый символ строки (с индексом 0) содержит фактическую длину строки. Пере-менные типа String выводятся на экран монитора посредством стандарт-ных процедур Write и WriteLn и вводятся с помощью ReadLn и Read. To есть вводятся и выводятся не поэлементно, как массивы, а сразу цели-ком. Следующий простой пример иллюстрирует сказанное.

Program Prl4_5; Var s:String; w:String[10]; v:String[5]; i,j:Integer; Begin

ReadLn(v); WriteLn (v); WriteLn(Integer(v[0])); ReadLn (w); WriteLn(w); WriteLn(Ord(w[0])); ReadLn(s); WriteLn (s); WriteLn (Integer (s[0])); For i:=l To Ord(s[0]) Do Begin For j:=1 To i-1 Do Write (' ') ; WriteLn (s [i]) ; End; ReadLn; End.

Вы вводите строки v и w большей длины, чем указано в описании. Вы-вод на экран показывает, что при этом они «обрезаются». Операторы WriteLn(Integer(v[OJ)) и WriteLn(Ord(w[0])) обеспечивают вывод значения длины строки. Если Вы измените первый оператор на WriteLn(v[0]), то вместо цифрового значения на экран выводится «непонятный» символ. Попробуйте объяснить этот результат и понять смысл преобразования Integer(v[0]). Следующие операторы примера демонстрируют посимволь-ное обращение к строке - строка рассматривается как s:Array[0..255] Of Char;. Вывод символов строки s на экран осуществляется «лесенкой». Сравнение строк происходит посимвольно слева направо: сравнива-

ются коды соответствующих символов до тех пор, пока не нарушится равенство, при этом сразу делается вывод о знаке неравенства. Две строки называются равными, если они равны по длине и совпадают посимвольно. 1. Подсчет количества символов (параметр q) в строке (параметр st).

Function QChar(q:Char;st: String): Byte; Var i, k: Byte; Begin k:=0; For i:=l To Length (st) Do If st[i]=q Then Inc(k) ; QChar:=k; End;

2. Удалить среднюю букву при нечетной длине строки и две средние буквы при четной длине строки. Procedure MiDel( Var st: String); Var k: Byte; Begin

Page 91: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

91

k:=Length (st); If k Mod 2=1 Then Delete (st, k Div 2+1,1) Else Delete (st,k Div 2,2) ; End;

3. Заменить все вхождения подстроки w в строке st на подстроку 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 I, k, d, s: Integer; Begin s:=0; For i:=l To Length (st) Do Begin Val (st[i],d,k) ; If k=0 Then s:=s+d; End; Sum:=s; End;

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

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

Program Pr14_б; Const n=20, m=l0; ( * Количество слов в тексте и количество букв в слове . Естественно , что эти параметры программы допускается из-менять. *) Type TString=String[m]; Procedure DelPr(Var s:String); {Удаляем пробелы в начале текста .} Begin While (s[l] = ' ') And (s<>") Do Delete (s,l ,1) ; End; Function GetWord(Var s:String) :TString,{Выделяем слово , при этом оно уда-

ляется из текста , и убираем пробелы после слова. } Begin GetWord:=Copy (s, l, Pos(' ' ,s)-l) ; Delete(s,1,Pos (' ',s)); DelPr (s) ; End;

{*****************************************************} Var A:Array[1..n] Of TString; s: String; k, I:.Integer; Begin WriteLn('Введите текст'); ReadLn (s); s:=s+' ';{Добавляем символ пробела в конец текста . Зачем?} DelPr(s);{Удаляем пробелы в начале текста. } k:=0; {Пока текст не пустой.} While s<>'' Do Веgin Inc(k) ;A[k]:=GetWord (s);{Берём слово из текста. } End;

For i:=l To k Do WriteLn(A[i]); ReadLn; End. Удалите вызов процедуры DelPr(s) из основной программы, а в функции

GetWord переставьте этот вызов в её начало. Что изменится в работе про-граммы? На каких исходных данных она не будет правильно работать?

Page 92: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

92

Что произойдет, если в конец текста не добавлять символ пробела? В проце-дуре DelPr измените While (s[l]=' ') And (s<>") Do Delete(s,l,l) на While (s[l]=' ') Do Delete(s,l,l). Найдите пример исходного текста, при котором результат работы программы окажется неверным или его вооб-ще не будет. Продолжите эксперименты с программой. 2. По правилам машинописи после запятой в тексте всегда ставится пробел.

Программа исправляет такой тип ошибки в тексте. Program Pr14_7; Var i : Integer; s: String; Begin WriteLn ('Введите текст'); ReadLn (s) ; I:=1; While i<Length (s) Do Begin If (s[i] = ',’) And Not (s [i+l]=' ') Then Insert(‘ ‘,s,I+1);

Inc (i) ; end; WriteLn (s) ;

End. Почему выбран оператор While, а не For? Почему в теле цикла нельзя ис-

пользовать функцию Pos(' ',s)? Обратите внимание, если запятая — последний символ текста, то мы не добавляем пробела. Измените программу для ис-правления ошибки типа «после символов '!', '?', '.' должен стоять пробел, а затем текст начинается с заглавной буквы». Вспомните еще ряд традици-онных ошибок и модифицируйте программу для их исправления. 3. Даны две строки X и Y. Назовем расстоянием (r) между X и У количество

символов, которыми X и Y различаются между собой. Например: X='abcd', Y='dxxc’, r=4; Х='1111111’, Y='111222',r=7. Написать программы вычис-ления расстояния между строками.

Program Pr14_8; Var i , j , r : Integer ; S, X, Y :String; Begin

WriteLn ('Первая строка' ) ; ReadLn (X) ; WriteLn ('Вторая строка' ) ; ReadLn (Y) ; If Length(X)>Length(Y) Then Begin S:=X; X:=Y; Y:=S; End;{*Y строка большей длины.*} r:=Length(Y) ; For i:=l To Length (X) Do Begin While (j<=Length(Y) ) And (Y[j]<>X[i]> Do Inc(j) ; If j>Length(Y) Then Inc(r)

Else Begin Dec(r); Delete(Y,j,1);End;{*Есть совпадение . Уменьшаем расстояние и удаляем символ из Y. *} End;

WriteLn ('Расстояние ' , г ) ; End. К каким последствиям приведет исключение из решения строки со

сравнением длин строк и записи в Y строки наибольшей длины? Проверьте. Исключим оператор Delete(Y,j,l). Что за результат у нас будет получаться? Измените программу так, чтобы в тексте находилось два слова, рас-стояние между которыми имеет максимальное (минимальное) значе-ние. 4. Дана строка не более чем из шести произвольных различных символов.

Разработать программу вывода всех возможных подмножеств (под-строк), составленных из символов данной строки. Количество подстрок 2"-1, где n — количество символов в строке. Естественно, что каждый сим-вол входит в подстроку не более одного раза. Будем генерировать шес-тиразрядные двоичные числа, так, 010101 соответствует подстрока 'bdf строки 'abcdefh'. Принцип генерации: просматриваем число слева направо до первого нуля, заменяя при этом все 1 на 0, например после 110011 по-лучаем 001011. Завершение генерации — число 0000001.

Page 93: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

93

Program Pr14_9; Const MaxN=6; Type Vector=Array[l. .MaxN+1] Of Byte; Function Eg(X,Y:vector ;n:Integer): Boolean; {*Сравнение текущего значения с 0000001, если n=6. *} Var i :.Integer; Begin i : =1 ; While (i<=n) And (X[i]=Y[i]) Do Inc(i);

Eq:=(i>n) ; End; {***********************************} Var A,Last:vector; i:Integer; s:String; 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 : =1 ; While (i<=Length (s)) And (A[i]=l) Do Begin A[i]:=0;Inc (i) ;End; A[I]:=1; If I<=Length (s) Then For i:=l To Length (s) Do If A [I]=1 Then Write (S[i] ) ; (*Вывод подстроки. *} WriteLn; End; ReadLn; End.

В схеме генерации реализовано обычное прибавление единицы к двоичному числу, только число записывается слева направо: 000000, 100000, 010000, 110000, 001000 и т. д. Каждому такому числу ставится в соответствие подстро-ка. Измените схему генерации так, чтобы единица прибавлялась традицион-ным способом: 000000, 000001, 000010, 000011, 000100 и т. д. А можно ли ис-ключить генерации последовательности двоичных чисел из решения задачи?

Задания для самостоятельной работы 1. Написать программу вывода на экран последовательности символов:

ZYYXXX…AA..AA; ABC…ZZBC…ZZZC…ZZ..ZZ.

2. Составить программу, которая выводит True, если в строке буква А встречается чаще, чем буква В, и False в противном случае.

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

4. Подсчитать количество гласных латинских букв в строке. 5. Удвоить вхождение некоторой буквы в текст. Например, текст «мама папа» должен иметь

вид — «маамаа паапаа». 6. Даны две строки. Вывести буквы, встречающиеся и в той и в другой строках. 7. Дан текст. Вывести все слова, начинающиеся с согласных букв латинского алфавита. 8 . Дан текст. Определите:

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

Page 94: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

94

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

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

дующему правилу: удалить из слова все предыдущие вхождения последней буквы; оставить в слове только первые вхождения каждой буквы.

12. Дан текст. Вывести слова, которые отличны от последнего слова и удовлетворяют сле-дующему условию: в слове нет повторяющихся букв; буквы слова упорядочены по алфавиту; слово совпадает с начальным отрезком латинского алфавита (a, ab, abc, abcd,...).

13. Дан текст. Вывести все слова, предварительно выполнив преобразования их по правилу: заменить во всех словах первую букву на заглавную.

14. Дан текст. Написать программу проверки правильности написания сочетаний «жи», «ши», «ча», «ща», «чу» и «щу». Исправить ошибки.

15. Даны три строки. Определить, можно ли из символов первых двух строк полу-чить третью строку.

16. Даны две строки. Определить можно ли, переставляя символы в первой строке, получить вторую строку.

15. Тема: Вещественный тип данных План занятия: 1. вещественный тип данных (см. Книга 1, стр. 27-29); 2. короткие программы иллюстрации работы с вещественным типом данных; 3. экспериментальная работа с программами:

• решения уравнения f(x)=0; • вычисления площади фигуры; • вычисления элементарных функций с использованием представления этих

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

Вещественный тип данных. Числа записываются в виде мантиссы (дробной части числа), умноженной на порядок (степень десяти, обознача-ется буквой Е). Существуют следующие типы вещественных чисел: Real, Sin-gle, Double, Extended, Соmp (наиболее используемый — Real).

Примечание Величины типа данных Сотр принимают целочисленные значения, в из-

вестной степени этот тип является расширением типа Longlnt до 19 разрядов. Допустимые арифметические операции: '+' сложение, '-' вычитание, '*' ум-

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

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

пример 127.3, 25.0, -16.003, 200.59, 0.54 • с использованием символа Е, например 0.000009 – 9Е-6, 0.62*104 –

0.62Е+4, -10.8*1012 - -10.8Е12

Page 95: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

95

Короткие программы. Первый пример связан со сравнением двух веще-ственных чисел. Запустите эту простую программу.

Program pr15_1; Var x,y:Real; Begin

х:=0.3;у:=0.3; WriteLn(x=y) ; ReadLn; End. Получен ожидаемый результат — значение True. Изменим описание

переменной у на у:Single. Запуск программы не пройдет, есть ошибка Er-ror 116: Must be in 8087 mode to compile this.1 Суть в том, что выполнение арифметических операций с вещественными числами осуществляется по-другому, чем с целыми числами. Их реализация требует больших затрат процессорного времени. С этой целью созданы специальные бы-стродействующие сопроцессоры для выполнения операций с числами вещественного типа.

Директива {$N+}. Данная директива компилятора осуществляет переключение между двумя

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

Итак, при использовании дополнительных вещественных типов данных требуется записывать директиву {$N+} в начало программы. Вставим её и до-бавим еще два оператора. Текст программы имеет вид:

{$N+} Program prl5_lm;

Var x:Real; у:Single; Begin

x:=0.3;y:=0.3;WriteLn (x=y); WriteLn(x); WriteLn (y); ReadLn; End. Результат сравнения есть False. Причина очевидна — для хранения значе-

ний х и у выделяется разное количество разрядов. Отметим еще один момент. При работе в режиме {$N+} стандартная проце-

дура Write осуществляет вывод в формате Extended, так же как и значения, воз-вращаемые стандартными функциями, перечисленными выше по тексту. Сравните работу следующих фрагментов программ (sin(30°)=sin(0.5236...)=0.5) и проведите еще одно небольшое исследование. Задайте в разделе Const константы а и b. Затем, вместо оператора WriteLn(Pi) напишите WriteLn(Pi:a:b). Найдите зависимость между изменением значений a, b и результатами вывода числа Pi на экран.

{$N-} Program pr15_2; Begin WriteLn (Sin (0. 5236) ) ; WriteLn (Pi); End. {$N+} Program prl5_2; Begin WriteLn(Sin(0.5236)); WriteLn(Pi) ; End. Продолжим наши примеры. Известно, что (х/а)*а=х. Проверим это ут-

верждение с помощью следующей простой программы {$N+} Program pr15_3;

Page 96: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

96

Var x:Extended; i, cnt:Integer; Begin cnt:=0;

For i:=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+) Program pr15_3; Const eps=l.0Е-10; Var х:Extended; i,cnt:Integer;

Function Eq(x ,y:Real):Boolean; Begin

Eq:=Abs(x-y)<eps; End; Begin cnt:=0; For I:=20000 To 30000 Do Begin x:=1/10000; x:=x*10000; If Eq(x,i) Then inc(cnt);End;

WriteLnC 'Число совпадений ' ,cnt) ; WriteLn('Число несовпадений ',10001-cnt); ReadLn;

End. Результат работы программы — 10001 совпадение.

Экспериментальный раздел работы 1. Приближенное решение уравнения f(x)=0, где f(x) — заданная функция. Ре-шить уравнение — значит найти такое значение х*, при котором f(x*)=0. По-иск решения осуществляется на интервале [а,b], причем f(a)<0, a f(b)>0. По-иск такого интервала — отдельный вопрос, мы его не рассматриваем. Для решения используем одну из фундаментальных идей информатики — «разделяй и властвуй», в данном случае «метод деления пополам». Находим точку с — половину отрезка [а,b]. Если f(c)>0, то границу b изменяем на зна-чение с, а если f(c)<0, то изменяем а. Процесс продолжаем, пока длина ин-тервала не будет меньше заданной точности.

Пусть f(x)=x2-2, т. е. мы попытаемся найти значение квадратного корня из 2. Решение. {$N+} Program pr15_5;

Page 97: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

97

Const eps=1.0E-3; Function Eq(x,y:Real):Boolean; {*Функция рассмотрена выше. *}

Function F (x:Real):Real; Begin

F:=Sqr(x)-2; End; {***************************}

Var a,b,c:Real; Begin WriteLn('Введите интервал a<b ') ; ReadLn (a,b);

If F(a)*F(b)>0 Then WriteLn('Ha этом интервале мы не можем найти решение уравнения. ') Else Begin While Not Eq(a,b) Do

Begin c:=(a+b)/2; If F(c)>0 Then b:=c Else a:=c; End;

WriteLn('Результат ', a) ; End; ReadLn;

End. Обратите внимание на то, что вычисление f(x) оформлено как отдельная

функция. Использование программы для других данных требует лишь ее из-менения. Запуск программы при различных значениях а и b Вас не очень об-радует. Получаются значения 1.4140625 или 1.41357421875 (для различных ин-тервалов а и Ь), а фактическое значение равно 1.414213... Измените значение eps на 1.0Е-6. Точность вычислений имеет большое значение при работе с ти-пом данных Real. Исследуйте еще ряд функций с помощью приведенной про-граммы:

• х2-6х+5=0 • x-cos(x)=0 • х-ln(х)-2=0 • 2х3-9х2-60х+1=0

Возникает вопрос, как определять интервал [а,b], удовлетворяющий ус-ловиям задачи? А начните с таблицы, вначале составляйте её вручную (например, для первой функции):

X 0 2 3 6

Знак F(x) - - - + После ряда попыток Вы вспомните, что разумная лень - двигатель про-

гресса, так это называется, и автоматизируете процесс составления табли-цы, т. е. добавите к программе еще одну процедуру. 2. Пусть f(x) - непрерывная положительная функция на отрезке [а,b] (а<b). Вычислим площадь фигуры, ограниченную графиком функции f(x), осью х и прямыми х=а и х-b. Для этого разобъём отрезок [а,b] на п равных от-резков одинаковой длины ∇x=(b-a)/n: a=x(0)<x(l)<...<x(i)<x(i+l)< <х(п)=b. На каждом из отрезков [x(i),x(i+l)} как на основании, построим прямоугольник с высотой f(x(i)). Площадь прямоугольников мы умеем считать. Сумма площадей всех прямоугольников даст приближенное значение площади фигуры. Общая формула: Sприбл = (b-a)/n* (f(x(0)+f(x(1))+...+f(x(n-1)). Естественно, что чем «мельче» будет разбие-ние, тем точнее мы подсчитаем площадь фигуры. А сейчас вспомните один из тезисов нашей работы — «все подвергай сомнению, все проверяй». Где неточность в вышеизложенном материале?

Page 98: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

98

Для отладки программного решения возьмем простую функцию f(x)=x2

на интервале от 1 до 2. Площадь фигуры равна 2.333... {$N+;} Program pr15__6; Const eps=1.0E-3; Var a,b, s ,dx:Real;i ,n : Integer ; Function Eq(x,y:Real) : Boo lean; {* Функция рассмотрена выше. *} Function F (х : Real) : Real ; Begin F:=Sqr(x); End;

Begin WriteLn ('Введите интервал и число частей, на которые он разбивается'); ReadLn (a,b,n) ;

x:=a; dx:= (b-a) /n; s:=0; For i:=l To n-1 Do Begin s:=s+F(x); x:=x+dx; End; WriteLn (' Результат ' , (b-a)/n*s) ; ReadLn;

End. Запускаем программу при фиксированных границах интервала и различных

значениях п. Результаты удручают. При п=6 он равен 1.5277..., при n=50 — 2.224992..., при n=100 - 2.78749..., при n=300 - 2.315046... Давайте изменим ре-шение. Будем сравнивать два соседних приближенных значения площади для различных n. Если их разность больше заданной точности вычисления, то увеличим значение n и снова подсчитаем площадь. Начальное значение n в про-грамме взято 10, шаг изменения 100. Естественно, эти параметры программы вынесены в раздел констант, так же как и точность вычисления. Предыдущая логика оформлена как функция вычисления площади. Мы не смешиваем за-дачи. Эту часть нам известно, как решать.

Program pr_6m; _ Const eps=1.0E-3; nb=10;ns=100; Var a,b, wn, ws :Real ; i, n :.Integer; Function Eq (x, у : Real) : Boolean; {* Функция рассмотрена выше. *} Function F(x:Real) :Real; Function Sq (n: Integer):Real; Var x,dx,s:Real; 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 ; wn : =Sq (n) ; Repeat ws : =wn; n : =n +n s ; wn : =Sq (n) ; Until Eq(ws, wn); WriteLn (' Результат: количество частей и значение площади ' , n , wn ) ;

ReadLn; End. После запуска получаем, что при п=810 достигается требуемая точность

вычислений, результат — 2.32654955.... Однако в уме мы подсчитали точнее.

Page 99: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

99

Изменим точность на 1.0Е-6. Ваш компьютер задумается на некоторое время, а результат тг=23310 и площадь — 2.33309739... Для самоконтроля Вам предлагается вычислить площадь для следующих функций: f(x)=l/(l+-a), [0,1]; f(x)=l/x, [1,3]; f(x)=8in(x), [0,Pi/2].

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

11()...)1(...432

)1ln(

...!2

)1(...!6!4!2

1)cos(

...)!12(

)1(...!7!5!3

)sin(

...!

...!3!2

1

1432

2642

12753

32

≤<−+−++−+−=+

+−++−+−=

++

−++−+−=

++++++=

+

+

xnxxxxxx

nxxxxx

nxxxxxx

nxxxxe

nn

nn

nn

nx

В каждом из разложений точность представления функции будет, во-обще говоря, тем выше, чем больше взято слагаемых в сумме. Причем зна-чения самих слагаемых с ростом п стремятся к нулю. Для вычисления зна-чений функции с некоторой заданной точностью eps поступают следующим образом. Вычисляют и суммируют слагаемые до тех пор, пока очередное сла-гаемое не станет по абсолютной величине меньше eps или абсолютное значе-ние разности между соседними слагаемыми не станет меньше eps (что луч-ше?). Полученную сумму и принимают за приближенное значение функции. Продемонстрируем этот метод на примере вычисления функции sin(x).

{$N+} Program pr15_7; Const eps=1.0E-3; Function Eq(x,y:Real):Boolean;{*Функция рассмотрена выше.*} Function F(n:Integer;Var x:Real):Real; Var i:Integer; s:Longlnt; Begin s: =1 ; For i:=2 To n Do s:=s*i; x: =x *Sqr (x) ; F:=x/s; End;

{******************************************} Var x, sn, ss: Real; p, n: Integer;

Begin WriteLn (' Введите значение х '); ReadLn(x) ; ss : =0 ; sn : =х ; n : =1 ; р : =1 ; { * р - для реализации чередования знака слагаемого. *} Repeat ss:=sn; { *Предыдущее значение слагаемого.*} n:=n+2;p:= -l*p; sn :=ss+p*F (n,x) ;{*Новое значение слагаемого. *}

Until Eq(ssfsn) Or (n>=12) ; { *Второе условие требуется по простой причине — факториал числа мы умеем вычислять только при этих значениях п . * } WriteLn (' Результат ' ,sn); ReadLn;

End. Известно, что sin(30°)=1/2, sin(45°) = Sqrt(2)/2, 30°=0,5236... радиан,

45°=0,7854... радиан. Проверьте работоспособность программы. При какой точности вычисления нам не будет хватать наших знаний по вычислению факториала числа. Измените программу для вычисления других, приведенных выше функ-

ций. Исследуйте решения.

Page 100: Document2

Часть вторая. Процедуры и функции – элементы структуризации программ

100

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

1. Написать программы вычисления следующих выражений:

512*)183(

1536

32

74

...

...)(......

)...))4039(...2(1(

2),11)...(311)(

211(

223

32

8

23

4

2

2

−++=

−−+=

+−=

−+=

+=

+++=

+++=

+++=++++=

>−−−=

xxxxY

bbY

xxY

xxxY

xxYxSinxSinSinxY

SinxSinxSinxYразnSinxSinSinSinSinSinxSinxY

CosCosCosCosY

nn

Y

n

n

2. Вычислить приближенное значение бесконечной суммы:

.5*4*3

14*3*2

13*2*1

1

...5*3

14*2

13*1

1

...4*3

13*2

12*1

1

+++

+++

+++

Page 101: Document2

Часть третья. Массив – фундаментальная структура данных

101

16. Тема: Двумерные массивы. Работа с элементами. План занятия 1. структура двумерного массива и его описание; 2. экспериментальная работа с программами:

• поиска первой из строк квадратной матрицы, содержащей наибольшее количе-ство четных чисел в строке (см. Книга 2, стр14-15);

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

элементов соответствующей строки; • поиска элементов с определенными свойствами; • заполнения массива по заданным правилам;

3. выполнение самостоятельной работы. Массивы, положение элементов в которых описывается двумя индексами,

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

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

Как происходит отображение логической структуры двумерного массива в физическую? Известны два способа: отображение строками и отображение столбцами. В конкретной системе программирования используется один из них.

Рассмотрим первый, как обычно на примере. Пусть есть двумерный мас-сив А из целых чисел (тип Integer - 2 байта). Количество строк равно 5, коли-чество столбцов - 4. Пусть первый элемент А[1,1] записан в ячейке с номе-ром 1000. Вычислим адрес А[4,3]. Addr(A[4,3])=1000+2*(4*(4-1)+(3-1))=1028. Что мы делали? В каждой строке записано по 4 элемента. В трех строках — 12 элементов, в 4-й строке до 3-го элемента записано 2 элемента. Складываем и умножаем на 2, ибо 2 байта необходимо для хранения одного элемента. В общем случае для массива A[N,M] из элементов, занимающих V байт

памяти, формула имеет вид: Addr(A[i,j])=Addr(A[1,1 ])+V*(M*(i-l)+(j-l)). Двумерный массив на языке Турбо Паскаль определяется по-разному. Примеры:

Const MaxN=...; MaxM=...; {*Максимальные значения количества строк и столбцов двумерного массива.*} Type Vect=Array[l..MaxM] Of integer;{*Одномерный массив из целых чисел.*} Matr=Array[l..MaxN] Of Vect; {*Одномерный массив, элементами которого

являются одномерные массивы из целых чисел.*} или Type Matr=Array[l..MaxN,l..MaxM] Of Integer; {*Двумерный массив из целых чисел.*}

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

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

for i:=1 to m do for j:=1 to n do a[i,j]:=random(10);

Page 102: Document2

Часть третья. Массив – фундаментальная структура данных

102

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

for i:=1 to m do begin for j:=1 to n do write(a[i,j]:5); writeln; end;

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

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

2. Найти первый максимальный элемент массива и его индексы. Procedure Search(v,w:Integer; var X:matr; Var max,simax,sjmax:Integer);

Var i , j :Integer; Begin smax:=X[1,1]; simax:=1; sjmax:=1; For i:=1 To v Do For j:=1 To w Do If X[ i , j ]>smax Then Begin smax:=X[i, j] ; simax:=i; sjmax:=j; End; End; 3. Сформировать одномерный массив, каждый элемент которого равен сумме

отрицательных элементов соответствующей строки заданной целочислен-ной матрицы. Задача требует введения одномерного массива, поэтому приведем описа-ние данных, процедуры подсчета суммы отрицательных элементов в строке. Const MaxN=10; МахМ=8; Type Matr=Array[l..MaxN,1..MaxM] Of Integer; Vect=Array[1..MaxN] Of Integer; Var A:Matr; Sum:Vect; n,m:Integer;

Procedure SumNeg(v,w:Integer; Var X:Matr;Var Ssum: Vect); Var i,j;Integer;

Begin For i:=l To v Do Begin Ssum[i] :=0; For j:=1 To w Do If X[i,j]<0 Then Inc(Ssum[i],X[i,j]) ; End;

End; 4. Определить, есть ли в массиве элемент, равный 0.

Function SNeg (v,w:Integer; Var X:Matr):Boolean; Var i,j:Integer;pp:Boolean; Begin

pp:=False; For i:=1 To v Do For j:-1 To w Do If X[i,j]=0 Then pp:=True; SNeg:=pp; End;

5. Определить, является ли данный квадратный массив симметричным относи-тельно своей главной диагонали. Элементы на главной диагонали характе-

Page 103: Document2

Часть третья. Массив – фундаментальная структура данных

103

ризуется совпадением значением индексов — i=j, на второй i=N-j+l, где N — размерность массива. Function Sim (v,w:Integer; var X:Matr):Boolean; Var i,j :Integer; pp:Boolean; Begin pp:=True; For i:=1 To v-1 Do For j:=i+l To w Do If X[i,j]<>X[j,i] Then pp:=False; Sim:=pp; End; Измените функцию так, чтобы симметричность массива проверялась отно-

сительно второй диагонали. 6. В массиве А размерностью п*т к элементам четных столбцов прибавить

элемент первого столбца соответствующей строки. Procedure Change( v,w:Integer;Var X:Matr); Var i,j: Integer; Begin For i:=l 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.

Задания для самостоятельной работы 1. Дан двумерный массив. Найти сумму и количество элементов в каждом столбце:

• кратных k1 или k2; • попадающих в интервал от A до В; • являющихся простыми числами; • положительных и лежащих выше главной диагонали.

2. Дан двумерный массив. Найти: • сумму элементов в строках с kl no k2;

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

ского элементов данной строки; • номера первой пары неравных элементов в каждой строке.

3. Даны два квадратных массива А и В. Вывести тот из них, у которого след меньше (сум-ма элементов главной диагонали).

4. Дан двумерный массив. Определить: есть ли в данном массиве отрицательный элемент; есть ли два одинаковых элемента; есть ли данное число A среди элементов массива;

5. Дан двумерный массив. Определить, есть ли в данном массиве строка (столбец): • состоящая только из положительных элементов; • состоящая только из положительных или нулевых элементов; • состоящая только из элементов, больших числа А;

Page 104: Document2

Часть третья. Массив – фундаментальная структура данных

104

• состоящая только из элементов, принадлежащих промежутку от А до В. 6. Дан двумерный массив. Выполнить следующие преобразования с ним:

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

• последний отрицательный элемент каждого столбца заменить нулем; • положительные элементы умножить на первый элемент соответствующей строки,

а отрицательные — на последний; • заменить все элементы строки с номером к и столбца с номером I на противопо-

ложные по знаку; • к элементам столбца с номером k1 прибавить элементы столбца k2.

17. Тема: Двумерные массивы. Вставка и удаление.

План занятия

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

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

1. Вставить строку из нулей после строки с номером t. Для решения этой задачи необходимо:

• первые t строк оставить без изменения; • все строки после t-й сдвинуть на одну; • элементам строки t+1 присвоить заданное значение.

В процедуре ввода данных кроме ввода размеров массива следует преду-смотреть ввод номера строки — ReadLn(n,m,t). Ключевая процедура встав-ки строки имеет вид:

Procedure InsertStr(v,w,r:Integer;Var X:Matr); Var i , j :Integer; Begin For i:=v DownTo r+1 Do For j:=l To w Do X[i+1, j] : =X[i , j] ; For j:=l To w Do X[r+l,j]:=0; End; Измените процедуру для вставки строки перед строкой с заданным номе-

ром. 2. Вставить строку из нулей после всех строк, содержащих максимальный

элемент массива. Приведем полный текст решения (для напоминания о нашей схеме решения задач).

{$R+} Program Pr17_2; Const MaxN=8; MaxM=9; Type Matr = array[1..2*MaxN,l..MaxM] Of Integer; { * Резервируем 2MaxN строк на тот случай, если максимальный элемент массива

есть в каждой строке. *} Procedure vvod2(v,w:Integer;Var X:matr); Var i,j:Integer; Begin For i:=1 To v Do

Page 105: Document2

Часть третья. Массив – фундаментальная структура данных

105

For j:=l To w Do Readln (X[i, j]) ; End; Procedure vivod2(v,w:Integer;X:matr); Var i,j:Integer; Begin For i:=l To v Do Begin For j:=l To w Do Write (X[i, j] :4) ; WriteLn; End; End; Procedure InsertStr(v,w,r:Integer;Var X:Matr); Var i,j:Integer; Begin For i:=v DownTo r+1 do For j:=l To w Do X[i+l,j] :=X[i,j] ; For j:=l To w Do X[r+l,j] :=0; End; (*Поиск максимального элемента. *} Function TMax(v,w:Integer;X:Matr):Integer; Var i,j,max:Integer; Begin max:=X[1,1] ; For i:=1 To v Do For j:=l To w Do If X[i,j]>max Then max:=X[i,j]; TMax:=max End; Procedure Solve(Var v:Integer;w:Integer;Var X:Matr); Var i,j,smax:Integer; Begin smax: =TMax (v, w, X) ; { *Находим максимальный элемент. *} i:=l; While i<=v Do {*Почему используем этот тип цикла?*} Begin j:=1; While (j<=w) And (X[i,j]<>smax) Do Inc(j); If j<>w+l Then Begin{*B строке есть максимальный элемент. *} InsertStr(v,w,i,X);{*Вставляем строку.*} V:=v+1;{*Увеличиваем количество строк. *}

i:=i+2; {*Пропускаем вставленную строку. *} End Else I:=I+1 ;{*Обычное изменение индекса. *} End; End; { *В основной программе только вызовы процедур - крупных «кирпи-

чиков». *}

Page 106: Document2

Часть третья. Массив – фундаментальная структура данных

106

Var A:matr; n,m:Integer; Begin Readln(n,m); vvod2(n,m,A); vivod2(n,m,a); Solve(n,m,A); vivod2(n,m,A); End. Измените решение для вставки нулевых строк, перед строками, содержа-

щими максимальный элемент массива. 3. Удалить строку с номером t. Для того, чтобы удалить строку с номером t, необходимо сдвинуть все стро-

ки, начиная с данной, на одну вверх. Последнюю строку можно «обнулить», можно не рассматривать как строку массива.

Procedure DelStr(v,w,r:Integer;Var X:Matr); 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:Matr); Var i,j,smax:Integer; Begin smax: =TMax (v, w, X) ; i:=l; While i<=v Do Begin j:=1; While (j<=w) And (X[i,j]<>smax) Do j:=j+1; If j<>w+l Then Begin DelStr (v,w,i,X) ; v:=v-1 ; End

Else I:=I+1;

End; End; Сравните эту процедуру с процедурой Solve, рассмотренной ранее при

вставке строк, и объясните отличия. 4. Поменять местами элементы столбцов с номерами 11 и 12. С процедурой Swap мы уже встречались. Напомним её. Procedure Swap (Var x, у: Integer); Var z: Integer; Begin z:=x; x:=y; y:=z; End; Написание процедуры, реализующей требования примера, не вызывает

сложностей. Procedure Change(v,stl,st2:Integer; Var X:Matr); Var i:Integer; Begin For i;=1 To v Do Swap(X[i,stl] ,X[i,st2]) ; End;

Page 107: Document2

Часть третья. Массив – фундаментальная структура данных

107

5. Удалить все строки и столбцы, содержащие максимальный элемент мас-сива. Рассмотрим пример.

2 7 9 3 45 6 9 0 12 1 9 5 87 6 9 4 35 6 9 2 1

Максимальный элемент равен 9. Если выбрать логику просмотра по стро-кам, а затем по столбцам, то результат окажется верным. При просмотре же вначале по столбцам, а затем по строкам исключается только третий столбец. Выход из ситуации состоит в запоминании номеров строк и столбцов, со-держащих максимальный элемент массива. Пусть мы запомнили в массивах NumStr и NumStl номера удаляемых строк и столбцов. Для примера они имеют вид: (1, 2, 3, 4, 5) и (3, 3, 3, 3, 3). Пять раз удалять третий столбец яв-но нет смысла, поэтому из массивов необходимо удалить повторяющиеся элементы, т. е. во втором массиве оставить одну 3. Однако этим не ограничи-ваются возникающие сложности. Первая строка удалена, требуется удалять вторую, но она уже в нашей матрице стала первой. Необходимо после каждо-го удаления изменить номера строк (столбцов) в массиве NumStr — умень-шить на единицу те номера, которые больше номера удаленной строки. Ниже по тексту приведена только процедура Solve, остальные части программы совпадают с ранее рассмотренными.

Procedure Solve(Var v,w:Integer;Var X:Matr);

Type vector=Array[1..MaxN] Of Integer; {*Определяем одномерный массив для хранения номеров удаляемых элементов. Константа MaxN берется при усло-вии, что количество строк в матрице всегда больше коли-чества столбцов. *} {*Формирование массивов с номерами удаляемых строк и столбцов. *} Procedure FNum(Var q,r:Integer;Var Y,Z:vector); Var i,j:Integer; Begin g:=0;r;=0;

For i:=1 To v Do For j:= 1 To w Do If 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:vector); Var i,j,l:Integer;

Page 108: Document2

Часть третья. Массив – фундаментальная структура данных

108

Begin i:=l;

While i<t Do Begin j:=j+l; While j<=t Do If X[i]=X[j] Then Begin For l:=j To t-1Do X[l] :=X[1+1]; X[t]:=0; Dec (t) ; End Else Inc (j) ; Inc(i) ; End;

End; {*Уменьшение на единицу значений элементов массива, превышающих за-

данную величину.*} Procedure Change (t,a:Integer;Var X:OMyArray); Var I:.Integer; Begin For i:=2 To t Do If X[i]>a Then Dec(X[i]); End; {*Текст основной процедуры.*} {*Текст основной процедуры.*}

Var i,smax:Integer; NumStr, NumSt1:vector;{*Номера удаляемых строк и

столбцов. *} ykStr,ykStl:Integer;{*Количество элементов в массивах

NumStr и NumStl. *} Begin 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:=l; While i<=ykStr Do Begin DelStr(v,w,NumStr[i],X);{*Удаляем строку. *} Change (ykStr,NumStr[i],NumStr);{*Корректируем массив номеров строк. *} Dec(v); Inc (i) ; End; i:=l; While i<=ykStl Do Begin DelStl(v,w,NumStl[i],X);{*Удаляем столбец. *} Change(ykStl,NumStl[i],NumStl) ; {*Корректируем массив номеров

столбцов.*} Dec(w); Inc(i) ; End; End;

Page 109: Document2

Часть третья. Массив – фундаментальная структура данных

109

О технологии написания. Напоминаем, что процесс разработки процедуры Solve заключается в написании «тела» процедуры с заголовками составляющих частей. Программа компилируется, сохраняется и только затем дописываются составляющие части. В процедуре Solve есть два практически совпадающих фрагмента (циклы по i). Исключите эти повторения, они недостойны Вас.

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

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

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

первый минимальный элемент; • последнюю строку после всех строк, в которых есть заданное число А; • второй столбец перед всеми столбцами, в которых нет отрицательных элементов; • первую строку перед всеми строками, в которых есть 0, и первый столбец после всех

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

2. Удалить • столбцы, в которых есть минимальный элемент; • строку с номером h и столбец с номером I; • все столбцы, в которых нет нулевого элемента; • все строки и столбцы, на пересечении которых стоят отрицательные элементы; • среднюю строку (строки); • все столбцы, в которых первый элемент больше последнего; • столбец, в котором находится первый четный отрицательный элемент; • все столбцы, в которых первый элемент больше заданного числа А.

3. Заменить • минимальный элемент в каждой строке на противоположный по знаку; • все элементы первых трех столбцов на их квадраты; • все симметричные элементы квадратной матрицы на нули.

4. Поменять местами • средние столбцы; • средние строки с первой и последней; • средние столбцы со вторым и предпоследним; • средние строки; • первый максимальный и последний минимальный элементы; • в каждой строке первый элемент и максимальный по модулю; • в каждой строке первый отрицательный и последний положительный; • первую строку и строку, в которой находится первый нулевой элемент; • вторую и предпоследнюю строки; • первую строку с последней строкой, вторую — с предпоследней и так далее; • столбцы так, чтобы в конечном итоге они имели следующий порядок: • последний, предпоследний, ..., второй, первый; • первый, последний, второй, предпоследний, третий,...

5. Найти максимальный элемент массива, встречающийся более одного раза.

Page 110: Document2

Часть третья. Массив – фундаментальная структура данных

110

6. Расстоянием между строками назовем сумму произведений соответствующих элемен-тов строк. Найти две строки с максимальным расстоянием.

7. Дан массив размерностью N*N, N — нечетное число. Вывести элементы массива, при об-ходе его по спирали, начиная с центра.

8. Для заданного целочисленного массива (N*N) найти максимум среди сумм элементов диагоналей, параллельных главной диагонали.

9. Для заданного целочисленного массива (N*N) найти минимум среди сумм элементов диа-гоналей, параллельной побочной диагонали матрицы.

10. Значения элементов массива находятся в интервале от А до В. Две строки матрицы назо-вем похожими, если они отличаются только порядком элементов. Найти пары похожих строк.

11. Элемент массива A[i,j] называют седловой точкой, если он является минимальным в строке с номером i и максимальным в столбце с номером /. Найти седловые точки.

12. Для данного массива найти такие значения k, что k строка совпадает с k столбцом. 13. Среди столбцов заданного целочисленного массива найти столбцы, состоящие только из

нечетных элементов. 14. Элемент A[i,j] назовем локальным минимумом, если он строго меньше всех своих сосе-

дей. Соседями являются элементы A[k,l] с {i-l< k< i+1), (J-l< I <j+l), (k,l)* (i,j). Найти ко-личество локальных минимумов для заданного массива. Найти максимум среди локаль-ных минимумов.

15. Решить задачу поиска числа X в двумерном массиве A[1..N,1..M] (элементы в строках и столбцах упорядочены)за время, пропорциональное 0(N+M).

18. Тема: Множественный тип данных План занятия: 1. описание множественного типа данных, операции с величинами этого типа данных; 2. экспериментальная работа с программами: вывода цифр, не входящих в десятичную

запись числа n, поиска простых чисел с помощью «решета Эратосфена», решения ре-бусов;

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

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

1. Дано натуральное число n. Составить программу вывода цифр, не входящих в десятичную запись числа n (в порядке возрастания). Program Pr18_1; Type Mn=Set Of 0. .9; Var s:Mn; n:LongInt; i : In teger ; Begin

WriteLn('Введите число ' ) ; ReadLn (n); s :=[0. .9] ; While n<>0 Do Begin

S:=s- [n Mod 10] ;{*Исключаем цифру. *} n:=n div10; End;

For i:=0 To 9 Do If i In s Then Write (i:2) ; WriteLn; ReadLn; End.

Измените программу так, чтобы находились общие цифры в записи т чисел. 2. «Решето Эратосфена». Найти простые числа в интервале от 2 до n.

Page 111: Document2

Часть третья. Массив – фундаментальная структура данных

111

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

Program Pr18_2; Const n=1000; {*Интервал чисел, в котором находятся простые числа. *} Var i:Integer; Function Simple (m:integer):Boolean; Var i:Integer; Begin i:=2; While (i<= m Div 2) And (m Mod i<>0) Do Inc(i) ; Simple: = (i>m Div 2); End; Begin For i:=2 To n Do If Simple (i) Then Write ( i , ' ' : 3 ) ; ReadLn; End. Измените программу так, чтобы находилась первая 1000 простых

чисел. Откажемся от этого простого решения. Применим идею «решета Эра-

тосфена», ибо наша цель — изучение множественного типа данных. Суть метода — считаем все числа интервала простыми, а затем «вы-черкиваем» те, которые не удовлетворяют требованию простоты. Как осуществляется вычеркивание? Находим очередное невычеркнутое число, оно простое, и удаляем все числа, кратные ему. После такого «просеивания» в исходном множестве останутся только простые числа. Program Pr18_2m;

Const n=255; Type Mn=Set Of 0..n; Var Sim:Mn; i , j : In teger ;

Begin Sim:=[2.. n ] ; j:=2;

While j<=n Div 2 Do Begin If j In Sim Then Begin {*Поиск очередного простого числа*} i: = j +j:

While i<=n Do Begin Sim:=Sim-[i]; I:=I+j; End; {*Вычеркивание*} End; j:=j+1; End;

For i:=2 To n Do If i In Sim Then Write (i: 4) ; {*Вывод остав-шихся после вычеркивания чисел, они простые. *}

End. Поиск простых чисел из интервала, большего, чем 0.. 255, «упира-

ется» в ограничение множественного типа данных — не более 256 зна-чений базового типа. Уйдем от этого ограничения путем ввода массива, элементами которого являются множества. Но прежде, чем рассмот-рим решение, небольшой фрагмент:

{$R+} Program Pr18_2mm; Var Mn: Set Of 1..255;

Page 112: Document2

Часть третья. Массив – фундаментальная структура данных

112

a:.Word; Begin

Mn:=[l..255]; a:=258; If a In Mn Then WriteLn (' Yes') Else WriteLn ('No') ; End.

После запуска программы видим знакомую до боли ошибку — Er-ror 202: Range check error. Значение переменной а выходит за допус-тимый диапазон значений. Учтем этот факт при написании очередной версии программы.

{$R+} Program Pr18_2mmm; Const m=255; n=1000; Type Mn=Set Of 1..m; Vector=Array[0..(n Div m)] Of Mn; Var Sim:Mn; A:vector; i, j,k:Integer; Begin k:=(n Div m) ;For i:=0 To k Do A[i]:=[1. .m]; j:=2;

While j<=n Div 2 Do Begin If (j Mod m) In A[j Div m] Then Begin i:=j+j; While i<=n Do Begin A[i Div m] :=A[i Div m]-[i Mod m]; Inc(i,j); End; End; Inc(j) ; end; For i:=2 To n Do If (i Mod m) In A[i Div m] Then Write (i , ' ' : 3 ) ; End.

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

1. Дан текст. Найти множества, элементами которых являются встречающиеся в тексте: • цифры от '0' до '9' и знаки арифметических операций; • буквы от 'А ' до 'F' и от 'X' до 'Z'; • знаки препинания и буквы от 'E' до ‘N’.

2. Вывести элементы множества, составленного из произвольных букв от А ... Z, в алфа-витном порядке.

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

4. Дан текст. Подсчитать количество гласных и согласных букв. 5. Дан текст. Вывести в алфавитном порядке все буквы текста, входящие в него:

• не менее (не более) двух раз; • более двух раз; • по одному разу.

Page 113: Document2

Часть третья. Массив – фундаментальная структура данных

113

19. Тема: Текстовые файлы План занятия: 1. файловый тип данных («Язык программирования Pascal». Часть I. Основы про-

граммирования. Книга 2, стр. 37-45; 50-52); 2. операторы работы с файловым типом данных; 3. короткие программы иллюстрации работы с текстовыми файлами; 4. экспериментальная работа с программами:

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

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

Файловый тип данных В рамках данного занятия мы ограничимся работой с текстовыми или по-

следовательными файлами. Текстовые файлы хранят информацию в виде по-следовательности символов. Символы составляют строки произвольной дли-ны. В конце каждой строки записываются два особых символа: #13 #10 (воз-врат каретки, перевод строки), которые отделяют строку от следующей. С текстовыми файлами, однако, мы работали, начиная с первого занятия, прав-да, не говоря об этом. Есть стандартные файлы ввода и вывода. Они связаны с клавиатурой и дисплеем. Имена этих файлов в системе Турбо Паскаль (файловые переменные) Input и Output, их имя в операционной системе Dos — Con (консоль). Таким образом, при записи операторов Read или Write фактически осуществлялось чтение из стандартного фай-ла ввода с клавиатуры или запись в стандартный файл вывода, связанный с дисплеем. Почти все время осуществлялся ввод и вывод цифровой информации. Оказывается, при чтении и записи чисел их преобразование в строковый тип и наоборот выполняется автоматиче-ски при работе с текстовыми файлами, a Input и Output — текстовые файлы.

Пример оформления работы с текстовым файлом: Var f:Text; s:String; Begin Assign(f,'a:\data.txt'); {Файл с именем data.txt находится на диске с логическим именем а } Reset(f);{Файл открывается для чтения.} ReadLn(f,s);{Читаем одну строку из файла.} Close(f);

End. Текстовый файл для оператора ReadLn такой же источник данных,

как и клавиатура. Однако в этом случае данные следуют друг за дру-гом не во времени, как при вводе с клавиатуры, а в измерении файла. Если файл представить в виде некой линейки, то единственное разре-шенное движение от левого конца линейки вправо и, чтобы попасть на 10-й сантиметр, необходимо обязательно пройти через все предыду-щие сантиметры. Как во времени, так и в измерении файла можно пе-ремещаться только в одну сторону. Допускается лишь последователь-ное чтение из файла. Мы не можем в начале прочитать 100-ю строку, а затем 10-ю. Последовательный файл, как лента магнитофона. При этом, конечно, между элементами данных файла обязаны быть разде-лители, ибо данные файла могут быть разной длины и файл обязан заканчиваться каким-то признаком конца файла. А как иначе закон-чить чтение? В Турбо Паскале есть специальная функция Eof («файловая переменная>), которая возвращает значение True, если достигнут конец файла, и False в противном случае.

Короткие примеры. 1. Вводим данные (строки) из файла и выводим их на экран.

Page 114: Document2

Часть третья. Массив – фундаментальная структура данных

114

Program pr19_1; Var f:Text; s:String; Begin Assign (f, 'Input.txt'); Reset(f);

While Not Eof (f) Do Begin ReadLn (f,s); WriteLn (s); End; Close (f);

End. Запуск программы даст ошибку Error 2: File not found. Файла с именем In-

put.Txt нет на диске, его необходимо создать. Открываем новый файл, за-писываем строки А В b С с с D d d d Е е е е е 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 в тот каталог, где находится Ваша про-грамма. Запустите. Результат нормальный.

Измените программу. Вместо ReadLn(f,s) напишите Read(f,s). Результат пла-чевный. Приходится использовать Ctrl+Break, чтобы остановить программу, она «зациклилась». Причина — не может найти признак конца файла. Откройте окно Debug/Watch, введите имя переменной s и выполните программу в поша-говом режиме. Первую строку мы благополучно вводим и выводим, а затем идут пустые строки. Попробуйте дать объяснение.

А можно ли читать посимвольно? Изменим программу. Program pr19_1m; Var f:Text; ch:Char; Begin Assign(f,'Input.Txt'); Reset (f); While Not Eof(f) Do Begin Read(f,ch); Write (ch); End; Close(f);

End. Результат правильный. Но если Вы измените Read(f,ch); Write(ch); на

Read(f,s); Write(s); где s имеет тип String, то результат опять отрицатель-ный — «зацикливание». Осознайте разницу. Найдите еще варианты, при которых Ваша программа будет «зацикливаться». Проведите очередное изменение программы. Её вид: Program pr19_1mm; 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); WriteLn(s), где s имеет строковый тип данных. Запуск

Page 115: Document2

Часть третья. Массив – фундаментальная структура данных

115

программы приводит к ошибке Error 104: File not open for input (файл не от-крыт для ввода). Вставим после Close(Input) оператор Reset(Input). Программа заработала. Однако вместо ввода с клавиатуры строки s мы имеем ввод первой строки из файла 1пput.Txt. Как вернуться к вводу с клавиатуры? Продолжим рассмотрение примеров. Запустите программу. Program pr19_2; Uses Crt; Const MaxN=100; Type Vector=Array[l..MaxN] Of Integer; Var A:Vector; i,j:Integer; Begin ClrScr; Assign(Input,'Input.Txt') ; Reset (Input); i:=0; While Not Eof Do Begin Inc (i) ; Read (A[i] ) ; End; Close (Input); For j:=l To i Do Write (A[j] :2) ;

End. Результат традиционный — ошибка Error 106: Invalid numeric format (не-

правильный числовой формат). Мы вводим в цифровой массив А, система преобразует символ в число, но символ не соответствует числу и как след-ствие — ошибка. Изменим файл Input.Txt на

I 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. Вместо этого числа выводится нечто странное -9981. Если Вы хорошо усвоили материал предыдущих занятий, то суть ошибки очевидна. Если нет, то перед программой вставьте {$R+} и про-анализируйте ошибку. Вернемся к посимвольному вводу из файла.

Program pr19_2m; Uses Crt; Const MaxN=100; Type vector=Array[1..MaxN] Of Integer; Var A:Vector; i,j,k:Integer; ch:Char;

Begin ClrScr; Assign (Input, 'Input. Txt'); Reset (Input); i :=0; While Not Eof Do Begin Read (ch); { Val (ch, j, k); }

{If k=0 Then Begin} Inc(i) ; A[i] :=0rd(ch)-0rd('0') ; {End;} End;

Close (Input); For j:=l To i Do Write (A[j] : 6) ;

End. Результат — странная последовательность: 1 -35 -38 2 -16 2 2 -35 -38

3…, но с определенной закономерностью. Объясните её. Уберите фи-гурные скобки и вновь запустите программу. Результат: 1 2 2 2 3 3 3 3 3... Измените программу так, что бы результат все же был первоначаль-ным 1 2 22 3 3 333 ..... но ввод осуществлялся посимвольно. А можно ли

Page 116: Document2

Часть третья. Массив – фундаментальная структура данных

116

таким же образом вводить вещественные числа? В программе Pr16_2 измените Type Vector=Array[l..MaxN] Of Real; и проведите аналогичные экс-перименты, естественно, изменив при этом и файл Input.Txt. В следующем примере показано переопределение выходного файла.

После запуска программы на экране монитора ничего нет. Необходимо открыть файл Output.Txt. Разместите на экране все три файла Pr16_3, In-put.Txt, Output.Txt (после первого прогона программы на диске в текущем каталоге будет создан выходной файл) так, чтобы они были одновре-менно обозримы. Измените несколько раз файл Input.Txt, при этом запус-кая программу.

Program Pr19_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); вставьте следую-

щие операторы. Assign(Input,'Con'); Assign(Output,'Con'); Reset (Input); Rewrite(Output) ; ReadLn(s); WriteLn(s);

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

Экспериментальный раздел работы 1. Известно, что в файле записаны целые числа, например из диапазона от -100 до 100. Необходимо подсчитать, сколько раз каждое число встречается в файле. Для решения задачи требуется создать файл с целыми числами. Попробу-

ем это сделать с помощью следующей программы. Program Pr19_4; Const MaxN=10000; а=100; b=200; с=15; Var i,j,k:Integer; Begin Randomize;

Assign (Output,'Number.Txt') ; Rewrite(Output); i:=0; k:=0; While i<MaxN Do Begin j:=Integer (Random(b))-a; Inc(i) ;Inc(k) ; Write (j); Write (‘ ’) ; If k=c Then Begin WriteLn; k:=0; End; End; Close (Output);

End. Она работает. Файл Number.Txt создается в текущем каталоге. Он содержит

MaxN целых чисел, разбитых на строки по с чисел в каждой строке. Можно ли записать в файл 100000 чисел? Простое изменение константы MaxN приводит к зацикливанию. Причина традиционная, мы забыли изменить тип данных у переменной i на Longlnt. После каждого числа в файл записывается символ пробела и после с чисел осуществляется переход на новую строку. Обязательно ли сие? Изменим программу, возьмите в фигурные скобки текст {Write(' '); If k=c Then Begin WriteLn;k:=0;End;} программного кода. Программа радует нас новыми сообщениями, они выдаются на стадии выполнения программы и оформлены несколько иначе, чем простые синтаксические ошибки (обратите на этот факт внимание). Первое со-общение: Error. Line too long, truncated (строка очень большая, она ок-

Page 117: Document2

Часть третья. Массив – фундаментальная структура данных

117

ругляется); второе сообщение, но уже не как ошибка, а как предупреж-дение: Warning. Error encountered reading file NUMBER.TXT (ожидается ошибка при чтении файла). Вернемся к исходному тексту программы, а в качестве упражнения попробуйте сформировать файл, у которого и количество чисел в строке формируется случайным образом. Вторая часть задачи реализуется с помощью следующей программы. Program Pr19_5; Const MaxN=100; c=20; Type Vector=Array[-MaxN..MaxN] Of Integer; Var A:Vector; i,j:Integer; Begin FillChar(A,SizeOf(A),0); {Подсчитываем, сколько раз каждое число встречается в файле. Число используется как индекс к элементу массива А. Обратите на этот факт особое внимание. Это «принцип косвенной адреса-ции» — одна из ключевых идей информатики, реализован как на уровне ап-паратуры компьютера, так и в любых системах программирования.} Assign(Input,'Number.Txt'); Reset (Input); While Not Eof Do Begin Read (i) ;Inc (A[i] ) ;End; Close (In-put); Assign(Output,'Count.Txt');{Записываем результат в файл Count.Txt, не раз-бивая на строки.} Rewrite(Output); For i := -MaxN To MaxN Do Write (A[i] ,' ') ; Close (Output); Assign(Input,'Count.Txt');{Открываем файл Count.Txt для чтения.} Reset (Input); Assign(Output,'Con');{Результат выводим на экран. } Rewrite(Output); j:=0; While Not Eof Do Begin Read (i) ; Inc (j) ; Write (i,' '); If j=c Then Begin WriteLn; j:=0;End; {Разбиваем на строки, иначе трудно просматривать.*} End; End. З а м е ч а н и е : На экране должны быть обозримы все файлы: с первой и второй программами; Number.Txt и Count.Txt. В программе, хотя она и работает, содержится явная неточность. Устрани-

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

дировкой ASCII в некотором текстовом файле. Необходимо изменить решение задачи так, чтобы она работала и с вещест-

венными числами. Если создание файла Number.Txt не вызывает затруднений, то логика подсчета частоты повторяемости каждого числа «расползается». Использовать вещественное число в качестве индекса нельзя, косвенная адре-сация не работает. Требуется брать ближайшее целое число, но это уже дру-гая задача. В этом случае вещественные числа из единичного интервала пред-ставлены одним целым числом. А если уменьшить интервал? Попробуйте ис-следовать эту проблему. Создается файл с не очень большим количеством слов. Затем формируется

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

Примеры для пояснения сути задачи:

Page 118: Document2

Часть третья. Массив – фундаментальная структура данных

118

6. (()) — скобки в строке расставлены правильно; 7. {( ] } — скобки в строке расставлены неправильно; 8. ({[}]) — скобки в строке расставлены неправильно. Между скобками допустима запись любых символов. Итак, если встречает-ся одна из закрывающих скобок, то последняя открывающая должна быть такого же типа. Это основной критерий проверки правильности расстановки скобок в тексте программы. Создадим простую работающую программу для проверки работоспособности предполагаемого решения задачи. Program Pr19_sk; Begin

Wri teLn ( ‘ { * 9 ( ( ( ) ) ) [ [ [ [ ] ] ] ] *}'); End. Далее выясним, как отличаются коды (по ASCII) рассматриваемых в задаче

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

Program Pr19_р; Uses Crt; Begin

ClrScr; WriteLn ( Ord ( ‘(‘ ) ) ; WriteLn (Ord ( ‘ )’ ) ) ; {Ответ: 40, 41.} WriteLn (Ord ( ‘[' ) ) ; WriteLn (Ord ( ‘]’ ) ) ; {Ответ: 91, 93.} WriteLn (Ord(‘{‘) ) ; WriteLn (Ord ( ‘}’ ) ) ; {0твет: 123, 125} ReadLn; End.

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

{$R+} Program Pr19_6; Const MaxN=100; Type Vector=Array[ 1..MaxN] Of Char; Var A:Vector; ch:Char; yk: Integer ; ff:Boolean; Begin FillChar(A,SizeOf(A),0); yk:=0; ff:=True; Assign (Input, 'Pr16_SK.PAS' ) ; Reset (Input); While (Not Eof) And ff Do Begin Read(ch) ; If (ch=’(‘ ) Or (ch=’[‘) Or (ch=’{‘) Then Begin Inc(yk); A[yk]:=ch; End

Else If (ch=’)’) Or (ch=’]’) Or (ch=’}’) Then If Ord(ch)- Ord(A[yk])<=2 Then Dec (yk) Else ff:=False; End;

Close (Input); If ff Then WriteLn ( 'Скобки расставлены правильно') Else WriteLn ( 'В тексте программы есть ошибка в расстановке скобок ' ) ;

End. На экране обозримы две программы — последняя и Pr19_sk. Можно при-

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

Page 119: Document2

Часть третья. Массив – фундаментальная структура данных

119

торых исходных данных возникает ошибка Error 201: Range check error. Про-грамма содержит логическую неточность. Устраните её. Измените программу так, чтобы, например, проверялась пра-

вильность записи слова WriteLn в тексте программы. Придумайте еще ряд модификаций программы. 3. Вы записали следующую программу на диск под именем Pr19_7.PAS. В программе подсчитывается количество строк в текстовом файле, каж-дая строка сохраняется как элемент массива для последующего анали-за, строки выводятся на экран. Мы получили один из вариантов програм-мы вывода собственного текста на экран, причём не лучший. Ваша цель — предложить наибольшее количество модификаций задачи, естественно, с их реализациями. Program Pr19_7; Const MaxN=100; Type Vector=Array[1..MaxN] Of String; Var A:Vector;

Cnt, i :Integer; Begin cnt:=0;

Assign (Input, 'Pr3_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. Дан текстовый файл, содержащий целые числа. Найти

• количество чисел в файле; • максимальный элемент в файле, в каждой строке; • сумму чисел в файле, в каждой строке; • разность между максимальным и минимальным элементами в файле, в каждой

строке; • арифметическое среднее чисел в файле, в каждой строке; • номер максимального элемента в файле; • сумму максимальных элементов в файле; • сумму четных чисел в файле.

2. Дан текстовый файл, содержащий строки. Найти • количество строк, начинающихся с заглавных латинских букв; • количество строк, начинающихся и заканчивающихся одинаковыми символами; • самые короткие строки; • симметричные строки (палиндромы).

3. Дан текстовый файл. Вставить в начало каждой строки ее номер и записать преобразо-ванные строки в новый файл.

4. Даны два текстовых файла. Записать в третий только те строки, которые есть и в первом и во втором файлах.

5. Дан текстовый файл. Дописать в его конце следующие данные: количество строк, коли-чество символов в каждой строке, количество чисел в каждой строке.

6. Даны два файла А и В (тип элементов одинаковый). Поменять местами содержимое этих файлов. Использовать процедуруRename не разрешается.

7. Содержимое текстового файла копируется в другой файл, при этом каждая строка цикли-чески сдвигается вправо на n символов. Пример циклического сдвига строки abcdefqwrt

Page 120: Document2

Часть третья. Массив – фундаментальная структура данных

120

на 3 символа, результат — wrtabcdefq. To же самое, но только каждое слово сдвигается на половину своей длины.

8. Дано некоторое конечное множество слов. Исключить их из текстового файла. Напри-мер, из файла с текстом программыисключить все слова Begin и End.

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

10. Дан файл, в котором встречаются даты. Каждая дата — это число, месяц и год (например, 13.05.1949 г.). Найти наименьшую дату.

20. Тема: Комбинированный тип данных (записи)

План занятия 1. описание типа данных; 2. разбор процедур и функций работы с датами и простейшими геометрическими объ-

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

Описание типа данных. При работе с массивами основное ограниче-

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

Запись — это составной тип данных, содержащий набор элементов разных типов. Составляющие запись элементы называются ее полями. В записи каж-дое поле имеет свое собственное имя. Чтобы описать запись, необходимо указать ее имя, имена объектов, составляющих запись и их типы. Общий вид такой: Туре <имя записи> = Record

<поле 1>:<ТИП 1>; <поле 2>:<тип 2>; …………………….. <поле п>:<тип п>

End;

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

Туре Year=1583..3000; Month_number=l..12; Day=l.. 31; Date=Record Dyear:Year; Dmonth:Month_number; Dday:day; End;

Разумеется, что тип date можно было определить и так: Type date=Record

Dyear: 1583..3000; Dmonth: 1..12; Dday:1 ..31;

End; но остановимся на первом способе, ибо при решении задач с датами нам по-требуется работа и с типами данных Year, Month_ number, Day.

С полем записи в программе можно поступать, как с переменной того же типа, что поле. Обращаются к полю по составному имени: <имя запи-си>.<имя поля>.

Page 121: Document2

Часть третья. Массив – фундаментальная структура данных

121

Пример. Есть Var t:Date; Следующие операторы присвоения имеют си-лу:

t.dyear:=1987; t.dmonth:=12; t.dday:=30;

Для не любителей писать много эта запись сокращается с помощью опера-тора With: With <имя записи> Do <оператор>;. Операторы присвоения в этом случае записываются несколько по-другому.

With t Do Begin dyear:=1987 ; dmonth:=12 ; dday:=30;

End;

Оператор Case состоит из выражения (селектора) и списка операторов, каждому из которых предшествует либо одна или несколько констант (назы-ваемых константами варианта), либо слово Else. Селектор должен быть по-рядкового типа, причем порядковые значения верхней и нижней границ этого типа должны находиться в диапазоне от -32768 до 32767. Оператор Case вы-бирает для выполнения тот оператор, перед которым стоит константа, равная значению селектора или диапазону, содержащему значение селектора. Если в диапазоне выбора не существует такой константы выбора и в операторе име-ется часть Else, выполняется оператор, написанный после слова Else. Если часть Else отсутствует, ни один оператор не выполняется.

Примеры: Var с :Char; Begin ReadLn (с) Case с Of ' 0 ' . . ' 9 ' : WriteLn( ' с - цифра . ' ) ; 'а '. . 'z': WriteLn ("с — маленькая латинская буква. ' ) ; 'А '. . ' Z' :WriteLn ( ' с — большая латинская буква. ') ; Else WriteLn ( ' с — некоторый другой символ. ') End; End; V a r i : I n t e g e r ; Begin ReadLn (i); Case i Of 0 , 2 , 4 , 6 , 8 : WriteLn('i — четное число меньше 1 0 . ' ) ; 1 , 3 , 5 , 7 , 9 : WriteLn ('i — нечетное число меньше 1 0 . ' ) ; 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 100<>0) And (x Mod 4=0);

End;

Page 122: Document2

Часть третья. Массив – фундаментальная структура данных

122

Задача определения количества дней в месяце решается с помощью сле-дующей функции:

Function DayM(x:year;y:month_number):day; Begin Case у Of

4 ,6 ,9 ,11 : DayM: =30; {*B апреле, июне, сентябре и ноябре 30 дней. *}

1 , 3 , 5 , 7 ,8 ,10,12: DayM:=31; {*B январе, марте, мае, июле, ав-густе, октябре и декабре 31 день.*) 2: If Leap(x)Then DayM: =29 Else DayM: =28; {*Если год високосный, то в фев-рале 29 дней.*} End; End; Для вывода даты, например в виде 30.12.1987, используется ее преобра-

зование в тип String. Function StringDate(w:Date):String; Var s,q:String; Begin

str(w.dday,s); If w.dday<l0 Then s:='0'+s;{*Преобразуем день даты. *} s:=s+'.';

str (w. dmonth,q) ; { *Преобразуем месяц даты. *} If 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 у:date); Begin у:=х;

If x.Dday<>DayM(x.dyear,x.dmonth) Then y.dday:=x.dday+l {*День даты не является последним днем месяца. *} Else If x.dmonth<>12 Then Begin

у.dmonth:=x.dmonth+l; {*Месяц даты не является последним месяцем в году. *} у.dday:=1; End Else Begin

у.dyear:=x.dyear+1; у.dmonth: =1; у.dday:=1; End; End; Для определения даты, которая наступит в будущем, через определенное

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

Procedure Future(x:Integer;Var у:Date);{*х — количество дней, у — текущая дата. *}

Page 123: Document2

Часть третья. Массив – фундаментальная структура данных

123

Var i:Integer; Function Dyears(z:year):Integer;{*Определяем количе-ство дней в году. *) Begin If Leap(z) Then Dyears:=366 Else Dyears:=365; End; Begin For i:=l To y.dmonth-1 Do x:=x+DayM (y. dyear , i ) ; {*Возвращаемся в начало года, определенного текущей датой.*}

х :=х+у.dday; { *Прибавляем количество дней из даты. Получаем общее количество дней от начала текущего года*}

While x>Dyears(у.dyear) Do Begin{*Счет идет по годам. *} x:=x-Dyears(у.dyear); Inc (у.dyear) ;

End; { *Год будущей даты сформирован. *} у.dmonth :=1; { *Определяем месяц будущей даты. *) While x>DayM(у.dyear,y.dmonth) Do Begin {*Счет идет по месяцам. *) х: =x-DayM(у. dyear,у. dmonth); Inc(у.dmonth);

End; {*Месяц будущей даты получен. *} у.dday:=x; {* Оставшиеся дни являются днями будущей да-ты. *} End; Основная программа для работы с датами может иметь следующий

вид: Program Pr20_l; Var t,r:date; w:Integer; {*Процедуры и функции. *) Begin

WriteLn(' Введите год, месяц, день даты: ') ; ReadLn( t . dyear,t.dmonth,t.dday); Tomorrow(t,r); WriteLn(StringDate(r)) ; WriteLn(' Введите количество дней до будущей даты.'); ReadLn(w);

Future(w,t); WriteLn(StringDate(t));

End. Многочисленные модификации данной программы получаются при выполне-нии ряда заданий для самостоятельной работы. 2. Начальные сведения из компьютерной геометрии. Точка на плоскости в

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

Type TPoint=Record х, у: Real; End; Const Eps:Real=1e-7;{*Точность вычисления.*} ZeroPnt:Tpoint= (x:0; у:0);{*Точка с координатами 0,0. *}

Пример реализации операций сравнений:

Page 124: Document2

Часть третья. Массив – фундаментальная структура данных

124

Function RealEq(a, b: Real): Boolean; {*Строго равно. *} Begin RealEq:=Abs(a-b)<=Eps; End; Функция проверки совпадения двух точек на плоскости имеет вид:

Function EqPoint(А, В:TPoint):Boolean;{*Совпадают ли точки ?*} Begin EqPoint:=RealEq(А.х, В.х) And RealEq (А.у, В.у); End; При вычислении расстояния между двумя точками на плоскости удоб-

нее опять же использовать функцию. Function Dist (А,В:TPoint): Real;{*Расстояние между двумя точками на плоскости. *}

Begin Dist:=Sqrt(Sqr(A.x-B.x) + Sqr(A.y-B.y)); End; Любая точка на плоскости определяет вектор (v) — направленный отрезок,

соединяющий точку (0,0) с точкой (х,у). Вектор v можно задать в полярной системе координат через его длину (модуль) v и угол относительно оси ОХ. Координаты полярной системы координат (v) и прямоугольной декартовой (х, у) связаны соотношениями:

x=v*cos (a), y=v*sin (а), v= 22 yx + , tan (а)=у/х.

Разработаем функцию определения угла α (в радианах) по координатам точ-

ки в декартовой системе координат. Function GetAngle (w:TPoint) :Real; { *w — ненулевая точка на плоскости. *} Var v,angle:Real; Begin

v:=Dist(w,ZeroPnt); {*Расстояние. *} If RealEq(v,0) Then GetAngle:=0 Else Begin

If RealEq(w.x,0) Then If w.y>0 Then angle:=Pi/2 Else angle:=3*Pi/2 {*Выделяем особые случаи. Точка находится на одной из осей. *} Else If RealEq(w.y,0) Then If w.x>0 Then angle:=0 Else angle:=Pi Else Begin

angle:=ArcTan(Abs(w.y/w.x)); {*Определяем четверть плоскости. *} If w.x*w.y>0 Then Begin If w.x<0

Page 125: Document2

Часть третья. Массив – фундаментальная структура данных

125

Then angle:=Pi+angle; End

Else If w.x<0 Then angle:=Pi/2+angle Else angle:=2*Pi-angles; End;

GetAngle:=angle; End; End;

Скалярным произведением двух векторов называется число, равное произведению длин этих векторов на косинус угла между ними. Если хоть один из векторов нулевой, то угол не определен, и скалярное произведение по определению считается равным нулю. Таким образом, (v,w)= v*w*Cos(a), где a - угол между векторами v и w.

Напомним, что косинус угла между векторами 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.у; End; Вычисление скалярного произведения векторов в полярной системе коор-

динат ((v,α) и (w,β)) выполняется по формуле: v*w = v.x*w.x + v.y*w.y = v*Cos(a)*w*Cos(β)+v*Sin(a)*w*Sin(β) =

v*w*Cos(a-β). Из этого соотношения следует, что скалярное произведение

• ненулевых векторов равно нулю тогда и только тогда, когда векторы перпендикулярны;

• больше нуля, если угол между векторами острый, и меньше нуля, если угол тупой.

Прямая линия на плоскости, проходящая через две точки р} и р2, определя-ется следующим линейным уравнением от двух переменных: (p2.x-p1.x)*(y-p1.y)=(p2.y-p1.y)*(x-pl.x). После преобразований получаем: -(p2.y-p1.y) *x +(р2.х-р1.х)*у+(р2.у-р1.у)* р1.х - (р2.х-р1.х) * р1.у=0, или – A*x + В*у + С = 0, после соответствующих обозначений. Если прямые заданы с помощью уравнений А1*х+В1*y+C1=0 и

А2*х+В2*у+С2=0, то точка их пересечения, в случае ее существования (А1*В2—А2*В1 <>0), определяется по формулам:

Х=-(C1 *В2-С2*В1) / (А1*В2 - А2*В1) , у = (А2*С1-А1 *С2) / A1*B2 -A2*B1)

Определим тип данных для описания прямой: Туре ТLine=Record А,В,С: Real; End; процедуру вычисления коэффициентов в уравнении прямой, проходящей

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

Procedure Point2ToLine (А, В: TPoint; Var L: TLine); {*Определение уравнения прямой по координатам двух точек. *}

Begin L.A:=B.y-A.y; L.B:=A.x-B.x; L.C:=- (A.x*L.A + A.y*L.b); End; Function Line2ToPoint(fL,sL:TLine; Var P: TPoint): Boo-lean; {*Определение координат точки пересечения двух линий. Значение функции равно True, если точка есть, и False, ес-ли прямые параллельны. *}

Page 126: Document2

Часть третья. Массив – фундаментальная структура данных

126

Var s t : Real; Begin

st:=fL.A*sL.B-sL.A*fL.B; If Not(RealEq(st,0) ) Then Begin Line2ToPoint:=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 Line2ToPoint:=False; End;

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

1. Написать процедуру (или функцию) определения: • даты вчерашнего дня; • даты, которая была за m дней до указанной даты; • количества дней, прошедших от даты tl до t2; • дня недели, выпадающий на дату tt (для решения задачи необходимо ввести тип

данных «день недели» и определенную дату «связать» с конкретным днем не-дели);

• годов столетия (1900-2000), которые начинаются и заканчиваются в воскресе-нье;

• годов столетия, содержащих максимальное число воскресений. 2. Дана дата Вашего рождения (включая и день недели). Найти те даты, когда Ваш день

рождения «попадает» на тот же день недели. 3. Даны даты рождения членов семьи из пяти человек. Столетними юбилеями семьи яв-

ляются дни, когда сумма возрастов всех членов семьи кратна 100. Найти такие даты. 4. Дано время, описанное следующим образом: Type t ime=Record h:0..23; m,s:0.. 5 9 End;

Описать: • логическую функцию для проверки, предшествует ли время tt времени t2 (в

рамках суток); • процедуру, присваивающую параметру tt время, на 1 секунду большее времени t

(учесть смену суток). 5. Дано следующее описание данных:

Const п=300; Type MyRecord = Record key: Integer; name : String;End; Vector=Array[l..n]Of MyRecord. Считая, что записи в массиве имеют различные ключи, описать: • процедуру, упорядочивающую записи массива по убыванию значений поля key; • логическую функцию поиск(t,k,h), определяющую, есть ли в массиве t (все записи

которой уже упорядочены по возрастанию значений поля key) запись со значени-ем поля key, равным k, и, если есть, присваивающую ее номер параметру h.

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

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

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

• багаж, средний вес одной вещи в котором отличается не более чем на 0,3 кг от об-щего среднего веса одной вещи;

Page 127: Document2

Часть третья. Массив – фундаментальная структура данных

127

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

• имеется ли пассажир, багаж которого состоит из одной вещи весом менее 30 кг. 8. Разработать процедуры перевода координат точки из декартовой в полярную (и об-

ратно) системы координат. Для описания точки в полярной системе координат исполь-зовать тип данных Type TPol=Record r,ang:Real; End. Описание процедур должно иметь следующий вид:

{*Перевод из декартовой системы координат в полярную. *} Procedure TurnDecPol(w:TPoint; Var q:TPol); {*Перевод из полярной системы координат в Декартовую. *} Procedure TurnPolDec(q:TPol; Var w: TPoint);

9. Разработать процедуры сложения, вычитания векторов в декартовой (полярной) системе координат. Пример: {*Сумма двух векторов в полярной системе координат. *} Procedure AddPol(a, b: TPol; Var с: TPol); Begin

с .ang:=(a.ang+b.ang)/2; C.r:=Sqrt (Sqr(a.r*Cos(a.ang)-b.r*Cos (b.ang))+ Sqr (a . r*Sin (a . ang) -b. r*Sin (b. ang))) ; End;

10. Координаты точек отрезка {pltp2) можно задать двумя параметрическими уравнениями от одной независимой переменной t: p.x=p1.x+(p2.x-p1.x)*t u p.y=p1.y+(p2.y-p1.x)*t. При 0≤t≤l точка р(х.у) лежит на отрезке, а при t<0 или t>l — вне отрезка на прямой линии, продолжающей отрезок. Даны координаты двух точек на плоскости, определяющих отрезок, и координаты третьей точки. Определить, принадлежит ли третья точка отрезку?

11. Даны два отрезка. На рисунке точка пересечения отрезков, если она есть, обозначена как р=(х,у). Первый отрезок задан точками p1=(x1,y1) и p2=(х2,у2), а второй — точ-ками p3=(х3,у3) и р4=(х4,у4). Определить координаты точки пересечения отрезков.

12. Дано N точек. Найти пару точек с максимальным расстоянием между ними. 13. Дано множество прямых. Подсчитать количество точек пересечения этих прямых. 14. Дано два множества точек. Найти пересечение и разность этих множеств. 15. Известно, что два отрезка находятся на одной прямой. Определить их взаимное распо-

ложение. 16. Дано множество точек. Выбрать из него две точки, такие, чтобы количество точек, лежа-

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

17. Даны координаты вершин треугольника и точки. Определить взаимное расположение этих геометрических объектов.

18. Даны координаты вершин треугольник и отрезка. Определить их взаимное расположе-ние.

Page 128: Document2

Часть третья. Массив – фундаментальная структура данных

128

19. Даны координаты вершин двух треугольников. Определить их взаимное расположе-ние.

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

1. В задачах все геометрические объекты задаются на плоскости. 2. Исходные данные в задачах вводятся из файла.

21. Тема: Типизированные файлы План занятия 1. файловый тип данных (см. «Язык программирования Pascal». Часть I. Основы

программирования. Книга 2, стр. 37-50); 2. операторы работы с файловым типом данных; 3. короткие программы иллюстрации работы с типизированными файлами; 4. экспериментальная работа с программами:

• подсчета суммы элементов файла (см. стр. 46); • сортировки элементов файла (см. стр. 47-48); • чтение символьного файла двумя способами: файла символов и файла чисел

(см. стр. 48-49); 5. выполнение самостоятельной работы.

Работа с типизированными файлами

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

Процедура Write(f, список переменных); записывает в файл f всю инфор-мацию из списка переменных.

Процедура Read(f, список переменных); читает из файла f компоненты в указанные переменные.

Процедура Seek(f, n); смещает указатель файла f на n-ную позицию. Нуме-рация в файле начинается с 0.

Функция FileSize(f): longint; возвращает количество компонент в файле f. Функция FilePos(f): longint; возвращает порядковый номер текущего ком-

понента файла f. Процедура Truncate(f); отсекает конец файла, начиная с текущей позиции

включительно. Процедура Assign(f, FileName) связывает файловую переменную f с физи-

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

Page 129: Document2

Часть третья. Массив – фундаментальная структура данных

129

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

Функция EOF(f): boolean - возвращает значение TRUE, когда при чтении достигнут конец файла. Это означает, что уже прочитан последний элемент в файле или файл после открытия оказался пуст.

Процедура Rename(f, NewName) - позволяет переименовать физический файл на диске, связанный с файловой переменной f. Переименование возможно после закрытия файла.

Процедура Erase(f) - уничтожает физический файл на диске, который был связан с файловой переменной f. Файл к моменту вызова процедуры Erase дол-жен быть закрыт.

Функция IOResult - возвращает целое число, соответствующее коду по-следней ошибки ввода - вывода. При нормальном завершении операции функ-ция вернет значение 0. Значение функции IOResult необходимо присваивать ка-кой-либо переменной, так как при каждом вызове функция обнуляет свое зна-чение. Функция IOResult работает только при выключенном режиме проверок ошибок ввода - вывода или с ключом компиляции {$I-}.

Задания для самостоятельной работы 1. Имеется типизированный файл с числами. Изменить все его элементы, порядковый номер

которых кратен 3. Новые значения вводятся с клавиатуры. 2. Имеется типизированный файл с числами. Найти:

Произведение первого и последнего чисел файла; Количество чисел файла, не превышающих a; Среднее арифметическое положительных чисел файла; Первое число, большее b. Если такого числа нет, то сообщить об этом; Порядковый номер минимального числа в файле. Если таких чисел несколько, найти иномер первого из них.

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

Получить предложение, составленное из слов, порядковый номер которых четный. Найти самое длинное (короткое) слово

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

Найти число вхождений в файл каждой из букв «а», «о» и «у». 5. Имеется типизированный файл с целыми числами. Удалить из него число, записанное по-

сле первого нуля (принять, что нули в файле имеются). 6. Имеется типизированный файл с целыми числами. Вставить число 100 после первого

числа –100. 7. Сводная ведомость результатов экзаменационной сессии студенческой группы находится

в файле на диске и для каждого студента содержит фамилию, инициалы и оценки по пяти

Page 130: Document2

Часть третья. Массив – фундаментальная структура данных

130

предметам. Количество студентов в группе не превышает 30. Составить программу, с по-мощью которой можно корректировать и дополнять список и получать: • Список студентов; • Список студентов, сдавших экзамены только на «5»; • Список студентов, имеющих тройки; • Список студентов, имеющих двойки. При этом студент, имеющий более чем одну

двойку, исключается из списка. 8. Предприятие имеет местную телефонную станцию на 20 номеров. Телефонный справоч-

ник данного предприятия для каждого номера телефона содержит номер помещения и список служащих, сидящих в данном помещении. Составить программу, которая: • Корректирует базу; • По номеру телефона выдает номер помещения и список в нем людей; • По номеру помещения выдает номер телефона; • По фамилии выдает номер телефона и номер помещения. • Номер телефона – двузначный. В одном помещении может находиться от одного до

четырех служащих. 9. В гостинице имеется 15 номеров, из них 5 одноместных и 10 двуместных.

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

лию жильца и номер проживания. • Если в гостинице проживает два или более жильцов с такой фамилией, программа до-

полнительно запрашивает инициалы.

Page 131: Document2

131

Литература 1. Абрамов В.Г., Трифонов Н.П., Трифонова Г.Н. Введение в язык Паскаль. - М.:

Наука - 1988 - 320с. 2. Васюкова Н.Д., Тюляева В.В. Практикум по основам программирования. Язык

Паскаль.: Учеб. Пособие для учащихся сред. спец. учеб. заведений.- М.:Высш.шк., 1991. – 160 с.: ил.

3. Иванова Г.С. Основы программирования: Учебник для вузов. – 2-е изд., перераб. и доп. – М.: изд-во МГТУ им. Н.Э.Баумана, 2002. – 416 с.

4. Немнюгин С.А. Turbo Pascal. СПб.: Питер, 2002.- 496 с.: ил. 5. Марченко А.И., Марченко Л.А. Программирование в среде Turbo Pascal 7.0. - М.:

Бином Универсал, К.: ЮНИОР, 1997. - 496 с. 6. Окулов С.М. Основы программирования. – М.:ЮНИМЕДИАСТАЙЛ, 2002. – 424

с.: ил. 7. Основы программирования и алгоритмизации: Уч. пос. для техн. - М.: Энерго-

атомиздат. , 1991. - 400 с. 8. Павловская Т.А. Паскаль. Программирование на языке высокого уровня: Учебник

для вузов. – СПб.: Питер, 2003. – 393 с. 9. Прайс Д. Программирование на языке Паскаль: Практическое руководство. - М.:

Мир , 1987. - 232 с. 10. Программирование на языке Паскаль: задачник/под ред. Усковой О.Ф. – СПб.: Пи-

тер, 2002. – 336 с.: ил. 11. Рапаков Г.Г., Ржеуцкая С.Ю. Turbo Pascal для студентов и школьников. СПб.:

БХВ-Петербург, 2005. – 352 с.: ил. 12. Сафронов И.К. Задачник-практикум по информатике. – СПб.: БХВ-Петербург,

2002. – 432 с.: ил. 13. Семашко Г.Л., Салтыков А.И. Программирование на языке Паскаль. М.: Наука -

1993 – 208 с. 14. Эрбс Х.-Э., Штольц О. Введение в программирование на языке Паскаль. - М.:

Мир, 1989. - 299с. 15. Turbo Pascal 7.0. - Киев: BHV, 1996. - 448 с.