Разветвляющиеся вычислительные процессы




 

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

Оператор if обеспечивает передачу управления на одну из двух ветвей вычислений, а оператор case — на одну из произвольного числа ветвей. Рассмотрим сначала задачи с применением оператора if.

 

Условный оператор if

 

Формат оператора:

 

if выражение then оператор_1 [else оператор_2:]

 

Сначала вычисляется выражение, которое должно иметь логический тип. Как правило, в выражении используются знаки операций отношения (меньше <, больше >, равно =, не равно <>, меньше или равно <=, больше или равно >=)2. Если требуется проверить несколько условий, их объединяют знаками логических операций and (И), or (ИЛИ), хог (исключающее ИЛИ) и not (отрицание)3. Примеры логических выражений:

 

а < 2

(х <> 0) and (у <> 0)

 

Если выражение имеет значение true, выполняется первый оператор, иначе — второй (рис. 2.1). Ветвь else может отсутствовать. После выполнения операторов из соответствующей ветви управление передается оператору, следующему за условным оператором.

 

Рис. 1. Структурная схема условного оператора

 

Когда по какой-либо ветви требуется выполнить не один, а несколько операторов, применяют блок (составной оператор). Блок обрамляется ключевыми словами begin и end:

if а=0 then begin х:=0; у:=0 end

else begin х:=1; у:=2 end;

 

Внутри условного оператора можно записать еще один условный оператор, например:

if а >= b then if а = b then с:= 0 else с:= 1 else с:= 2;

Ключевое слово else всегда считается относящимся к ближайшему слову if, т.е.

 

if а >= b

then if а = b

then с:= 0

else с:= 1

else с:= 2;

 

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

 

 

Оператор case

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

 

Рис. 2. Структурная схема оператора выбора

 

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

Формат оператора:

case выражение of

константы_1: оператор_1; константы_2: оператор_2;

...

константы_n: оператор_n;

[else: оператор ]

end;

 

Вычисление значения функции.

 

Пример 1. Написать программу, которая по введенному значению аргумента вычисляет значение функции, заданной в виде графика (рис.) на интервале [-3; 3].

Рис. Функция, заданная в виде графика

 

Начинать решение даже простейшей задачи необходимо с четкого описания ее исходных данных и результатов. В данном случае это очевидно: исходными данными является вещественное значение аргумента х, который определен на интервале [-3; 3], а результатом — вещественное значение функции у. Поэтому для представления этих величин в программе следует выбрать тип real.

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

Сначала запишем функцию в виде формул:

 

Далее приведено описание алгоритма в словесной форме.

1. Ввести значение аргумента х.

2. Проверить, принадлежит ли оно области определения функции.

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

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

5. Вывести значение у.

 

Опишем четвертый пункт алгоритма более подробно:

· Если аргумент х принадлежит интервалу [-3; -2), то .

· Если аргумент х принадлежит интервалу [-2; 0), то .

· Если аргумент х принадлежит интервалу [0; 1), то

· Если аргумент х принадлежит интервалу [1; 3], то .

 

По такому алгоритму можно практически «один в один» написать программу:

 

 

program calc_fun;

var x, y: real;

begin

writeln ('Введите значение аргумента:');

readln(x);

if (x<-3) or (x >3) then begin

writeln ('Значение должно принадлежать [-3;3]');

exit

end;

if x < -2 then y:= -2 * x - 5;

if (x>=-2) and (x<0) then y:=-sqrt(1-sqr(x + 1)) - 1;

if (x>=0) and (x<1) then y:=x-1;

if x>=1 then y:= sqrt(1-sqr(x-2));

writeln ('Для x= ', x:3:2, ' значение функции y=', y:3:2)

end.

 

 

Обратите внимание на запись условий, содержащих два сравнения. Начинающие часто записывают такие условия, просто воспроизводя математическую формулу, то есть как а < х < Ь. Ошибка состоит в том, что операции отношения (< >, ==, <, >,! =) являются бинарными, то есть должны иметь два операнда. И если мы хотим проверить два условия (а < х и х < Ь), то и операций должно быть две.

Поскольку необходимо, чтобы эти условия выполнялись одновременно, они объединены с помощью операции логического И (and), то есть выражение принимает вид (a<x) and (x<b). Заключать каждое условие в круглые скобки необходимо потому, что логические операции имеют более высокий приоритет, чем операции отношения1. При отсутствии скобок сначала будет предпринята попытка выполнить операцию х and х 2, что вряд ли соответствует нашему замыслу.

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

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

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

Структурная схема вычисления значения функции приведена на рис.

 

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

 

if x < -2 then y:= -2 * x - 5

else if x < 0 then y:= -sqrt(1 - sqr(x + 1)) - 1

else if x < 1 then y:= x - 1

else y:= sqrt(1 - sqr(x - 2));

 

Этот вариант вычисления значения функции иллюстрирует рис.

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

 

 

Рис. Второй вариант вычисления значения функции

 

Пример 2. Написать программу, реализующую калькулятор на четыре арифметических действия (+, −, /, *)

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

 

program calculator;

var

a, b, resiltat: real;

operac: char;

begin

writeln('Введите первый операнд: ');

readln(a);

writeln('Введите операцию: ');

readln(operac);

writeln('Введите второй операнд: ');

readln(b);

case operac of

'+': resiltat:=a+b;

'-': resiltat:=a-b;

'/': resiltat:=a/b;

'*': resiltat:=a*b;

else begin

writeln(' Недопустимая операция ');

exit end;

end;

writeln(' resiltat=', resiltat:10:2);

end.

 

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

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


Организация циклов

 

Цикл — это фрагмент программы, повторяемый многократно. В Паскале три оператора цикла — while, repeat и for. В принципе, без них можно обойтись, поскольку любой цикл можно реализовать с помощью условного оператора if и оператора перехода goto, но операторы цикла гораздо удобнее и нагляднее. У каждого из них есть предпочтительная область применения.

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

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

Цикл завершается, если условие его продолжения не выполняется. Возможно принудительное завершение как текущей итерации (для этого применяется процедура continue), так и цикла в целом (процедура break и оператор goto). Передавать управление извне внутрь цикла не рекомендуется, потому что при этом не выполнятся начальные установки. Иными словами, выйти из цикла можно в любой момент, а войти — только в начало (примерно как в самолете).

)

Цикл с предусловием while

 

В цикле с предусловием проверка условия продолжения цикла выполняется перед телом цикла (рис. 3.1, а). Если при входе в цикл условие не выполняется, он не будет выполнен ни разу. Оператор цикла имеет вид

 

while выражениеоператор

 

Рис. 3.1. Структурная схема операторов цикла:

а — цикл с предусловием; б — цикл с постусловием

 

Задача 3.1. Печать таблицы значений функции

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

 

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

------------------------------------------

│ X │ Y │

-------------------------------------------

│ -4.00 │ 0.76 │

 

│ -3.00 │ -0.14 │

 

│ -2.00 │ -0.91 │

 

│ -1.00 │ -0.84 │

 

│ -0.00 │ -0.00 │

 

│ -1.00 │ -0.84 │

 

│ -2.00 │ -0.91 │

 

│ -3.00 │ -0.14 │

 

│ -4.00 │ -0.76 │

-------------------------------------------

Рис. 3.2. Результаты выполнения программы для ; ; .

 

1. Опишем алгоритм в словесной форме.

2. Ввести исходные данные: , , .

3. Вывести заголовок таблицы.

4. Принять в качестве первого значения аргумента.

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

6. Вывести строку таблицы (текущее значение аргумента и соответствующее ему значение функции).

7. Перейти к следующему значению аргумента.

8. Если оно не превышает конечное значение, повторить шаги 4-6, иначе закончить.

 

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

 

program tabl_fun;

var

Xn, Xk: real; {начальное и конечное значения аргумента}

dX: real; {шаг изменения аргумента}

x,y: real; {текущие значения аргумента и функции}

begin

writeln ('Введите Xn, Xk, dX'); {приглашение ко вводу данных}

readln (Xn, Xk, dX); {ввод исходных данных - 1}

writeln ('------------------------------------'); {заголовок таблицы - 2}

writeln ('| X | Y |');

writeln ('------------------------------------');

x:= Xn; {первое значение аргумента = Xn - 3}

while x <= Xk do begin {заголовок цикла - 7}

y:= sin(x); {вычисление значение функции - 4}

writeln ('|', x:9:2, ' | ', y:9:2, ' | '); {вывод строки табл. - 5}

x:=x + dX; {переход к следующему значению аргумента - 6}

end;

writeln ('------------------------------------');

end.

 

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

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

Блок модификации параметра цикла представлен оператором, выполняющимся на шаге 6. Для перехода к следующему значению аргумента текущее значение наращивается на величину шага и заносится в ту же переменную. Начинающие часто забывают про модификацию параметра, в результате чего программа «зацикливается». Если с вами произошла такая неприятность, попробуйте для завершения программы нажать клавиши Ctrl+Break, а впредь перед запуском программы для параметра цикла проверяйте:

§ присвоено ли ему начальное значение;

§ изменяется ли он на каждой итерации цикла;

§ верно ли записано условие продолжения цикла.

 

 

Цикл с постусловием repeat

 

Оператор цикла с постусловием реализует структурную схему, приведенную на рис. 3.1, б, и имеет вид

 

repeat

тело цикла

until выражение

 

В отличие от цикла while, этот цикл будет выполняться, пока ложно логическое выражение, указанное после слова until. Как только результат выражения станет истинным, произойдет выход из цикла. Вычисление выражения выполняется в конце каждой итерации цикла. Тело цикла заключено между служебными словами repeat и until, поэтому дополнительно заключать его между ключевыми словами begin и end не требуется.

Цикл repeat применяется в тех случаях, когда тело цикла необходимо обязательно выполнить хотя бы один раз: например, если в нем вводятся данные и выполняется их проверка. Если же такой необходимости нет, предпочтительнее пользоваться циклом с предусловием.

 

Задача 3.3. Нахождение корня нелинейного уравнения

Найти корень уравнения методом деления пополам с точностью 0,0001.

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

Суть метода деления пополам очень проста. Задается интервал, в котором есть ровно один корень (следовательно, на концах этого интервала функция имеет значения разных знаков, как показано рис. 3.4). Вычисляется значение функции в середине этого интервала. Если оно того же знака, что и значение на левом конце интервала, значит, корень находится в правой половине интервала, иначе — в левой. Процесс повторяется для найденной половины интервала до тех пор, пока его длина не станет меньше заданной точности.

 

 

Рис. График функции

 

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

program root;

var x, L, R: real;

begin

L:= 0; R:= 1; {левая и правая границы интервала}

repeat

x:= (L + R) / 2; {середина интервала}

if (cos(x) - x) * (cos(L) - L)<0 {если значения разных знаков, то}

then R:= x {корень в правой половине интервала}

else L:= x {иначе корень в левой половине интервала}

until abs(R - L) < 1e-4;

writeln('Корень равен', x:9:4);

end.

 

 

Цикл с параметром for

 

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

 

for параметр:= выражение_1 to выражение_2 do оператор

for параметр: = выражение_2 downto выражение_1 do оператор

 

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

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

 

ВНИМАНИЕ

Если в теле цикла необходимо выполнить более одного оператора, необходимо заключить их в блок с помощью ключевых слов begin и end.

 

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

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

 

Задача 3.5. Пифагоровы числа

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

Пифагоровы числа — это тройки натуральных чисел, таких, что треугольник, длины сторон которого пропорциональны (или равны) этим числам, является прямоугольным.

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

 

program Pyfagor;

const

n = 15;

var

a, b, c, cx: integer;

begin

for a:= 1 to n do

for b:= a to n do begin { перебор всех пар чисел от 1 до 15 }

cx:= sqr(a) + sqr(b); { сумма квадратов пары чисел }

c:= trunc(sqrt(cx)); { кандидат на третье число }

if (sqr(c) = cx) and (c <= n) then writeln (a:3, b:3, c:3);

end;

end.

 

Итоги

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

2. Области применения операторов щикла:

o оператор for применяется, если требуется выполнить тело цикла заданное число раз;

o оператор repeat используют, когда цикл требуется обязательно выполнить хотя бы один раз, например, при анализе корректности ввода данных;

o оператор while удобнее во всех остальных случаях.

3. Выражение, определяющее условие продолжения циклов while и repeat, вычисляется в соответствии с приоритетами операций и должно иметь тип boolean.

4. Для принудительного перехода к следующей итерации цикла используется процедура continue, для преждевременного выхода из цикла — процедура break.

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

o заключать в блок тело циклов while и for, если в них требуется выполнить более одного оператора;

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

o проверять, изменяется ли в цикле хотя бы одна переменная, входящая в условие выхода из цикла;

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


Одномерные массивы

 

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

 

Пять простых переменных:

 

a b c d e
         

 

Массив из пяти элементов:

 

а[1] а[2] а[3] а[4] а[5]
         

 

Рис. 4.1. Простые переменные и массив

 

 

Описание массива

Чтобы описать массив, надо сообщить компилятору:

· сколько в нем элементов;

· какого типа эти элементы;

· как они нумеруются.

Массив не является стандартным типом данных, поэтому он задается в разделе описания типов:

 

type имя_типа = array [ тип_индекса ] of тип элемента

 

Здесь type — признак начала раздела описания типов, array и of — ключевые слова, тип индекса задается в квадратных скобках, например:

 

type mas = array [1.. 10] of real:

 

Здесь описан тип массива из вещественных элементов, которые нумеруются от 1 до 10. После задания типа его можно использовать точно так же, как любой стандартный тип, то есть описывать с его помощью переменные, например:

var a. b: mas:

Компилятор, встретив такой оператор, выделит по 60 байт под каждый из массивов а и b (10 элементов по 6 байт). К элементу массива обращаются, указав его имя, за которым в квадратных скобках записывается порядковый номер элемента:

 

а[4] b[i]

 

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

ЗАМЕЧАНИЕ

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

Если тип массива используется только в одном месте программы, можно задать тип прямо при описании переменных, например:

const п = 100:

var х. у: array [1.. n] of integer:

С массивами в целом можно выполнять только одну операцию — присваивание. При этом массивы должны быть одного типа, например:

 

х:= у:

 

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

 

Задача 4.1. Количество элементов между минимумом и максимумом

Написать программу, которая для 10 целочисленных элементов определяет, сколько положительных элементов располагается между максимальным и минимальным элементами.

Запишем алгоритм в самом общем виде:

1. Считать исходные данные в массив.

2. Определить, где расположены его максимальный и минимальный элементы., то есть найти их индексы.

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

 

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

 

  -8     -1     -10    
    макс +   + + мин    

 

Для этого примера программа должна вывести число 3.

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

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

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

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

Сформулируем алгоритм поиска максимума:

 

1. Принять за максимальный первый элемент массива.

2. Просмотреть массив, начиная со второго элемента.

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

 

Программа поиска максимального элемента приведена ниже:

program max_elem;

const n = 10;

var

a: array [1.. n] of integer; { массив }

i: integer; { номер текущего элемента }

max: integer; { максимальный элемент }

begin

writeln('Введите ', n, ' элементов массива');

for i:= 1 to n do read(a[i]);

max:= a[1]; {принять за максимальный первый элемент массива}

for i:= 2 to n do {просмотреть массив, начиная со второго элемента}

if a[i] > max { если очередной элемент больше максимального, }

then max:= a[i]; { принять за максимальный очередной элемент }

writeln('Максимальный элемент: ', max)

end.

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

Для решения поставленной задачи нам требуется знать не значение максимума, а его положение в массиве, то есть индекс:

 

program index_max_elem;

const n = 10;

var

a: array [1.. n] of integer; { массив }

i: integer; { номер текущего элемента }

imax: integer; { номер максимального элемента }

begin

writeln('Введите ', n, ' элементов массива');

for i:= 1 to n do read(a[i]);

imax:= 1;

for i:= 2 to n do

if a[i] > a[imax] then imax:= i;

writeln('Номер максимального элемента: ', imax);

writeln('Максимальный элемент: ', a[imax])

end.

 

Как видим, в этой программе в переменной imax запоминается номер максимального из просмотренных элементов. По этому номеру осуществляется выборка элемента из массива.

Запишем уточненный алгоритм решения нашей задачи:

1. Определить, где в массиве расположены его максимальный и минимальный элементы:

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

· просмотреть массив, поочередно сравнивая каждый его элемент с ранее найденными максимумом и минимумом. Если очередной элемент больше ранее найденного максимума, принять этот элемент за максимум (то есть запомнить его индекс). Если очередной элемент меньше ранее найденного минимума, принять этот элемент за минимум.

2. Определить границы просмотра массива для поиска положительных элементов, находящихся между его максимальным и минимальным элементами:

· если максимум расположен в массиве раньше, чем минимум, принять левую границу просмотра равной индексу максимума, иначе — индексу минимума;

· если максимум расположен в массиве раньше, чем минимум, принять правую границу просмотра равной индексу минимума, иначе — индексу максимума.

3. Определить количество положительных элементов в найденном диапазоне:

· обнулить счетчик положительных элементов;

· просмотреть массив в указанном диапазоне. Если очередной элемент больше нуля, увеличить счетчик на единицу.

Для экономии времени значения элементов массива при отладке задаются путем инициализации:

program num_positive_l;

const n = 10;

a: array [1.. n] of integer = (1, 3, -5, 1, -2, 1, -1, 3, 8, 4);

var

i: integer; { индекс текущего элемента }

imax: integer; { индекс максимального элемента }

imin: integer; { индекс минимального элемента }

ibeg: integer; { начало интервала }

iend: integer; { конец интервала }

count: integer; { количество положительных элементов }

begin

for i:= 1 to n do write(a[i]:3); writeln; { отладочная печать }

imax:= 1; imin:= 1; { начальные значения номеров макс, и мин. эл-тов }

for i:= 1 to n do begin

if a[i] > a[imax] then imax:= i; { новый номер максимума }

if a[i] < a[imin] then imin:= i; { новый номер мимимума }

end;

writeln(' max= ',a[imax], ' min= ', a[imin]); { отладочная печать }

if imax < imin then ibeg:= imax else ibeg:= imin; { левая граница }

if imax < imin then iend:= imin else iend:= imax; { правая граница }

writeln('ibeg = ', ibeg, ' iend=', iend); { отладочная печать }

count:= 0;

for i:= ibeg + 1 to iend - 1 do { подсчет количества положительных }

if a[i] > 0 then inc(count);

writeln(' Количество положительных: ', count);

end.

 

 

СОВЕТ

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

Массив просматривается начиная с элемента, следующего за максимальным (минимальным), до элемента, предшествующего минимальному (максимальному).

Тестовых примеров для этой задачи должно быть по крайней мере три — для случаев, когда:

· элемент a[imin] расположен левее элемента a[imax];

· элемент a[imin] расположен правее элемента a[imax];

· элементы a[imin] и a[imax] совпадают.

Последняя ситуация имеет место, когда в массиве все элементы имеют одно и то же значение. Желательно также проверить, как работает программа, если элементы a[imin] и a[imax] расположены рядом, а также в начале и в конце массива (граничные случаи). В массиве должны присутствовать как положительные, так и отрицательные элементы.


Работа с текстовыми файлами

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

Результат работы программы тоже бывает полезно выводить не на экран, а в текстовый файл для последующего неспешного анализа. Ниже приведена версия предыдущей программы, использующая файлы:

 

program num_positive_2;

const n = 10;

var

f_in, f_out: text; { 1 }

a: array [1.. n] of integer;

i, imax, imin, ibeg, iend, count: integer;

begin

assign(f_in, 'E:\input.txt'); { 2 }

reset(f_in); { 3 }

assign(f_out, 'E:\output.txt'); { 4 }

rewrite(f_out); { 5 }

for i:=1 to n do read(f_in, a[i]); { 6 }

imax:= 1; imin:= 1;

for i:=1 to n do begin

if a[i] > a[imax] then imax:= i;

if a[i] < a[imin] then imin:= i;

end;

if imax < imin then ibeg:= imax else ibeg:= imin;

if imax < imin then iend:= imin else iend:= imax;

count:= 0;

for i:= ibeg + 1 to iend - 1 do

if a[i] > 0 then inc(count);

writeln(f_out, ' Количество положительных:', count); { 7 }

close(f_out); { 8 }

end.

 

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

1. Объявить файловую переменную (оператор 1).

2. Связать ее с файлом на диске (операторы 2 и 4).

3. Открыть файл для чтения (оператор 3) или записи (оператор 5).

4. Выполнить операции ввода-вывода (операторы 6 и 7).

5. Закрыть файл (оператор 8).

В этой программе объявляются две переменные f_in и f_out стандартного типа «текстовый файл». Процедура assign связывает эти переменные с файлами на диске, путь к которым задается с помощью строк символов. Если полный путь не указан, предполагается, что файл находится в текущем каталоге. Процедура reset открывает файл для чтения, a rewrite — для записи. Если файл, который требуется открыть для записи, существует, он стирается и создается заново.

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

При вводе из файла и выводе в файл используются процедуры read, readln, write и writeln, первым аргументом в которые передается файловая переменная. Файл, в который выполняется запись, после окончания работы нужно обязательно закрывать с помощью процедуры close, иначе информация может быть потеряна.


Задача 4.3. Сжатие массива

Написать программу, которая «сжимает» целочисленный массив из 10 элементов, удаляя из него элементы, меньшие заданной величины. Освободившиеся в конце массива элементы заполнить нулями.

Исходный массив:

 

6 -8 15 9 -1 3 5 -10 12 2

 

Допустим, требуется удалить из него все элементы, зн



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-04-11 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: