Контрольная работа №1: оператор if Задание. Разработать программу, определяющую попадание точки с координатами (X,Y) в заштрихованную область рисунка. Результат поместить в булеву переменную. Использовать рис. 4 из [2, с. 12]. Номер варианта рисунка для контрольной работы равен ((номер студента по списку в журнале преподавателя) + 14 + (номер подхода к контрольной работе)) mod 30 +1. Требования к организации системы программирования и программе: - программу создавать в новом пустом каталоге с именем Kontr<# работы>, - автосохранение файлов включить, - контроль ошибок времени выполнении включить, - компиляция на диск, - структурированность текста и программы, - осмысленные имена , - дружественный интерфейс. Эти требования действуют во всех контрольных работах! Работа 3. Преобразование строки в число и обратно [8,9] Задание. Разработать программу, выполняющую: - ввод строки, изображающей число в позиционной системе счисления со знаком или без знака, - преобразование этой строки в десятичное число целого или вещественного типа, - вывод этого числа, - преобразование десятичного числа в строковое представление, - вывод этой строки. Ограничения Для ввода и вывода строк можно использовать тип строка или вводить и выводить строки по символам. Для преобразования чисел из одной системы счисления в другую можно использовать любой алгоритм (кроме табличного), но проще применить методы «цифра за цифрой». Использовать библиотечные функции преобразования запрещено. Отчет. Индивидуальное задание. Пример преобразования строки в число и наоборот. Описание двух алгоритмов: 1)преобразования строки в число и 2) наоборот. Граф-схемы этих двух алгоритмов. Тексты программ со ссылками на номера элементов граф-схем. Результаты прогона тестов (6 штук, включая крайние случаи). Варианты Вариант задания определяется по ассоциативной табл.1, где полужирным стилем (содержимое клетки) приведен номер студента по списку, а заголовки столбцов и строк определяют 1) тип входного числа, изображенного строкой – целый (напр., 17) или вещественный (напр., 0,F), 2) систему счисления (двоичная и т.д.), 3) наличие символа знака у входной строки. Например, если номер студента по списку 8 (пятая клетка в нижней строке табл. 1), то его индивидуальное задание: преобразовать строку, изображающую целое шестнадцатеричное число со знаком в вещественное число и обратно, напр.: ‘-A1’ -17 ‘-A1’. А если номер студент по списку = 9 (шестая клетка в предпоследней строке), то он должен обработать строку, изображающую вещественное двоичное число без знака, модуль которого меньше единицы, напр.: ‘0,011’ 0.375 ‘0,011’. Таблица 1 Система счисления Без знака Со знаком 2 Варианты индивидуальных заданий Тип данных Целый Вещественный 8 10 16 2 8 10 16 1 12 7 2 11 6 15 13 3 8 9 2 5 10 16 14 Контрольная работа №2: строка <--> число Задание. Разработать программу преобразования десятичного числа в строку, а затем преобразования этой строки в число. Номер варианта определяется по табл. 2.. Таблица 2 Система счисления Без знака Со знаком 2 Варианты заданий контрольной работы Тип данных Целый Вещественный 8 10 16 2 8 10 1 12 7 2 15 13 3 8 9 2 5 10 Контрольная работа №3: массивы Элементы лексического анализа [8,6] Контрольная работа №4: текстовые файлы Рекурсии: определитель матрицы [8,6] Графическое решение системы уравнений [8,6] 4. РАЗРАБОТКА МНОГОМОДУЛЬНОЙ ПРОГРАММЫ Разработать программу, вычисляющую обратную матрицу по исходной с максимальной размерностью 10. Программа должна иметь следующую структуру. 1. Ввод требуемой размерности. 16 14 16 11 6 2. Выбор пользователем варианта заполнения исходной матрицы: 1) ввод матрицы с клавиатуры с автоматической нумерацией столбцов и строк; 2) заполнение матрицы с помощью генератора случайных чисел по строкам сверху вниз, по элементам слева направо; 3) заполнение матрицы натуральным рядом чисел по строкам сверху вниз, по элементам слева направо; 3. Вывод введенной или сгенерированной матрицы с нумерацией строк и столбцов. 4. Получение обратной матрицы по алгоритму [9. С. 121]. 5. Вывод обратной матрицы с нумерацией строк и столбцов. 6. Получение произведений прямой и обратной, обратной и прямой матриц. 7. Вывод произведений матриц с нумерацией строк и столбцов. Требования по организации программы. Действия 4 и 6 должны быть организованы как внутренние процедуры, действия 2 и 3 - как внешние. Генератор случайных чисел требуется организовать по алгоритму [17. С. 25] в виде внешней функции, используемой в процедуре 2. К начальному значению Х необходимо прибавить дробную часть текущего времени (функция TIME), чтобы псевдослучайные числа были более "случайными". Обмен данными между процедурами и главной программой должен быть осуществлен только через заголовки процедур и функций. Максимальный размер матрицы должен задаваться в разделе констант. - 60 - Этап 1. Цель и средства. Цель - учебная, средства - языки ГСА, Паскаль, операционная среда - ОС РВ СМ ЭВМ. Этапы 2 и 3. В соответствии с требованиями по организации программа должна иметь структуру, приведенную на рис.4.1. -----------¬ ¦ПСЕВДОСЛ ¦ - Внешняя функция получения псевдослучайных чисел LT---------^ -+---------¬ ¦ЗАПОЛНЕНИЕ¦ - Заполнение матрицы одним из трех способов по LT---------- выбору пользователя (ввод с клавиатуры, запол^ нение случайными числами или натуральным рядом) ¦ --------¬ ¦ ¦ ВЫВОД ¦ - Вывод матрицы с нумерацией строк и столбцов ¦ LT------¦ ^ -+--+--------¬ ¦ ОБРАТ_МАТР ¦- Главная программа вычисления обратной матрицы ¦-----------¬¦ ¦¦ОБРАТНАЯ ¦¦- Получение обратной матрицы ¦L-----------¦ ¦-----------¬¦ ¦¦УМНОЖ ¦¦- Вычисление произведений двух матриц ¦L-----------¦ ¦ ¦ L------------Рис.4.1. Структура многомодульной программы Следуя идеологии модульного программирования, процедуру ЗАПОЛНЕНИЕ разобъем на три подпрограммы (рис. 4.2). Этап 4. Разработка структур данных. 1. Так как для получения обратной матрицы задан алгоритм Гаусса, в котором требуется переставлять строки, примем, что матрица есть массив строк: type СТРОКА = array [1..10] of real; - 61 - --------------¬ ¦ЗАПОЛНЕНИЕ ¦ - Процедура ЗАПОЛНЕНИЕ ¦------------¬¦ ¦¦ВВОДМАТРИЦЫ¦¦ - Ввод матрицы с клавиатуры с автоматической ¦L------------¦ нумерацией строк и столбцов ¦------------¬¦ ¦¦НАРУР_РЯД ¦¦ - Заполнение матрицы натуральным рядом ¦L------------¦ ¦------------¬¦ ¦¦SLUCH_РЯД ¦¦ - Заполнение матрицы псевдослучайными числами ¦L------------¦ с помощью функции ПСЕВДОСЛ ¦ ¦ L-------------Рис. 4.2. Структура процедуры ЗАПОЛНЕНИЕ ТИПМАТРИЦА = array [1..10] of СТРОКА; var МАТРИЦА: ТИПМАТРИЦА; 2. Так как по заданию требуется вводить матрицу, получать обратную и находить произведение прямой на обратную и наоборот, нам потребуется три матрицы: var МАТРИЦА, (* введенная матрица *) ОБРАТМАТР, (* обратная матрица *) ПРОИЗВ: ТИПМАТРИЦА; (* произведение двух матриц *) 3. Так как обратная матрица может не существовать, определим переменную СВОЙСТВО: type ТИПСВОЙСТВО = (ВЫРОЖДЕН,НЕВЫРОЖДЕН); var СВОЙСТВО: ТИПСВОЙСТВО; 4. Так как размер матрицы произволен в пределах 1..1О, введем переменную var РАЗМЕР: integer; , которую передадим в те процедуры, которые обрабатывают матрицу. - 62 - Этап 5. Задача разбивается на ряд элементарных подзадач, поэтому алгоритм в виде ГСА изображать не будем. Этап 6. С учетом этих рассуждений организуем главную программу как последовательность обращений к процедурам в порядке, определенном задачей (рис. 4.1). Дополнительно введем сообщения, идентифицирующие смысл вводимой матрицы. Параметры процедур определим в соответствии с назначением процедур. Program ОБРАТ_МАТР (input,output); const MaxРАЗМЕР = 10; type ТИПСТРОКА = array [1..MaxРАЗМЕР] of real; ТИПМАТРИЦА = array [1..MaxРАЗМЕР] of ТИПСТРОКА; ТИПСВОЙСТВО = (ВЫРОЖДЕН,НЕВЫРОЖДЕН); var РАЗМЕР : integer; МАТРИЦА,ОБРАТМАТР,ПРОИЗВЕД : ТИПМАТРИЦА: СВОЙСТВО: ТИПСВОЙСТВО; procedure ЗАПОЛНЕНИЕ (РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); external; procedure ВЫВОД (РАЗМЕР: integer; МАТРИЦА: ТИПМАТРИЦА); external; procedure УМНОЖ (РАЗМЕР: integer; A, B: ТИПМАТРИЦА; var AB: ТИПМАТРИЦА); begin (* Умножаем A на B, результат в AB *) end; (* УМНОЖ *) procedure ОБРАТНАЯ (РАЗМЕР: integer; МАТРВХ: ТИПМАТРИЦА; var X: ТИПМАТРИЦА; var СВОЙСТВО: ТИПСВОЙСТВО); begin (* Получение обратной матрицы X по входной МАТРВХ *) end; (* ОБРАТНАЯ *) begin (* Main *) writeln (' Преобразование матрицы в обратную '); - 63 - writeln (' Размер матрицы? (от 1 до 10): '); readln(РАЗМЕР); if РАЗМЕР < 2 then РАЗМЕР := 2; if РАЗМЕР > 10 then РАЗМЕР := 10; ЗАПОЛНЕНИЕ(РАЗМЕР, МАТРИЦА); writeln(' Исходная матрица: '); ВЫВОД(РАЗМЕР, МАТРИЦА); write(' Для продолжения нажмите [Enter] '); readln; ОБРАТНАЯ(РАЗМЕР, МАТРИЦА, ОБРАТМАТР, СВОЙСТВО); if СВОЙСТВО = НЕВЫРОЖДЕН then begin writeln(' Обратная матрица: '); ВЫВОД(РАЗМЕР, ОБРАТМАТР); write(' Для продолжения нажмите [Enter] '); readln; writeln(' Произведение прямой на обратную: '); УМНОЖ(РАЗМЕР, МАТРИЦА, ОБРАТМАТР, ПРОИЗВЕД); ВЫВОД(РАЗМЕР, ПРОИЗВЕД); write(' Для продолжения нажмите [Enter] '); readln; writeln(' Произведение обратной на прямую: '); УМНОЖ(РАЗМЕР, ОБРАТМАТР, МАТРИЦА, ПРОИЗВЕД); ВЫВОД(РАЗМЕР, ПРОИЗВЕД); end else writeln(' Матрица вырожденная '); end. В данном тексте внутренние процедуры записаны в виде пустых процедур, имеющих только заголовок. Заметим, однако, что этот текст можно компилировать (в среде ОС РВ), чтобы проверить синтаксис главной программы. После определения входных и выходных параметров будем последовательно разрабатывать тексты процедур, т.е. спустимся вниз на шаг и станем детализировать процедуры (принцип проектирования сверху вниз). 4.1. Процедура УМНОЖ Каждый элемент произведения матриц равен сумме произведений элементов соответствующей строки первого сомножителя на элементы столбца второго сомножителя (рис. 4.3). - 64 - Размер AB[i,j] = A[i,k] * B[k,j] k j j---> -------------¬ --------T-T--¬ --------------¬ ¦ k ---> ¦ ¦ ¦ ¦k ¦ ¦ ¦ +--------T-T-+ ¦ ¦ ¦¦ ¦ i¦ --¬ ¦ i +--------+-+-+ ¦ +-+¦ ¦ ¦¦ L-- ¦ ¦ A[i,k] ¦ ¦ B[k,j]+-+V ¦ V¦ AB[i,j] ¦ ¦ ¦ L-------+-+--- ¦ ¦ L------------L-------------A B AB = A * B Рис.4.3. Нахождение элемента произведения матриц Получим: procedure УМНОЖ (РАЗМЕР: integer; A, B: ТИПМАТРИЦА; var AB: ТИПМАТРИЦА); var i, (* номер строки матрицы AB *) j, (* номер элемента строки матрицы AB *) k: (* номер перемножаемых элементов в строке *) (* i матрицы A и столбце j матрицы B] *) integer; begin for i := 1 to РАЗМЕР do for j := 1 to РАЗМЕР do begin AB[i,j] := 0; for k := 1 to РАЗМЕР do AB[i,j] := AB[i,j] + A[i,k] * B[k,j] end; end; (* УМНОЖ *) 4.2. Процедура ОБРАТНАЯ По заданию предложено использовать алгоритм Гаусса. Возьмем этот алгоритм [9, с.121] и запишем его на языке Паскаль: но - 65 - сначала определим переменные (они следуют из текста алгоритма), обозначив их теми же именами, что и в алгоритме. procedure ОБРАТНАЯ (РАЗМЕР: integer; (* n *) МАТРВХ: ТИПМАТРИЦА; var X: ТИПМАТРИЦА; (* обратная *) var СВОЙСТВО: ТИПСВОЙСТВО); var A: ТИПМАТРИЦА; (* Исходная матрица, которая будет *) (* Преобразована в обратную *) j, (* Номер обрабатываемого столбца A *) i, (* Номер элемента в строке *) k, (* Номера элементов *) StrMax: (* Номер строки с наибольшим по абсолютной *) (* величине элементом в столбце j ниже строки j-1 *) integer; M: real; (* Наибольший по абсолютной величине элемент *) СТРОКА: ТИПСТРОКА; (* Рабочая строка для перестановки строк *) begin (* а теперь читаем текст и пишем программу *) (* Во-первых, положите матрицу X равной матрице I *) for i := 1 to РАЗМЕР do for j := 1 to РАЗМЕР do if i = j then X[i,j] := 1 else X[i,j] := 0; (* в процессе вычислений матрица A будет в конце концов преобразована в I, матрица X, которая изначально была единичной, станет обратной к матрице A *) A: = МАТРВХ; СВОЙСТВО := НЕВЫРОЖДЕН; (* Исходное значение *) (* Для каждого столбца A, начиная с столбца 1 слева и кончая столбцом n справа, выполним следующее: обозначим столбец, который будет обрабатываться на каждом этапе, символом j. *) j := 1; while (СВОЙСТВО=НЕВЫРОЖДЕН) and (j<=РАЗМЕР) do begin (* Пусть M = Max ¦Aij¦ есть наибольший по абсолютной j<=i<=n величине элемент в столбце j ниже строки j-1. *) - 66 - M := Abs(A[j,j]); StrMax := j (* Номер строки, где лежит M *) for i := j to РАЗМЕР do if Abs(A[i,j]) > M then begin M := Abs(A[i,j]); StrMax := i; end; (* Если M равно нулю, то A - вырожденная матрица, и продолжать обращение не имеет смысла. *) if M < 1E-37 then СВОЙСТВО := ВЫРОЖДЕН (* В противном случае поменяйте местами в обеих матрицах A и X строку j и строку, в которой находится M. *) else begin СТРОКА:=A[StrMax]; A[StrMax]:=A[j]; A[j]:=СТРОКА; СТРОКА:=X[StrMax]; X[StrMax]:=X[j]; X[j]:=СТРОКА; (* И наконец, разделите каждый элемент в строке j матриц A и X на новое значение Ajj. *) M := A[j,j]; for i := 1 to РАЗМЕР do begin A[j,i] := A[j,i]/M; X[j,i] := X[j,i]/M; end; (* Теперь для всех строк i, i<>j выполните все вычитания: *) Aik = Aik - Aij*Ajk j <= k <= n, Xik = Xik - Aij*Xjk 1 <= k <= n. *) for i := 1 to РАЗМЕР do if i <> j then begin M := A[i,j]; for k := j to РАЗМЕР do A[i,k] := A[i,k] - M*A[j,k]; for k := 1 to РАЗМЕР do X[i,k] := X[i,k] - M*X[j,k]; end; end; (* else *) j := j + 1; (* для каждого столбца A *) end; (* while *) end; (* ОБРАТНАЯ *) - 67 - 4.3. Процедура ВЫВОД По заданию требуется, чтобы процедура выводила матрицу с нумерацией строк и столбцов. Поэтому сначала выведем строку нумерации элементов в строках, затем будем выводить матрицу построчно. А перед выводом элементов выведем номер строки. const MaxРАЗМЕР = 10; type ТИПСТРОКА = array [1..MaxРАЗМЕР] of real; ТИПМАТРИЦА = array [1..MaxРАЗМЕР] of ТИПСТРОКА; procedure ВЫВОД (РАЗМЕР: integer; МАТРИЦА: ТИПМАТРИЦА); external; procedure ВЫВОД; var i, (* номер строки *) j: integer; (* номер элемента в строке *) begin for j := 0 to РАЗМЕР do write (j:7); writeln; for i := 1 to РАЗМЕР do begin write(i:7); (* номер строки *) for j := 1 to РАЗМЕР do write (МАТРИЦА[i,j]:7:3); writeln end end; (* ВЫВОД *) 4.4. Процедура ЗАПОЛНЕНИЕ Так как по заданию вариант заполнения исходной матрицы из трех возможных выбирает пользователь, в теле процедуры ЗАПОЛНЕНИЕ выведем меню возможностей и затем оператором выбора обратимся к одной из трех процедур (рис. 4.2). Не забудем также, что случайные числа генерируются внешней функцией и ей нужны исходные данные, поэтому введем - 68 - type НОМЕРВХОДА = (ПЕРВ, НЕПЕРВ); При первом входе устанавливается исходное состояние и генерируется первое случайное число, при последующих вызовах за исходное значение X, Y принимается предыдущее случайное число. function ПСЕВДОСЛ(var ВХОД: НОМЕРВХОДА; var X,Y: (* старое и новое значения *) real): real; Теперь текст процедуры ЗАПОЛНЕНИЕ: const MaxРАЗМЕР = 10; type НОМЕРВХОДА = (ПЕРВ, НЕПЕРВ); ТИПСТРОКА = array [1..MaxРАЗМЕР] of real; ТИПМАТРИЦА = array [1..MaxРАЗМЕР] of ТИПСТРОКА; function ПСЕВДОСЛ(var ВХОД: НОМЕРВХОДА; var X, Y: real): real; external; procedure ЗАПОЛНЕНИЕ (РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); external; procedure ЗАПОЛНЕНИЕ; var СПОСОБ: integer; (* Номер варианта заполнения *) procedure ВВОДМАТРИЦЫ(РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); begin (* Ввод с клавиатуры *) end; (* ВВОДМАТРИЦЫ *) procedure НАТУР_РЯД(РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); begin (* Заполнение натуральным рядом *) end; (* НАТУР_РЯД *) procedure SLUCH_РЯД(РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); begin (* Заполнение случайным рядом чисел *) end; (* SLUCH_РЯД *) - 69 - begin (* ЗАПОЛНЕНИЕ *) writeln(' Выберите способ заполнения:':30); writeln('С клавиатуры: 1'); writeln('Случайными числами: 2'); writeln('Натуральным рядом: 3'); write(' Введите номер пункта меню: '); readln(СПОСОБ); if (СПОСОБ < 1) or (СПОСОБ > 3) then СПОСОБ := 2; (* Защита от ошибок пользователя *) case СПОСОБ of 1: ВВОД_МАТРИЦЫ(РАЗМЕР,МАТРИЦА); 2: SLUCH_РЯД(РАЗМЕР,МАТРИЦА); 3: НАТУР_РЯД(РАЗМЕР,МАТРИЦА); end; end; (* ЗАПОЛНЕНИЕ *) Теперь спустимся еще на один шаг вниз и запрограммируем вызываемые процедуры. Процедура ВВОД_МАТРИЦЫ По заданию при вводе матрицы с клавиатуры требуется автоматически нумеровать строки и столбцы, поэтому эту процедуру организуем по аналогии с процедурой ВЫВОД: procedure ВВОДМАТРИЦЫ(РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); var i, j: integer; (* Номер строки, номер элемента в строке *) begin writeln(' Введите матрицу: ',РАЗМЕР:1,'*',РАЗМЕР:1); for j := 0 to РАЗМЕР do write (j:7); writeln; (* Нумерация элементов *) for i := 1 to РАЗМЕР do begin write(i:7,' '); (* Номер строки *) for j := 1 to РАЗМЕР do read(МАТРИЦА[i,j]); readln end end; (* ВВОДМАТРИЦЫ *) - 70 - Процедура НАТУР_РЯД Заполняем матрицу по строкам сверху вниз, по элементам слева направо, начиная с единицы (переменная n), с шагом 1: . procedure НАТУР_РЯД(РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); var i, j, n : integer; begin n := 1; for i := 1 to РАЗМЕР do for j := 1 to РАЗМЕР do begin МАТРИЦА[i,j] := n; n := n+ 1 end; end; (* НАТУР_РЯД *) Процедура SLUCH_РЯД Эта процедура заполняет матрицу в таком же порядке, как и предыдущая, но числа берутся не из n, а генерируются функцией ПСЕВДОСЛ, следовательно, мы должны обеспечить интерфейс этой функции, прежде чем ее вызвать: НОМВХОДА := ПЕРВ . procedure SLUCH_РЯД(РАЗМЕР: integer; var МАТРИЦА: ТИПМАТРИЦА); var i, j: integer; x, y: real; НОМВХОДА: НОМЕРВХОДА; begin НОМВХОДА := ПЕРВ; for i := 1 to РАЗМЕР do for j := 1 to РАЗМЕР do МАТРИЦА[i,j] := ПСЕВДОСЛ(НОМВХОДА, x, y); end; (* SLUCH_РЯД *) - 71 - А теперь спустимся еще на один шаг вниз и уточним текст функции ПСЕВДОСЛ, алгоритм которой рекомендовано взять из [17]. Функция ПСЕВДОСЛ В качестве начального значения выбираем Pi и добавляем дробную часть текущего времени. type НОМЕРВХОДА = (ПЕРВ, НЕПЕРВ); function ПСЕВДОСЛ(var ВХОД: НОМЕРВХОДА; var X, Y: real): real; external; function ПСЕВДОСЛ; var z : real; begin if ВХОД = ПЕРВ then begin X := 3.141593 + TIME - Trunc(TIME); if X > 4.0 then X := X - 1.0; Y := 0.2 * X; ВХОД := НЕПЕРВ; end; z := X + Y; Y := X; if z > 4.0 then z := z - 4.0; X := z; ПСЕВДОСЛ := 0.25 * z; end; (* ПСЕВДОСЛ *)