Как правило, созданию программы предшествует разработка
(рис 3.1) Блок начала (конца) алгоритма
(рис 3.2) Блок ввода (вывода)
(рис 3.3) Арифметический блок
(рис 3.4) Условный блок
Рассмотренные блоки позволяют описать три основные конструкции алгоритма : линейный процесс, разветвляющийся процесс и циклический процесс.
Линейный процесс — это конструкция, представляющая собой последовательное выполнение двух или более блоков (рис. 3.5).
Разветвляющийся процесс задаёт выполнение одного или другого оператора в зависимости от выполнения условия (рис. 3.6).
Циклический процесс задаёт многократное выполнение оператора или группы операторов (рис. 3.7).
(рис 3.5) Линейный процесс
(рис 3.6) Разветвляющийся процесс
(рис 3.7) Циклический процесс
Нетрудно заметить, что каждая из основных конструкций алгоритма имеет один вход и один выход. Это позволяет вкладывать конструкции друг в друга произвольным образом и составлять алгоритмы для решения задач любой сложности.
Рассмотрим операторы языка программирования Free Pascal, реализующие основные конструкции алгоритма.
Оператор присваивания в языке Free Pascal состоит из двух символов: двоеточия и знака равенства. Символы := всегда пишут слитно. Пробелы допускаются перед символом двоеточия и после символа равенства.
В общем случае оператор присваивания имеет вид:
имя_переменной := значение;
где значение — это выражение, переменная, константа или функция. Выполняется оператор так. Сначала вычисляется значение выражения, указанного в правой части оператора, а затем его результат записывается в область памяти (переменную), имя которой указано слева. Например, запись a:=b означает, что переменной а присваивается значение выражения b.
Типы переменной a и выражения b должны совпадать или быть совместимыми для присваивания, то есть тип, к которому принадлежит b, должен находиться в границах типа переменной а.
Оператор присваивания, как и любой другой оператор в языке Free Pascal, отделяется от других точкой с запятой.
Составной оператор — группа операторов, отделённых друг от друга точкой с запятой, начинающихся со служебного слова begin и заканчивающихся служебным словом end:
begin оператор_1; ... оператор_n end;
Транслятор воспринимает составной оператор как один оператор.
В языке Free Pascal одна из основных конструкций алгоритма, разветвляющийся процесс, реализована двумя условными операторами: if и case. Рассмотрим каждый из них.
При решении большинства задач порядок вычислений зависит от определённых условий, например от исходных данных или от промежуточных результатов, полученных на предыдущих шагах программы. Для организации вычислений в зависимости от какого-либо условия в языке Free Pascal используется условный оператор if..then..else, который в общем виде записывается так:
(рис 3.8) Алгоритм условного оператора if..then..else
if условие then оператор_1 else оператор_2;
где if..then..else — зарезервированные слова, условие — выражение логического
оператор_1 и оператор_2 — любые операторы языка Free Pascal.
Работа условного оператора организована следующим образом. Сначала вычисляется выражение, записанное в условии. Если оно имеет значение истина (True), то выполняется оператор_1. В противном случае, когда выражение имеет значение ложь (False), оператор_1 игнорируется и управление передаётся оператору_2.
Алгоритм, который реализован в условном операторе if..then..else, представлен на рис. 3.8.
Например, чтобы сравнить значения переменных $$x$$ и $$y$$, нужно создать следующий программный код:
write ( ’ x= ’ ); readln ( x ); write ( ’ y= ’ ); readln ( y ); if x=y then writeln ( ’значение x равно значению y ’ ) else writeln ( ’значение x не равно значению y ’ );
Если в задаче требуется, чтобы в зависимости от значения условия выполнялся не один оператор, а несколько, необходимо использовать составной оператор:
if условие then begin оператор_1; оператор_2; ... оператор_n; end else begin оператор_1; оператор_2; ... оператор_n; end;
Альтернативная ветвь else в условном операторе может отсутствовать, если в ней нет необходимости:
if условие then оператор; или if условие then begin оператор_1; оператор_2; ... оператор_n; end;
В таком "усечённом" виде условный оператор работает так: оператор (группа операторов) либо выполняется, либо пропускается в зависимости от значения выражения, представляющего условие. Алгоритм этого условного процесса представлен на рис. 3.9.
Пример применения условного оператора, без альтернативной ветви else может быть таким:
write ( ’ x= ’ ); readln ( x );
write ( ’ y= ’ ); readln ( y );
c : = 0;
{Значение переменной c изменяется только при условии,}
{что x не равно y.}
if ( x<>y ) then c :=x+y;
{Вывод на экран значения переменной c}
{выполняется в любом случае.}
writeln ( ’Значение переменной с= ’, c );
Условные операторы могут быть вложены друг в друга. При вложениях условных операторов всегда действует правило: альтернатива else считается принадлежащей ближайшему if, имеющему ветвь else. Например, в записи он принадлежит оператору if с условием_1.
(рис 3.9) Алгоритм условного оператора if без альтернативной ветви else
if условие_1 then if условие_2 then оператор_А else оператор_Б;
оператор_Б относится к условию_2, а в конструкции
if условие_1 then begin if условие_2 then оператор_А; end else оператор_Б;
Для сравнения переменных в условных выражениях применяют операции отношения : =, <>, <, >, <=, >=. Условные выражения составляют с использованием логических операций and, or и not. В языке Free Pascal приоритет операций отношения меньше, чем у логических операций, поэтому составные части сложного логического выражения заключают в скобки.
Допустим, нужно проверить, принадлежит ли переменная $$x$$ интервалу $$[a, b]$$. Условный оператор будет иметь вид:
if ( x>=a ) and ( x<=b ) then...
Запись
if x>=a and x<=b then...
неверна, так как фактически будет вычисляться значение выражения
x>=(a and x)<=b.
(рис 3.10) Графическое представление задачи 3.1
Рассмотрим использование оператора if на
Аналитически функцию, представленную на рис. 3.10, можно записать так:
$$y(x) = \begin{cases} 4, x \le -2, \\ x^2, -2 < x < 1, \\ 1, x \ge 1. \end{cases}$$Составим словесный алгоритм решения этой задачи:
Блок-схема, соответствующая описанному алгоритму, представлена на рисунке 3.11. Как видно, блок-схема нагляднее и проще для восприятия, чем словесное описание алгоритма. В дальнейшем для описания алгоритма мы часто будем использовать именно блок-схемы.
(рис 3.11) Блок-схема алгоритма решения задачи 3.1
Текст программы на языке Free Pascal будет иметь вид:
var x, y : real; begin write ( ’ x= ’ ); readln ( x ); if x<= -2 then y:=4 else if x>=1 then y:=1 else y:= sqr ( x ); writeln ( ’ x= ’, x : 5 : 2, ’ y= ’, y : 5 : 2 ); end.
Эту программу можно ввести в текстовом редакторе Geany (или в текстовом редакторе Free Pascal) и запустить на выполнение.
Как показано на рис. 3.12, область ограничена линиями $$x = -1, x = 3, y = -2$$ и $$y = 4$$. Значит, точка с координатами $$(x; y)$$ будет принадлежать этой области, если будут выполняться следующие условия: $$x \ge -1, x \le 3, y \ge -2$$ и $$y \le 4$$. Иначе точка лежит за пределами области.
Блок-схема, описывающая алгоритм решения данной задачи, представлена на рис. 3.13.
(рис 3.12) Графическое представление задачи 3.2
(рис 3.13) Алгоритм решения задачи 3.2
Текст программы к задаче 3.2:
var x, y : real; begin write ( ’ x= ’ ); readln ( x ); write ( ’ y= ’ ); readln ( y ); if ( x>= -1) and ( x<=3) and ( y>= -2) and ( y<=4) then writeln ( ’Точка принадлежит области ’ ) else writeln ( ’Точка не принадлежит области ’ ); end.
Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.
(рис 3.14) Алгоритм решения квадратного уравнения
Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — корни квадратного уравнения — либо сообщение о том, что корней нет.
Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.
Составим словесный алгоритм решения этой задачи.
"Действительных корней нет" и переход к п. 8.Блок-схема, соответствующая этому описанию, представлена на рис. 3.14.
Текст программы, которая реализует решение квадратного уравнения:
{Описание переменных.}
var a, b, c, d, x1, x2 : real;
begin
{Ввод значения коэффициентов квадратного уравнения.}
writeln ( ’Введите коэффициенты квадратного уравнения ’ );
readln ( a, b, c );
{Вычисление дискриминанта.}
d:=b * b-4*a*c;
{Если дискриминант отрицателен,}
if d<0 then
{то вывод сообщения, что корней нет,}
writeln ( ’Действительных корней нет ’ )
else
begin
{иначе вычисление корней x1, x2}
x1:=(-b+sqrt ( d ) ) / 2 / a;
x2:=(-b_sqrt ( d ) ) / ( 2 * a );
{и вывод их на экран.}
writeln ( ’X1= ’, x1 : 6 : 3, ’ -X2= ’, x2 : 6 : 3 )
end
end.
Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.
Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — действительные корни квадратного уравнения — либо $$x_1$$ и $$x_2$$ — действительная и мнимая части комплексного числа.
Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.
Можно выделить следующие этапы решения задачи:
и вывод их на экран. При отрицательном дискриминанте выводится сообщение о том, что действительных корней нет, и вычисляются комплексные
(рис 3.15) Алгоритм решения задачи 3.4
$$\frac{-b}{2a}+i\frac{\sqrt{|d|}}{2a},
\frac{-b}{2a}-i\frac{\sqrt{|d|}}{2a}.$$
У обоих комплексных корней действительные части одинаковые, а мнимые отличаются знаком. Поэтому можно в переменной x1 хранить действительную часть числа $$-b/(2a)$$, в переменной x2 — модуль мнимой части $$\sqrt{|d|}/(2a)$$, а в качестве корней вывести x1+ix2 и x1-ix2.
На рис. 3.15 изображена блок-схема решения задачи. Блок 1 предназначен для ввода коэффициентов квадратного уравнения. В блоке 2 осуществляется вычисление дискриминанта. Блок 3 осуществляет проверку знака дискриминанта; если дискриминант отрицателен, то корни комплексные, их расчёт происходит в блоке 4 (действительная часть корня записывается в переменную x1, модуль мнимой — в переменную x2), а вывод — в блоке 5 (первый корень x1+ix2, второй — x1-ix2). Если дискриминант положителен, то вычисляются действительные корни уравнения (блоки 6—7) и выводятся на экран (блок 8).
Текст программы, реализующей поставленную задачу:
var a, b, c, d, x1, x2 : real;
begin
writeln ( ’Введите_коэффициенты_квадратного_уравнения ’ );
readln ( a, b, c );
d:=b * b-4*a*c;
if d<0 then
begin
//Если дискриминант отрицателен, то вывод сообщения,
//что действительных корней нет, и вычисление комплексных корней.
writeln ( ’Действительных корней нет ’ );
{Вычисление действительной части комплексных корней.}
x1:=-b /(2 * a );
{Вычисление модуля мнимой части комплексных корней.}
x2:= sqrt ( abs ( d ) ) / ( 2 * a );
writeln ( ’Комплексные корни уравнения ’,
a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
{Вывод значений комплексных корней в виде $$x1\pm ix2$$}
writeln ( x1 : 1 : 2, ’+i * ( ’, x2 : 1 : 2, ’ ) ’ );
writeln ( x1 : 1 : 2, ’- i * ( ’, x2 : 1 : 2, ’ ) ’ );
end
else
begin
{иначе вычисление действительных корней x1, x2}
x1:=(-b+sqrt ( d ) ) / 2 / a;
x2:=(-b_sqrt ( d ) ) / ( 2 * a );
{и вывод их на экран.}
writeln ( ’Действительные корни уравнения ’,
a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
writeln ( ’X1= ’, x1 : 1 : 2, ’ X2= ’, x2 : 1 : 2 )
end
end.
Кубическое уравнение имеет вид
$$ax^3 + bx^2 + cx + d = 0$$После деления на $$a$$ уравнение (3.1) принимает канонический вид:
$$x^3 + rx^2 + sx + t = 0$$где $$r = b/a, s = c/a, t = d/a$$. В уравнении (3.2) сделаем замену $$x = y - r/3$$ и получим приведённое уравнение:
$$y^3 + py + q = 0,$$где
$$p = \frac{3s-r^2}{3}, q = \frac{2r^{3}}{27} - \frac{rs}{3}+t.$$Число действительных корней приведённого уравнения (3.3) зависит от знака дискриминанта $$D = (p/3)^3+ (q/2)^2$$ приведённого кубического уравнения (табл. 3.1).
| Дискриминант | Количество действительных корней | Количество комплексных корней |
|---|---|---|
| D > 0 | 1 | 2 |
| D < 0 | 3 | — |
Корни приведённого уравнения могут быть рассчитаны по формулам Кардано:
$$\begin{aligned} y_{1} = u+v\\ y_{2} = \frac{-{u+v}}{2}+\frac{u-v}{2}i\sqrt{3}\\ y_{3} = \frac{-{u+v}}{2}-\frac{u-v}{2}i\sqrt{3} \end{aligned}$$где
$$u=\sqrt[{3}]{-q/2+\sqrt{D}}, v=\sqrt[{3}]{-q/2-\sqrt{D}}.$$При отрицательном дискриминанте уравнение (3.1) имеет три действительных корня, но они будут вычисляться через вспомогательные комплексные величины. Чтобы избавиться от этого, можно воспользоваться формулами:
$$\begin{aligned} y_1=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}\right),\\ y_2=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{2\pi}{3}\right),\\ y_3=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{4\pi}{3}\right), \end{aligned}$$где
$$\rho =\sqrt{\frac{-p^3}{27}}, \cos(\varphi)=\frac{-q}{2\rho}.$$Таким образом, при положительном дискриминанте кубического уравнения (3.3) расчёт корней будем вести по формулам (3.4), а при отрицательном — по формулам (3.5).
После расчёта корней приведённого уравнения (3.3) по формулам (3.4) или (3.5) необходимо по формулам
$$x_{k}=y_{k}-\frac{r}{3}, k=1,2,3\ldots$$перейти к корням заданного кубического уравнения (3.1).
Блок-схема решения кубического уравнения представлена на рис. 3.16.
(рис 3.16) Алгоритм решения кубического уравнения
Описание блок-схемы. В блоке 1 вводятся коэффициенты кубического уравнения, в блоках 2—3 рассчитываются коэффициенты канонического и приведённого уравнений. Блок 4 предназначен для вычисления дискриминанта. В блоке 5 проверяется знак дискриминанта кубического уравнения. Если он отрицателен, то корни вычисляются по формулам (3.5) (блоки 6—7). При положительном значении дискриминанта расчёт идет по формулам (3.4) (блок 9, 10). Блоки 8 и 11 предназначены для вывода результатов на экран.
Текст программы с комментариями приведён
var a, b, c, d, r, s, t, p, q, ro, f i, x1, x2, x3, u, v, h, g : real; begin //Ввод коэффициентов кубического уравнения. write ( ’ a= ’ ); readln ( a ); write ( ’ b= ’ ); readln ( b ); write ( ’ c= ’ ); readln ( c ); write ( ’ d= ’ ); readln ( d ); //Расчёт коэффициентов канонического уравнения по 3.2. r :=b/a; s := c /a; t :=d/a; //Вычисление коэффициентов приведённого уравнения 3.3. p :=(3 * s - r * r ) / 3; q:=2* r * r * r /27- r * s/3+ t; //Вычисление дискриминанта кубического уравнения. d :=( p /3) * sqr ( p/3)+ sqr ( q / 2 ); //Проверка знака дискриминанта, //ветка then реализует формулы (3.5), //ветка else — формулы 3.4 if d<0 then begin ro := sqrt (-p * p * p / 27 ); //Следующие два оператора реализуют расчёт угла fi, //сначала вычисляется величина косинуса угла, //затем вычисляется его арккосинус через арктангенс. fi :=-q /(2 * ro ); fi := pi /2-arctan ( fi / sqrt (1 - fi * fi ) ); //Вычисление действительных корней уравнения x1, x2 и x3 x1 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3) - r / 3; x2 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+2 * pi /3) - r / 3; x3 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+4 * pi /3) - r / 3; writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, x2 : 1 : 3, ’ x3= ’, x3 : 1 : 3 ); end else begin //Вычисление u и v с проверкой знака // подкоренного выражения. if -q/2+ sqrt ( d)>0 then u:=exp (1/3 * ln (-q/2+ sqrt ( d ) ) ) else if -q/2+ sqrt ( d)<0 then u:=-exp (1/3 * ln ( abs(-q/2+ sqrt ( d ) ) ) ) else u : = 0; if -q/2- sqrt ( d)>0 then v:=exp (1/3 * ln (-q/2- sqrt ( d ) ) ) else if -q/2- sqrt ( d)<0 then v:=-exp (1/3 * ln ( abs(-q/2- sqrt ( d ) ) ) ) else v : = 0; //Вычисление действительного корня кубического уравнения. x1:=u+v-r / 3; //Вычисление действительной и // мнимой части комплексных корней. h:=-(u+v)/2 - r / 3; g :=(u - v )/2 * sqrt ( 3 ); writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, h : 1 : 3, ’+i* ’, g : 1 : 3, ’ x3= ’, h : 1 : 3, ’ -i* ’, g : 1 : 3 ); end end.
Входные данные: a, b, c. Выходные данные: x1, x2, x3, x4.
Для решения биквадратного уравнения необходимо заменой $$y = x^2$$ привести его к квадратному уравнению $$ay^2 + by + c = 0$$ и решить это уравнение.
Опишем алгоритм решения этой задачи (рис. 3.17):
(рис 3.17) Алгоритм решения биквадратного уравнения
Текст программы на языке Free Pascal с комментариями:
//Описание переменных: //a,b,c - коэффициенты биквадратного уравнения, //d - дискриминант, //x1,x2,x3,x4 - корни биквадратного уравнения, //y1,y2 - корни квадратного уравнения ay^2+by+c=0. var a, b, c, d, x1, x2, x3, x4, y1, y2 : real; begin //Ввод коэффициентов уравнения. writeln ( ’Введите коэффициенты биквадратного уравнения ’ ); readln ( a, b, c ); //Вычисление дискриминанта. d:=b * b-4*a*c; //Если он отрицателен, if d<0 then //вывод сообщения "Корней нет" writeln ( ’Корней нет ’ ) //Если дискриминант >= 0, else begin //вычисление корней квадратного уравнения. y1:=(-b+sqrt ( d ) ) / 2 / a; y2:=(-b-sqrt ( d ) ) / ( 2 * a ); //Если оба корня квадратного уравнения < 0, if ( y1 <0) and ( y2 <0) then //вывод сообщения "Корней нет". writeln ( ’Корней нет ’ ) //Если оба корня квадратного уравнения >= 0, else if ( y1>=0) and ( y2>=0) then begin //вычисление четырех корней биквадратного уравнения. x1:= sqrt ( y1 ); x2:=-x1; x3:= sqrt ( y2 ); x4:=-sqrt ( y2 ); //Вывод корней биквадратного уравнения на экран. writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 ); writeln ( ’X3= ’, x3 : 6 : 3, ’ X4= ’, x4 : 6 : 3 ); end //Если не выполнились оба условия // 1. y1<0 И y2<0 // 2. y1>=0 И y2>=0, //то проверяем условие y1>=0 else if ( y1>=0) then //Если оно истинно begin x1:= sqrt ( y1 ); x2:=-x1; writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 ); end else //Если условие y1>=0 ложно, то begin x1:= sqrt ( y2 ); x2:=-x1; writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 ); end end end.
Оператор варианта case необходим в тех случаях, когда в зависимости от значений какой-либо переменной надо выполнить те или иные операторы.
case выражение of значение_1 : оператор_1; значение_2 : оператор_2; ... значение_N : оператор_N else альтернативный_оператор end;
Здесь выражение — переменная перечислимого типа (включая char и boolean), значение_1, значение_2,..., значение_N — это конкретное значение управляющей переменной или выражение, при котором необходимо выполнить соответствующий оператор, игнорируя остальные варианты. Значения в каждом наборе должны быть уникальны, то есть они могут появляться только в одном варианте. Пересечение наборов значений для разных вариантов является ошибкой.
Оператор работает следующим образом. Вычисляется значение выражения. Затем выполняется оператор, помеченный значением, совпадающим со значением выражения. То есть, если выражение принимает значение_1, то выполняется оператор_1. Если выражение принимает значение_2, то выполняется оператор_2 и так далее. Если выражение не принимает ни одно из значений, то выполняется альтернативный_оператор, расположенный после ключевого слова else.
Альтернативная ветвь else может отсутствовать, тогда оператор имеет вид:
case выражение of значение_1 : оператор_1; значение_2 : оператор_2; ... значение_N : оператор_N; end;
Кроме того, в операторе case допустимо использование составного оператора. Например:
case выражение of значение_1 : begin оператор_A; оператор_B; end; значение_2 : begin оператор_C; оператор_D; оператор_E; end; ... значение_N : оператор_N; end;
Рассмотрим применение оператора варианта на следующих примерах.
D, при условии, что в месяце 31 день и первое число — понедельник.Для решения задачи воспользуемся операцией mod, позволяющей вычислить остаток от деления двух чисел, и условием, что первое число — понедельник. Если в результате остаток от деления (обозначим его R) заданного числа D на семь будет равен единице, то это понедельник, двойке — вторник, тройке — среда и так далее. Следовательно, при построении алгоритма необходимо использовать семь условных операторов, как показано на рис. 3.18.
Решение задачи станет значительно проще, если при написании программы воспользоваться оператором варианта:
var d : byte;
begin
write ( ’Введите число D= ’ );
readln (D);
case D mod 7 of {Вычисляется остаток от деления D на 7.}
{В зависимости от полученного значения}
{на печать выводится название дня недели}
1 : writeln ( ’ПОНЕДЕЛЬНИК ’ );
2 : writeln ( ’ВТОРНИК ’ );
3 : writeln ( ’СРЕДА ’ );
4 : writeln ( ’ЧЕТВЕРГ ’ );
5 : writeln ( ’ПЯТНИЦА ’ );
6 : writeln ( ’СУББОТА ’ );
0 : writeln ( ’ВОСКРЕСЕНЬЕ ’ );
end;
end.
(рис 3.18) Алгоритм решения задачи 3.7
В предложенной записи оператора варианта отсутствует ветвь else. Это объясняется тем, что переменная R может принимать только одно из указанных значений, т. е. 1, 2, 3, 4, 5, 6 или 0.
m вывести на печать название времени года.Для решения данной задачи необходимо проверить выполнение четырёх условий. Если заданное число m равно 12, 1 или 2, то это зима, если m попадает в диапазон от 3 до 5, то весна; лето определяется принадлежностью числа m диапазону от 6 до 8, и, соответственно, при равенстве переменной m 9, 10 или 11 это осень. Понятно, что область возможных значений переменной m находится в диапазоне от 1 до 12, и если пользователь введет число, не входящее в этот интервал, то появится сообщение об ошибке. Для этого в операторе case программы предусмотрена альтернативная ветка else.
var m: byte; begin write ( ’Введите номер месяца m= ’ ); readln (m); //Проверка области допустимых значений переменной m. case m of //В зависимости от значения m на печать //выводится название времени года. 1 2, 1, 2 : writeln ( ’ЗИМА ’ ); } 3.. 5 : writeln ( ’ВЕСНА ’ ); } 6.. 8 : writeln ( ’ЛЕТО ’ ); } 9.. 1 1 : writeln ( ’ОСЕНЬ ’ )} //Если значение переменной m выходит за пределы области //допустимых значений, то выдается сообщение об ошибке. else writeln ( ’ОШИБКА ПРИ ВВОДЕ ! ! ! ’ ); end end.
Понятно, что чем меньше в программе ошибок, тем она лучше. В очень хорошей программе ошибок нет вообще. А это значит, что программист должен не только основательно продумать алгоритм поставленной задачи, но и предугадать ошибки, которые может допустить пользователь, работая с программой.
Если пользователь допустил ошибку, например, при вводе данных, его необходимо проинформировать об этом. Для этого можно воспользоваться функцией MessageDlg, которая выводит сообщение в отдельном окне. В общем виде функцию записывают так:
MessageDlg (сообщение, тип_сообщения, [ список_кнопок ], справка );
где
сообщение — текст, который будет отображён в окне сообщения;тип_сообщения — определяет внешний вид окна (табл. 3.2);список_кнопок — константы (перечисляются через запятую), определяющие тип кнопок окна сообщения (табл. 3.3);справка — номер окна справочной системы, которое будет выведено на экран, если нажать F1; параметр равен нулю, если использование справки не предусмотрено.| Параметр | Тип окна сообщения |
|---|---|
mtInformation |
информационное |
mtWarning |
предупредительное |
mtError |
сообщение об ошибке |
mtConfirmation |
запрос на подтверждение |
mtCustom |
обычное |
| Константа | Кнопка |
|---|---|
mbYes |
Да |
mbNo |
Нет |
mbOk |
Ок |
| mbCancel | Отмена |
mbAbort |
Прервать |
mbRetry |
Повторить |
mbIgnore |
Пропустить |
mbHelp |
Помощь |
(рис 3.19) Конструирование формы для решения квадратного уравнения
Вернёмся к задаче решения квадратного уравнения (задача 3.3). Нами был рассмотрен алгоритм решения этой задачи и написана программа на языке программирования Free Pascal. Реализуем эту задачу в среде Lazarus.
Создадим новый
Для организации ввода коэффициентов уравнения внедрим на форму четыре объекта типа надпись (Label1, Label2, Label3, Label4) и три поля ввода (Edit1, Edit2, Edit3). Корни уравнения или сообщение об их отсутствии будем выводить в надпись
Label5 невидимой, для чего свойство Visible установим в false (Label5.Visible:=false).Button1.
При вводе данных в программе могут возникнуть следующие ошибки:
a равно
Для того чтобы не допустить подобные ошибки, необходимо контролировать данные, вводимые пользователем. Применим для этой цели встроенную процедуру Val(S,X,Kod), которая преобразовывает строку S в число. Тип этого числа (целое или вещественное) зависит от типа переменной X. Если преобразование прошло успешно, то параметр Kod принимает значение, равное нулю, а результат преобразования записывается в переменную X. В противном случае Kod содержит номер позиции в строке S, где произошла ошибка, и содержимое переменной X не меняется. Далее приведён фрагмент программы с подробными комментариями:
procedure TForm1. Button1Click ( Sender : TObject ); var a, b, c, d, x1, x2 : real; kod1, kod2, kod3 : integer; begin //Ввод значений коэффициентов квадратного уравнения. //Из поля ввода Edit1 считывается строка символов и //преобразовывается в вещественное число, //если преобразование прошло успешно, то kod1=0 и //полученное число записывается в переменную a. val ( Edit1. Text, a, kod1 ); val ( Edit2. Text, b, kod2 ); val ( Edit3. Text, c, kod3 ); //Если преобразования прошли успешно, то if ( kod1=0) and ( kod2=0) and ( kod3=0) then //проверить чему равен первый коэффициент. //Если значение первого коэффициента равно нулю, то if a=0 then //выдать соответствующее сообщение. MessageDlg ( ’Введите не нулевое значение а ’, mtInformation, [ mbOk], 0 ) else //иначе перейти к решению квадратного уравнения begin d:=b * b-4*a*c; Label5. Visible := true; if d<0 then Label5. Caption := ’В уравнении ’ +chr (13)+ ’нет действительных корней ’ else begin x1:=(-b+sqrt ( d ) ) / 2 / a; x2:=(-b-sqrt ( d ) ) / ( 2 * a ); Label5. Caption := ’X1= ’+FloatToStr ( x1 ) +chr (13)+ ’X2= ’+FloatToStr ( x2 ); end; end else //Преобразование не выполнено, выдать сообщение. MessageDlg ( ’Введите числовое значение ’, mtInformation, [ mbOk ], 0 ); end.
(рис 3.20) Обработка ошибки ввода данных — коэффициент a равен 0
Решение квадратного уравнения $$3x^2+4x+5=0$$
(рис 3.21) Решение квадратного уравнения(корней нет)
(рис 3.22) Обработка ошибки ввода данных — в поле ввода строка, которую невозможно преобразовать в число
(рис 3.23) Вычисление корней квадратного уравнения
Результаты работы программы показаны на рис. 3.20,рис. 3.21,рис. 3.22,3.23.
Метод обработки ошибок ввода, изложенный в этом примере, не единственный. Можно попытаться контролировать символы, поступающие в поля ввода. Набор символов, которые могут быть преобразованы в число, невелик. Это все цифры от 0 до 9, знак минус и символ запятой. Кроме того, пользователь должен иметь возможность удалять символы из поля ввода. Для этого ему понадобится клавиша BackSpace (#8 — символ с кодом восемь). Представим набор перечисленных символов в виде множества, и если окажется, что введённый символ не принадлежит ему, то будем выводить сообщение об ошибке при вводе.
Возможность контролировать ввод символов обеспечивает событие OnKeyPress. Если выделить компонент Edit2 и на вкладке инспектора объектов дважды щёлкнуть возле события нажатия клавиши OnKeyPress, то будет создана процедура обработки этого события. Текст процедуры с комментариями:
//Обработка события ввод символа в поле Edit2. procedure TForm1. Edit2KeyPress ( Sender : TObject; var Key : Char ); begin //Если символ не принадлежит //множеству допустимых символов,то if not ( Key in [#8, ’, ’, ’_ ’, ’ 0 ’.. ’ 9 ’ ] ) then begin //выдать сообщение и MessageDlg ( ’Введите числовое значение ’, mtInformation, [ mbOk ], 0 ); Abort; //прервать выполнение подпрограммы. end; end.
Циклический процесс, или просто цикл — это повторение одних и тех же действий. Последовательность действий, которые повторяются в цикле, называют телом цикла. Один проход цикла называют шагом, или
При написании циклических алгоритмов следует помнить следующее. Во-первых, чтобы цикл имел шанс когда-нибудь закончиться, содержимое его тела должно обязательно влиять на условие цикла. Во-вторых, условие должно состоять из корректных выражений и значений, определённых ещё до первого выполнения тела цикла.
В языке Free Pascal для удобства программиста предусмотрены три оператора, реализующих циклический процесс: while, repeat..until и for.
На рис. 3.24 изображена блок-схема алгоритма цикла с предусловием.
Оператор, реализующий этот алгоритм, в языке Free Pascal имеет вид:
while выражение do оператор;
(рис 3.24) Алгоритм циклической структуры с предусловием
Выражение должно быть логической константой, переменной или логическим выражением.
Работает оператор while следующим образом. Вычисляется значение выражения.
Если оно истинно (True), выполняется оператор. Выполнение цикла заканчивается, если условие ложно, после этого управление передается оператору, следующему за телом цикла. Выражение вычисляется перед каждой итерацией цикла. Если при первой проверке выражение ложно (False), цикл не выполнится ни разу.
Если в цикле надо выполнить более одного оператора, необходимо использовать составной оператор:
while условие do begin оператор_1; оператор_2; ... оператор_n; end;
Рассмотрим пример. Пусть необходимо вывести на экран значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0;\pi]$$ с шагом 0.1.
Применим цикл с предусловием :
var x, y : real;
begin
{Присваивание параметру цикла стартового значения.}
x : = 0;
{Цикл с предусловием.}
while x<=pi do {Пока параметр цикла не превышает}
{конечное значение, выполнять тело цикла.}
begin
{Вычислить значение y.}
y:=exp ( sin ( x ) ) * cos ( x );
{Вывод на экран пары х и y.}
writeln ( ’ x= ’, x, ’ y= ’, y );
{Изменение параметра цикла - переход к следующему значению x.}
x:=x + 0.1;
end; {Конец цикла.}
end.
В результате работы данного фрагмента программы на экран последовательно будут выводиться сообщения со значениями переменных x и y:
x= 0; y=1 x= 0.1; y =1.0995 ... x= 3.1; y= -1.0415
Если в цикле с предусловием проверка условия осуществляется до тела цикла, то в цикле с постусловием условие проверяется после тела цикла (см. рис. 3.25). Сначала выполняются операторы, являющиеся телом цикла, после чего проверятся условие, если последнее ложно, то цикл повторяется. Выполнение цикла прекратится, если условие станет истинным.
(рис 3.25) Алгоритм циклической структуры с постусловием
В языке Free Pascal цикл с постусловием реализован конструкцией
repeat оператор; until выражение;
или, если тело цикла состоит более чем из одного оператора
repeat оператор_1; оператор_2; ... оператор_N; until выражение;
Выражение должно быть логическим.
Работает цикл следующим образом. В начале выполняется оператор, представляющий собой тело цикла. Затем вычисляется значение выражения. Если оно ложно (False), оператор тела цикла выполняется ещё раз. В противном случае цикл завершается, и управление передается оператору, следующему за циклом.
Таким образом, нетрудно заметить, что цикл с постусловием всегда будет выполнен хотя бы один раз, в отличие от цикла с предусловием, который может не выполниться ни разу.
Если применить цикл с постусловием для создания подпрограммы, которая выводит значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1, получим:
var x, y : real;
begin
{Присваивание параметру цикла стартового значения.}
x : = 0;
{Цикл с постусловием.}
repeat {Начало цикла}
y:=exp ( sin ( x ) ) * cos ( x );
writeln ( ’ x= ’, x, ’ y= ’, y );
x:=x + 0.1; {Изменение значения параметра цикла.}
until x > pi; {Закончить работу цикла,}
{когда параметр превысит конечное значение.}
end.
Операторы цикла с условием обладают значительной гибкостью, но не слишком удобны для организации "строгих" циклов, которые должны быть выполнены заданное число раз. Оператор цикла for..do используется именно в таких случаях:
for параметр_цикла:=начальное_значение to конечное_значение do оператор; for параметр_цикла:=конечное_значение downto начальное_значение do оператор;
где оператор — любой оператор языка, параметр_цикла — имя переменной целочисленного или перечислимого типов, начальное_значение и конечное_значение должны быть того же типа, что и параметр_цикла.
Шаг изменения цикла for всегда постоянен и равен интервалу между двумя ближайшими значениями типа параметра цикла (при целочисленном значении параметра цикла шаг равен 1).
В случае если тело цикла состоит более чем из одного оператора, необходимо использовать составной оператор:
(рис 3.26) Алгоритм работы цикла for..do
(рис 3.27) Представление цикла for...do с помощью блок-схемы
for параметр_цикла:=начальное_значение to конечное_значение do begin оператор_1; оператор_2; ... оператор_N; end;
Опишем алгоритм работы цикла for..do (рис. 3.26).
Параметру_цикла присваивается начальное_значение.параметра_цикла превосходит конечное_значение, то цикл завершает свою работу. В противном случае выполняется п. 3.оператор.параметра_цикла изменяется на соответствующий шаг и осуществляется переход к п. 2, и т. д.Понятно, что этот алгоритм представляет собой цикл с предусловием.
В дальнейшем, чтобы избежать создания слишком громоздких алгоритмов, в блок-схемах цикл for будем изображать так, как показано на рис. 3.27
Фрагмент подпрограммы, приведённый далее, демонстрирует применение цикла for:
var i : integer; c : char;
begin
{Вывод на экран чисел от 1 до 10.}
for i :=1 to 10 do
writeln ( i );
{Вывод на экран чисел от 10 до -10.}
for i :=10 downto -10 do
writeln ( i );
{Вывод на экран символов от a до r.}
for c := ’ a ’ to ’ r ’ do
writeln ( c );
end.
Вернёмся к задаче вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1. Как видим, здесь количество повторений цикла явно не задано. Однако это значение, можно легко вычислить. Предположим, что параметр цикла $$x$$ принимает значения в диапазоне от $$x_n$$ до $$x_k$$, изменяясь с шагом $$dx$$, тогда количество повторений тела цикла можно определить по формуле:
$$n = \round\left(\frac{x_k-x_n}{dx}\right)+1$$округлив результат деления до целого числа. Следовательно, фрагмент программы вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1 будет иметь вид:
var i, n : integer; x, y : real;
begin
n:=round ( ( pi - 0)/0.1)+1; {Количество повторений цикла.}
x : = 0; {Начальное значение аргумента.}
{Цикл с известным числом повторений,}
{i - параметр цикла, изменяется от 1 до n с шагом 1.}
for i :=1 to n do
begin {Начало цикла.}
y:=exp ( sin ( x ) ) * cos ( x ); {Вычисление значения функции }
{для соответствующего значения аргумента.}
writeln ( ’ x= ’, x, ’ y= ’, y );
x:=x + 0.1; {Вычисление нового}
{значения аргумента.}
end; {Конец цикла.}
end.
Операторы передачи управления принудительно изменяют порядок выполнения команд. В языке Free Pascal таких операторов пять: goto, break, continue, exit и halt.
Оператор goto метка, где метка — обычный идентификатор, применяют для безусловного перехода, он передаёт управление оператору с меткой.
метка : оператор;
Обычно применение оператора goto приводит к усложнению программы и затрудняет отладку. Использование оператора нарушает принцип структурного
Операторы break и continue используют только внутри циклов. Оператор break осуществляет немедленный выход из циклов repeat, while, for, и управление передается оператору, находящемуся непосредственно за циклом. Оператор continue начинает новую итерацию цикла, даже если предыдущая не была завершена.
Оператор exit осуществляет выход из подпрограммы.
Оператор halt прекращает выполнение программы.
Рассмотрим использование циклических операторов на конкретных примерах.
Входные данные: A и B. Выходные данные: А — НОД.
Для решения поставленной задачи воспользуемся алгоритмом Евклида: будем уменьшать каждый раз большее из чисел на величину меньшего до тех пор, пока оба значения не станут равными, так, как показано в таблице 3.4.
В блок-схеме решения задачи, представленной на рис. 3.28, для решения поставленной задачи используется цикл с предусловием, то есть тело цикла повторяется до тех пор, пока А не равно В. Следовательно, при создании программы воспользуемся циклом while..do.
| Исходные данные | Первый шаг | Второй шаг | Третий шаг | НОД(A,B)=5 |
|---|---|---|---|---|
А=25 |
А=10 |
А=10 |
А=5 |
|
В=15 |
В=15 |
В=5 |
В=5 |
(рис 3.28) Алгоритм поиска наибольшего общего делителя двух чисел
Программа на языке Free Pascal, реализующая поставленную задачу:
var a, b : word;
begin
writeln ( ’введите два натуральных числа ’ );
write ( ’A= ’ ); readln ( a );
write ( ’B= ’ ); readln ( b );
{Если числа не равны, выполнять тело цикла.}
while a<>b do
{Если число А больше, чем В, то уменьшить его значение на В,}
if a>b then
a:=a-b
{иначе уменьшить значение числа В на А.}
else
b:=b-a;
writeln ( ’НОД= ’,A);
end.
(рис 3.29) Алгоритм вычисления факториала
Входные данные: N — целое число, факториал которого необходимо вычислить.
Выходные данные: factorial — значение факториала числа N, произведение чисел от 1 до N, целое число.
Промежуточные переменные: i — параметр цикла, целочисленная переменная, последовательно принимающая значения 2, 3, 4 и так далее до N.
Блок-схема приведена на рис. 3.29.
Итак, вводится число N. Переменной factorial, предназначенной для хранения значения произведения последовательности чисел, присваивается начальное значение, равное единице. Затем организуется цикл, параметром которого выступает переменная i. Если значение параметра цикла меньше или равно N, то выполняется оператор тела цикла, в котором из участка памяти с именем factorial считывается предыдущее значение произведения, умножается на текущее значение параметра цикла, а результат снова помещается в участок памяти с именем factorial. Когда параметр i становится больше N, цикл заканчивается, и на печать выводится значение переменой factorial, которая была вычислена в теле цикла.
Ниже представлен текст программы вычисления факториала на языке Free Pascal.
var factorial, n, i : integer; begin write ( ’ n= ’ ); readln ( n ); factorial : = 1; for i :=2 to n do factorial := factorial * i; writeln ( factorial ); end.
Входные данные: a — вещественное число, которое необходимо возвести в целую положительную степень n. Выходные данные: p (вещественное число) — результат возведения вещественного числа a в целую положительную степень n. Промежуточные данные: i — целочисленная переменная, принимающая значения от 1 до n с шагом 1, параметр цикла. Блок-схема приведена на рис. 3.30.
(рис 3.30) Алгоритм возведения вещественного числа в целую степень
Известно, что для того, чтобы получить целую степень n числа a, нужно умножить его само на себя n раз. Результат этого умножения будет храниться в участке памяти с именем p. При выполнении очередного цикла из этого участка предыдущее значение будет считываться, умножаться на основание степени a и снова записываться в участок памяти p. Цикл выполняется n раз.
В таблице 3.5 отображён протокол выполнения алгоритма при возведении числа 2 в пятую степень: a=2, n=5. Подобные таблицы, заполненные вручную, используются для тестирования — проверки всех этапов работы программы.
| i | 1 | 2 | 3 | 4 | 5 | |
| P | 1 | 2 | 4 | 8 | 16 | 32 |
Далее приведён текст программы, составленной для решения поставленной задачи.
var a, p : real; i, n : word; begin write ( ’Введите основание степени a= ’ ); readln ( a ); write ( ’Введите показатель степени n= ’ ); readln ( n ); p : = 1; for i :=1 to n do p:=p * a; writeln ( ’P= ’,P : 1 : 3 ); end.
Входные данные: N — целое число.
Выходные данные: S — сумма четных чисел.
Промежуточные переменные: i — параметр цикла, принимает значения 2, 4, 6, 8 и так далее, также имеет целочисленное значение.
При сложении нескольких чисел необходимо накапливать результат в определённом участке памяти, каждый раз считывая из этого участка предыдущее значение суммы и прибавляя к нему следующее слагаемое. Для выполнения первого оператора накапливания суммы из участка памяти необходимо взять такое число, которое не влияло бы на результат сложения. Перед началом цикла переменной, предназначенной для накапливания суммы, необходимо присвоить значение нуль (s=0). Блок-схема решения этой задачи представлена на рис. 3.31.
Так как параметр цикла i изменяется с шагом 2, в блок-схеме, построенной для решения данной задачи (рис. 3.31), использован цикл с предусловием, который реализуется при составлении программы с помощью оператора while..do:
(рис 3.31) Алгоритм вычисления суммы чётных натуральных чисел
var n, i, S : word; begin write ( ’ n= ’ ); readln ( n ); S : = 0; i : = 2; while i<=n do begin S:=S+i; i := i +2; end; writeln ( ’ S= ’, S ); end.
Эту же задачу можно решить иначе, используя цикл for..do:
var n, i, S : word;
begin
write ( ’ n= ’ );
readln ( n );
S : = 0;
for i :=1 to n do
{Если остаток от деления параметра цикла на 2 равен 0,}
{то это число четное, следовательно,}
{происходит накапливание суммы.}
if i mod 2 = 0 then
S:=S+i;
writeln ( ’ S= ’, S );
end.
В таблице 3.6 приведены результаты тестирования программы для n=7. Несложно заметить, что при нечётных значениях параметра цикла значение переменной, предназначенной для накапливания суммы, не изменяется.
i |
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
S |
0 | 0 | 2 | 2 | 6 | 6 | 12 | 12 |
Входные данные: N — целое число.
Выходные данные: целое число K — количество делителей N.
Промежуточные переменные: i — параметр цикла, возможные делители числа N.
В блок-схеме, изображённой на рис. 3.32, реализован следующий алгоритм: в переменную K, предназначенную для подсчёта количества делителей заданного числа, помещается значение, которое не влияло бы на результат (k=0). Далее организовывается цикл, в котором изменяющийся параметр i выполняет роль возможных делителей числа N. Параметр цикла меняется от 1 до N/2 c шагом 1. Если заданное число делится нацело на параметр цикла, это означает, что i является делителем N, и значение переменной K следует увеличить на единицу. Цикл необходимо повторить N/2 раз.
В таблице 3.7 отображены результаты тестирования алгоритма при определении делителей числа N=12.
i |
1 | 2 | 3 | 4 | 5 | 6 | |
K |
0 | 1 | 2 | 3 | 4 | 4 | 5 |
(рис 3.32) Алгоритм подсчёта делителей натурального числа
Текст программы, соответствующий описанному алгоритму:
var N, i,K: word;
begin
write ( ’N= ’ );
readln (N);
K: = 0;
for i :=1 to N div 2 do
{Если N делится нацело на i, то}
if N mod i= 0 then
k:=K+1; {увеличить счетчик на единицу.}
writeln ( ’ K= ’,K);
end.
ЗАДАЧА 3.14. Дано натуральное число $$N$$. Определить, является ли оно простым. Натуральное число $$N$$ называется простым, если оно делится нацело без остатка только на единицу и $$N$$. Число 13 — простое, так как делится только на 1 и 13, N = 12 не является простым, так как делится на 1, 2, 3, 4, 6 и 12.
Входные данные: N — целое число.
Выходные данные: сообщение.
Промежуточные данные: i — параметр цикла, возможные делители числа N.
Алгоритм решения этой задачи (рис. 3.33) заключается в том, что необходимо определить, есть ли у числа N делители среди чисел от 2 до N/2. Если делителей нет — число простое. Предположим, что число N является простым (Pr:=true). Организуем цикл, в котором переменная i будет изменяться от 2 до N/2. В цикле будем проверять, делится ли N на i. Если делится, то мы нашли делитель, N не является простым (Pr:=false). Проверка остальных делителей не имеет смысла, аварийно покидаем цикл.
(рис 3.33) Алгоритм определения простого числа
В алгоритме предусмотрено два выхода из цикла. Первый — естественный, при исчерпании всех значений параметра, а второй — досрочный. После выхода из цикла надо проверить значение Pr. Если Pr=true, то число N — простое, иначе N не является простым числом.
При составлении программы на языке Free Pascal досрочный выход из цикла удобно выполнять при помощи оператора break:
var
N, i : integer;
Pr : boolean;
begin
write ( ’N= ’ );
readln (N);
Pr:= true; {Предположим, что число простое.}
for i :=2 to N div 2 do
{Если найдется хотя бы один делитель, то}
if N mod i = 0 then
begin
Pr:= false; {число простым не является и}
break; {досрочный выход из цикла.}
end;
{Проверка значения логического параметра, и}
if Pr then
{вывод на печать соответствующего сообщения.}
writeln ( ’Число ’,N, ’ - простое ’ )
else
writeln ( ’Число ’,N, ’ простым не является ’ );
end.
Алгоритм решения данной задачи представлен на рис. 3.34.
Обратите внимание, что здесь осуществляется проверка корректности ввода исходных данных. Если границы интервала не положительны, или значение N превышает M, ввод данных повторяется в цикле с постусловием до тех пор, пока не будут введены корректные исходные данные. Далее для каждого числа из указанного интервала (параметр i принимает значения от N до M) происходит проверка. Если число является простым, то переменная k увеличивается на единицу. Подробно определение простого числа описано в задаче 3.14.
Программа на языке Free Pascal, реализующая алгоритм подсчёта количества простых чисел в заданном диапазоне:
var N,M, i, j, k : longint;
Pr : boolean;
begin
repeat
write ( ’N= ’ );
readln (N);
write ( ’M= ’ );
readln (M);
until (N>0) and (M>0) and (N<M);
k : = 0; {Количество простых чисел.}
for i :=N to M do {Параметр i принимает значения от N до M.}
begin
{Определение простого числа.}
Pr:= true;
for j :=2 to i div 2 do
if i mod j = 0 then
begin
Pr:= false;
break;
end;
{Если число простое, увеличиваем количество на 1.}
if Pr then
k:=k+1;
end;
if k=0 then writeln ( ’Простых чисел в диапазоне нет ’ )
else writeln ( ’Простых чисел в диапазоне ’, k );
end.
(рис 3.34) Алгоритм определения простых чисел в заданном интервале
Входные данные: N — целое число.
Выходные данные: kol — количество цифр в числе.
Промежуточные данные: M — переменная для временного хранения значения N.
Для того чтобы подсчитать количество цифр в числе, необходимо определить, сколько раз заданное число можно разделить на десять нацело. Например, пусть N=12345, тогда количество цифр kol = 5. Результаты вычислений сведены в таблицу 3.8. Алгоритм определения количества цифр в числе представлен на рис. 3.35.
| kol | N |
|---|---|
| 1 | 12345 |
| 2 | 12345 div 10=1234 |
| 3 | 1234 div 10=123 |
| 4 | 123 div 10=12 |
| 5 | 12 div 10=1 |
| 1 div 10=0 |
Текст программы, реализующей данную задачу, можно записать так:
var
M,N: longint;
kol : word;
begin
{Так как речь идёт о натуральных числах,}
{при вводе предусмотрена проверка.}
{Закончить цикл, если введено положительное число,}
{иначе повторить ввод}
repeat
write ( ’N= ’ );
readln (N);
until N>0;
M:=N; {Сохранить значение переменной N. }
kol : = 1; {Пусть число состоит из одной цифры.}
while M div 10 > 0 do
{Выполнять тело цикла, пока число делится нацело на 10.}
begin
kol := kol +1; {Счётчик количества цифр.}
M:=M div 1 0; {Изменение числа.}
end;
writeln ( ’ kol= ’, kol );
end.
(рис 3.35) Алгоритм определения количества цифр в числе
Входные данные: N — целое число.
Выходные данные: pos — позиция цифры в числе.
Промежуточные данные: i — параметр цикла, M — переменная для временного хранения значения N.
В связи с тем, что разряды в числе выделяются, начиная с последнего, то для определения номера разряда в числе, необходимо знать количество цифр в
Блок-схема алгоритма решения данной задачи показана на рис. 3.36.
(рис 3.36) Алгоритм решения задачи 3.17
| i | Число М | Цифра | Номер позиции |
|---|---|---|---|
| 1 | 120405 div 10=12040 |
120405 mod 10=5 |
6 |
| 2 | 12040 div 10=1204 |
12040 mod 10=0 |
5 |
| 3 | 1204 div 10=120 |
1204 mod 10=4 |
4 |
| 4 | 120 div 10=12 |
120 mod 10=0 |
3 |
| 5 | 12 div 10=1 |
12 mod 10=2 |
2 |
| 6 | 1 div 10=0 |
1 mod 10=1 |
1 |
Текст программы, реализующей данный алгоритм:
var
M,N: longint;
i, pos, kol : word;
begin
{Так как речь идет о натуральных числах,}
{при вводе предусмотрена проверка.}
{Закончить цикл, если введено положительное число,}
{иначе повторить ввод}
repeat
write ( ’N= ’ );
readln (N);
until N>0;
//Определение kol - количества разрядов.
M:=N; {Сохранить значение переменной N.}
kol : = 1; {Пусть число состоит из одной цифры.}
while M div 10 > 0 do
{Выполнять тело цикла, пока число делится нацело на 10.}
begin
kol := kol +1; {Счётчик количества цифр.}
M:=M div 1 0; {Изменение числа.}
end;
writeln ( ’ kol= ’, kol );
M:=N;
pos : = 0; {Пусть в числе нет нулей.}
for i :=1 to kol do
begin
{Выделение цифры из числа и сравнение её с нулем.}
if (M mod 10 = 0) then
begin
pos := kol-i +1; {Позиция нуля в числе.}
writeln ( ’Ноль в ’, pos, ’м- разряде. ’ );
end;
M:=M div 1 0; {Изменение числа.}
end;
if pos=0 then writeln ( ’Число не содержит цифру 0. ’ );
end.
Окно ввода — это стандартное диалоговое окно, которое появляется на экране в результате вызова функции InputBox. В общем виде оператор ввода данных с использованием этой функции записывают так:
имя:= InputBox (заголовок_окна,подсказка,значение );
где
заголовок_окна — строка, определяющая название окна;подсказка — текст поясняющего сообщения;значение — строка, которая будет находиться в поле ввода при появлении окна на экране;имя — переменная строкового типа, которой будет присвоено значение из поля ввода.После выполнения фрагмента программы
var S : string; begin S:= InputBox ( ’ЗАГОЛОВОК ОКНА ’, ’Подсказка : введите исходные данные ’, ’Данное значение ’ ); end;
появится окно, представленное на рис. 3.37.
(рис 3.37) Окно ввода
У пользователя есть возможность изменять текст в поле ввода. Щелчок по кнопке ОК приведёт к тому, что в переменную, указанную слева от оператора присваивания, будет занесена строка, находящаяся в поле ввода. В данном случае в переменную S будет записана строка ’Данное значение’. Щелчок по кнопке Cancel закроет окно ввода.
Учитывая, что функция InputBox возвращает строковое значение, при вводе числовых данных применяют функции преобразования типов:
var S : string; gradus, radian : real; begin S:= InputBox ( ’Ввод данных ’, ’Введите величину угла в радианах ’, ’ 0,000 ’ ); gradus := StrToFloat ( S ); radian := gradus * pi / 1 8 0; MessageDlg ( ’Величина угла в градусах ’ +FloatToStr ( radian ), MtInformation, [ mbOk ], 0 ); end;
Можно применять диалоговое окно при решении задач, обрабатывающих некоторые числовые последовательности. Рассмотрим несколько таких задач.
Входные данные: N — целое число; Х — вещественное число, определяет текущий элемент последовательности.
Выходные данные: Max — вещественное число, элемент последовательности с наибольшим значением.
Промежуточные переменные: i — параметр цикла, номер вводимого элемента последовательности.
Алгоритм поиска наибольшего элемента в последовательности следующий (рис. 3.38).
В памяти компьютера отводится ячейка, например, с именем Max, в которой будет храниться наибольший элемент последовательности — максимум. Вводим количество элементов последовательности и первый элемент последовательности. Предполагаем, что первый элемент последовательности наибольший, и записываем его в Max. Затем вводится второй элемент последовательности и сравнивается с предполагаемым максимумом. Если окажется, что второй элемент больше, его записываем в ячейку Max. В противном случае никаких действий не предпринимаем. Потом переходим к вводу следующего элемента последовательности, и алгоритм повторяется с начала. В результате, в ячейке Max будет храниться элемент последовательности с наибольшим
Разместим на форме объект типа надпись Label1 и кнопку Button1 (см. рисунок 3.39). Щелчок по кнопке приведёт к выполнению следующей процедуры:
(рис 3.38) Алгоритм поиска наибольшего числа в последовательности
procedure TForm1. Button1Click ( Sender : TObject ); var i,N: integer; max,X: real; S : string; begin //Ввод количества элементов последовательности. S:= InputBox ( ’Ввод ’, ’Введите_количество элементов в последовательности. ’, ’ 0 ’ ); N:= StrToFloat ( S ); //Ввод первого элемента последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’Введите число. ’, ’ 0 ’ ); X:= StrToFloat ( S ); //Предположим, что первый элемент максимальный — Max=X. max:=X; //Параметр цикла принимает стартовое значение i=2, //т.к. первый элемент уже введён. for i :=2 to N do begin //Ввод следующих элементов последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’Введите число. ’, ’ 0 ’ ); X:= StrToInt ( S ); //Если найдётся элемент, превышающий максимум, //записать его в ячейку Max — //теперь он предполагаемый максимум. if X>max then max:=X; end; //Вывод наибольшего элемента последовательности. MessageDlg ( ’Значение наибольшего элемента - ’ +FloatToStr (max ), MtInformation, [ mbOk ], 0 ); end;
Результаты работы программы представлены на рис. 3.39,3.40,3.41,3.42.
(рис 3.39) Первое окно диалога к задаче 3.18
(рис 3.40) Второе окно диалога к задаче 3.18
(рис 3.41) Третье окно диалога к задаче 3.18
(рис 3.42) Результат работы программы к задаче 3.18
Блок-схема решения задачи приведена на рис. 3.43.
(рис 3.43) Алгоритм поиска минимального положительного числа в последовательности
Далее приведён текст подпрограммы с подробными
procedure TForm1. Button1Click ( Sender : TObject ); var N, k, min : integer; S : string; begin //Ввод первого элемента последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’введите_число. 0 - конец последовательности ’, ’ 0 ’ ); N:= StrToInt ( S ); //Предположим, что положительных чисел нет — K=0. В переменной //K будет храниться количество минимумов среди положительных //чисел. k : = 0; //Пока введённое число не равно нулю, выполнять тело цикла. while N<>0 do begin //Проверяем, является ли введённое число положительным. if N>0 then begin //Если N>0 и K=0, поступил 1-й положительный элемент, //предположим, что он минимальный Min=N, соответственно, //количество минимумов равно 1. if k=0 then begin k : = 1; min:=N; end //Если элемент не первый, сравниваем его //с предполагаемым минимумом; //если элемент меньше, записываем его //в Min и обнуляем счетчик. else if N<min then begin min:=N; k : = 1; end //Если элемент равен минимуму, //увеличиваем количество минимумов на 1. else if N=min then k:=k+1; end; //Ввод следующего элемента последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’введите число. 0 - конец последовательности ’, ’ 0 ’ ); N:= StrToInt ( S ); end; //Конец цикла. if k<>0 then //Если значение счётчика не равно нулю, //выводим значение минимального элемента и //количество таких элементов, MessageDlg ( ’MIN = ’+IntToStr ( min)+ ’ K= ’+IntToStr ( k ), MtInformation, [ mbOk], 0 ) else //в противном случае сообщаем, //что положительных чисел нет. MessageDlg ( ’Положительных чисел нет ’, MtInformation, [ mbOk ], 0 ); end;
Чтобы решить задачу, нужно попарно перемножать элементы последовательности. Если результат произведения пары чисел — отрицательное число, значит, эти числа имеют разные знаки.
Пусть $$k$$ — количество смен знака последовательности — равно 0.
Пусть в переменной А хранится текущий элемент последовательности, а в переменой В — предыдущий. Введём N — количество элементов последовательности. Организуем цикл (переменная i меняется от 1 до N). В цикле будем делать следующее: вводим очередной элемент последовательности (A); если это это первый элемент последовательности (i=1), то сравнивать его не с чем, и просто переписываем переменную A в переменную B (B:=A). Если это не первый последовательности ($$i \not = 1$$), то проверяем знак произведения $$A \cdot B$$ (текущего и предыдущего элемента последовательности). Если произведение отрицательно, то счётчик k увеличиваем на 1. После чего не забываем в переменную B записать A. Блок-схема алгоритма приведена на рис. 3.44.
Разметим на форме два объекта типа надпись Label1 и Label2, объект поле ввода Edit1 и кнопку Button1 (рис. 3.45).
Текст подпрограммы, которая будет выполнена при обращении к кнопке, приведён далее.
(рис 3.44) Алгоритм к задаче 3.20
(рис 3.45) Форма программы решения задачи 3.20
procedure TForm1. Button1Click ( Sender : TObject ); var N, A, B, i, k : integer; S : string; begin N:= StrToInt ( Edit1. Text ); S:= InputBox ( ’Ввод элементов последовательности ’, ’Введите число. 0 - конец_последовательности ’, ’ 0 ’ ); A:= StrToInt ( S ); k : = 0; for i :=1 to N do begin S:= InputBox ( ’Ввод элементов последовательности ’, ’введите число. 0 - конец последовательности ’, ’ 0 ’ ); A:= StrToInt ( S ); if ( i <>1) then if A*B<0 then k:=k+1; B:=A; end; MessageDlg ( ’K = ’+IntToStr ( k ), MtInformation, [ mbOk ], 0 ); end;
Результаты работы программы представлены на рис. 3.46, 3.47.
(рис 3.46) Второе окно диалога к задаче 3.20
(рис 3.47) Результат работы программы к задаче 3.20
Как правило, созданию программы предшествует разработка
(рис 3.1) Блок начала (конца) алгоритма
(рис 3.2) Блок ввода (вывода)
(рис 3.3) Арифметический блок
(рис 3.4) Условный блок
Рассмотренные блоки позволяют описать три основные конструкции алгоритма : линейный процесс, разветвляющийся процесс и циклический процесс.
Линейный процесс — это конструкция, представляющая собой последовательное выполнение двух или более блоков (рис. 3.5).
Разветвляющийся процесс задаёт выполнение одного или другого оператора в зависимости от выполнения условия (рис. 3.6).
Циклический процесс задаёт многократное выполнение оператора или группы операторов (рис. 3.7).
(рис 3.5) Линейный процесс
(рис 3.6) Разветвляющийся процесс
(рис 3.7) Циклический процесс
Нетрудно заметить, что каждая из основных конструкций алгоритма имеет один вход и один выход. Это позволяет вкладывать конструкции друг в друга произвольным образом и составлять алгоритмы для решения задач любой сложности.
Рассмотрим операторы языка программирования Free Pascal, реализующие основные конструкции алгоритма.
Оператор присваивания в языке Free Pascal состоит из двух символов: двоеточия и знака равенства. Символы := всегда пишут слитно. Пробелы допускаются перед символом двоеточия и после символа равенства.
В общем случае оператор присваивания имеет вид:
имя_переменной := значение;
где значение — это выражение, переменная, константа или функция. Выполняется оператор так. Сначала вычисляется значение выражения, указанного в правой части оператора, а затем его результат записывается в область памяти (переменную), имя которой указано слева. Например, запись a:=b означает, что переменной а присваивается значение выражения b.
Типы переменной a и выражения b должны совпадать или быть совместимыми для присваивания, то есть тип, к которому принадлежит b, должен находиться в границах типа переменной а.
Оператор присваивания, как и любой другой оператор в языке Free Pascal, отделяется от других точкой с запятой.
Составной оператор — группа операторов, отделённых друг от друга точкой с запятой, начинающихся со служебного слова begin и заканчивающихся служебным словом end:
begin оператор_1; ... оператор_n end;
Транслятор воспринимает составной оператор как один оператор.
В языке Free Pascal одна из основных конструкций алгоритма, разветвляющийся процесс, реализована двумя условными операторами: if и case. Рассмотрим каждый из них.
При решении большинства задач порядок вычислений зависит от определённых условий, например от исходных данных или от промежуточных результатов, полученных на предыдущих шагах программы. Для организации вычислений в зависимости от какого-либо условия в языке Free Pascal используется условный оператор if..then..else, который в общем виде записывается так:
(рис 3.8) Алгоритм условного оператора if..then..else
if условие then оператор_1 else оператор_2;
где if..then..else — зарезервированные слова, условие — выражение логического
оператор_1 и оператор_2 — любые операторы языка Free Pascal.
Работа условного оператора организована следующим образом. Сначала вычисляется выражение, записанное в условии. Если оно имеет значение истина (True), то выполняется оператор_1. В противном случае, когда выражение имеет значение ложь (False), оператор_1 игнорируется и управление передаётся оператору_2.
Алгоритм, который реализован в условном операторе if..then..else, представлен на рис. 3.8.
Например, чтобы сравнить значения переменных $$x$$ и $$y$$, нужно создать следующий программный код:
write ( ’ x= ’ ); readln ( x ); write ( ’ y= ’ ); readln ( y ); if x=y then writeln ( ’значение x равно значению y ’ ) else writeln ( ’значение x не равно значению y ’ );
Если в задаче требуется, чтобы в зависимости от значения условия выполнялся не один оператор, а несколько, необходимо использовать составной оператор:
if условие then begin оператор_1; оператор_2; ... оператор_n; end else begin оператор_1; оператор_2; ... оператор_n; end;
Альтернативная ветвь else в условном операторе может отсутствовать, если в ней нет необходимости:
if условие then оператор; или if условие then begin оператор_1; оператор_2; ... оператор_n; end;
В таком "усечённом" виде условный оператор работает так: оператор (группа операторов) либо выполняется, либо пропускается в зависимости от значения выражения, представляющего условие. Алгоритм этого условного процесса представлен на рис. 3.9.
Пример применения условного оператора, без альтернативной ветви else может быть таким:
write ( ’ x= ’ ); readln ( x );
write ( ’ y= ’ ); readln ( y );
c : = 0;
{Значение переменной c изменяется только при условии,}
{что x не равно y.}
if ( x<>y ) then c :=x+y;
{Вывод на экран значения переменной c}
{выполняется в любом случае.}
writeln ( ’Значение переменной с= ’, c );
Условные операторы могут быть вложены друг в друга. При вложениях условных операторов всегда действует правило: альтернатива else считается принадлежащей ближайшему if, имеющему ветвь else. Например, в записи он принадлежит оператору if с условием_1.
(рис 3.9) Алгоритм условного оператора if без альтернативной ветви else
if условие_1 then if условие_2 then оператор_А else оператор_Б;
оператор_Б относится к условию_2, а в конструкции
if условие_1 then begin if условие_2 then оператор_А; end else оператор_Б;
Для сравнения переменных в условных выражениях применяют операции отношения : =, <>, <, >, <=, >=. Условные выражения составляют с использованием логических операций and, or и not. В языке Free Pascal приоритет операций отношения меньше, чем у логических операций, поэтому составные части сложного логического выражения заключают в скобки.
Допустим, нужно проверить, принадлежит ли переменная $$x$$ интервалу $$[a, b]$$. Условный оператор будет иметь вид:
if ( x>=a ) and ( x<=b ) then...
Запись
if x>=a and x<=b then...
неверна, так как фактически будет вычисляться значение выражения
x>=(a and x)<=b.
(рис 3.10) Графическое представление задачи 3.1
Рассмотрим использование оператора if на
Аналитически функцию, представленную на рис. 3.10, можно записать так:
$$y(x) = \begin{cases} 4, x \le -2, \\ x^2, -2 < x < 1, \\ 1, x \ge 1. \end{cases}$$Составим словесный алгоритм решения этой задачи:
Блок-схема, соответствующая описанному алгоритму, представлена на рисунке 3.11. Как видно, блок-схема нагляднее и проще для восприятия, чем словесное описание алгоритма. В дальнейшем для описания алгоритма мы часто будем использовать именно блок-схемы.
(рис 3.11) Блок-схема алгоритма решения задачи 3.1
Текст программы на языке Free Pascal будет иметь вид:
var x, y : real; begin write ( ’ x= ’ ); readln ( x ); if x<= -2 then y:=4 else if x>=1 then y:=1 else y:= sqr ( x ); writeln ( ’ x= ’, x : 5 : 2, ’ y= ’, y : 5 : 2 ); end.
Эту программу можно ввести в текстовом редакторе Geany (или в текстовом редакторе Free Pascal) и запустить на выполнение.
Как показано на рис. 3.12, область ограничена линиями $$x = -1, x = 3, y = -2$$ и $$y = 4$$. Значит, точка с координатами $$(x; y)$$ будет принадлежать этой области, если будут выполняться следующие условия: $$x \ge -1, x \le 3, y \ge -2$$ и $$y \le 4$$. Иначе точка лежит за пределами области.
Блок-схема, описывающая алгоритм решения данной задачи, представлена на рис. 3.13.
(рис 3.12) Графическое представление задачи 3.2
(рис 3.13) Алгоритм решения задачи 3.2
Текст программы к задаче 3.2:
var x, y : real; begin write ( ’ x= ’ ); readln ( x ); write ( ’ y= ’ ); readln ( y ); if ( x>= -1) and ( x<=3) and ( y>= -2) and ( y<=4) then writeln ( ’Точка принадлежит области ’ ) else writeln ( ’Точка не принадлежит области ’ ); end.
Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.
(рис 3.14) Алгоритм решения квадратного уравнения
Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — корни квадратного уравнения — либо сообщение о том, что корней нет.
Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.
Составим словесный алгоритм решения этой задачи.
"Действительных корней нет" и переход к п. 8.Блок-схема, соответствующая этому описанию, представлена на рис. 3.14.
Текст программы, которая реализует решение квадратного уравнения:
{Описание переменных.}
var a, b, c, d, x1, x2 : real;
begin
{Ввод значения коэффициентов квадратного уравнения.}
writeln ( ’Введите коэффициенты квадратного уравнения ’ );
readln ( a, b, c );
{Вычисление дискриминанта.}
d:=b * b-4*a*c;
{Если дискриминант отрицателен,}
if d<0 then
{то вывод сообщения, что корней нет,}
writeln ( ’Действительных корней нет ’ )
else
begin
{иначе вычисление корней x1, x2}
x1:=(-b+sqrt ( d ) ) / 2 / a;
x2:=(-b_sqrt ( d ) ) / ( 2 * a );
{и вывод их на экран.}
writeln ( ’X1= ’, x1 : 6 : 3, ’ -X2= ’, x2 : 6 : 3 )
end
end.
Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.
Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — действительные корни квадратного уравнения — либо $$x_1$$ и $$x_2$$ — действительная и мнимая части комплексного числа.
Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.
Можно выделить следующие этапы решения задачи:
и вывод их на экран. При отрицательном дискриминанте выводится сообщение о том, что действительных корней нет, и вычисляются комплексные
(рис 3.15) Алгоритм решения задачи 3.4
$$\frac{-b}{2a}+i\frac{\sqrt{|d|}}{2a},
\frac{-b}{2a}-i\frac{\sqrt{|d|}}{2a}.$$
У обоих комплексных корней действительные части одинаковые, а мнимые отличаются знаком. Поэтому можно в переменной x1 хранить действительную часть числа $$-b/(2a)$$, в переменной x2 — модуль мнимой части $$\sqrt{|d|}/(2a)$$, а в качестве корней вывести x1+ix2 и x1-ix2.
На рис. 3.15 изображена блок-схема решения задачи. Блок 1 предназначен для ввода коэффициентов квадратного уравнения. В блоке 2 осуществляется вычисление дискриминанта. Блок 3 осуществляет проверку знака дискриминанта; если дискриминант отрицателен, то корни комплексные, их расчёт происходит в блоке 4 (действительная часть корня записывается в переменную x1, модуль мнимой — в переменную x2), а вывод — в блоке 5 (первый корень x1+ix2, второй — x1-ix2). Если дискриминант положителен, то вычисляются действительные корни уравнения (блоки 6—7) и выводятся на экран (блок 8).
Текст программы, реализующей поставленную задачу:
var a, b, c, d, x1, x2 : real;
begin
writeln ( ’Введите_коэффициенты_квадратного_уравнения ’ );
readln ( a, b, c );
d:=b * b-4*a*c;
if d<0 then
begin
//Если дискриминант отрицателен, то вывод сообщения,
//что действительных корней нет, и вычисление комплексных корней.
writeln ( ’Действительных корней нет ’ );
{Вычисление действительной части комплексных корней.}
x1:=-b /(2 * a );
{Вычисление модуля мнимой части комплексных корней.}
x2:= sqrt ( abs ( d ) ) / ( 2 * a );
writeln ( ’Комплексные корни уравнения ’,
a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
{Вывод значений комплексных корней в виде $$x1\pm ix2$$}
writeln ( x1 : 1 : 2, ’+i * ( ’, x2 : 1 : 2, ’ ) ’ );
writeln ( x1 : 1 : 2, ’- i * ( ’, x2 : 1 : 2, ’ ) ’ );
end
else
begin
{иначе вычисление действительных корней x1, x2}
x1:=(-b+sqrt ( d ) ) / 2 / a;
x2:=(-b_sqrt ( d ) ) / ( 2 * a );
{и вывод их на экран.}
writeln ( ’Действительные корни уравнения ’,
a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
writeln ( ’X1= ’, x1 : 1 : 2, ’ X2= ’, x2 : 1 : 2 )
end
end.
Кубическое уравнение имеет вид
$$ax^3 + bx^2 + cx + d = 0$$После деления на $$a$$ уравнение (3.1) принимает канонический вид:
$$x^3 + rx^2 + sx + t = 0$$где $$r = b/a, s = c/a, t = d/a$$. В уравнении (3.2) сделаем замену $$x = y - r/3$$ и получим приведённое уравнение:
$$y^3 + py + q = 0,$$где
$$p = \frac{3s-r^2}{3}, q = \frac{2r^{3}}{27} - \frac{rs}{3}+t.$$Число действительных корней приведённого уравнения (3.3) зависит от знака дискриминанта $$D = (p/3)^3+ (q/2)^2$$ приведённого кубического уравнения (табл. 3.1).
| Дискриминант | Количество действительных корней | Количество комплексных корней |
|---|---|---|
| D > 0 | 1 | 2 |
| D < 0 | 3 | — |
Корни приведённого уравнения могут быть рассчитаны по формулам Кардано:
$$\begin{aligned} y_{1} = u+v\\ y_{2} = \frac{-{u+v}}{2}+\frac{u-v}{2}i\sqrt{3}\\ y_{3} = \frac{-{u+v}}{2}-\frac{u-v}{2}i\sqrt{3} \end{aligned}$$где
$$u=\sqrt[{3}]{-q/2+\sqrt{D}}, v=\sqrt[{3}]{-q/2-\sqrt{D}}.$$При отрицательном дискриминанте уравнение (3.1) имеет три действительных корня, но они будут вычисляться через вспомогательные комплексные величины. Чтобы избавиться от этого, можно воспользоваться формулами:
$$\begin{aligned} y_1=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}\right),\\ y_2=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{2\pi}{3}\right),\\ y_3=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{4\pi}{3}\right), \end{aligned}$$где
$$\rho =\sqrt{\frac{-p^3}{27}}, \cos(\varphi)=\frac{-q}{2\rho}.$$Таким образом, при положительном дискриминанте кубического уравнения (3.3) расчёт корней будем вести по формулам (3.4), а при отрицательном — по формулам (3.5).
После расчёта корней приведённого уравнения (3.3) по формулам (3.4) или (3.5) необходимо по формулам
$$x_{k}=y_{k}-\frac{r}{3}, k=1,2,3\ldots$$перейти к корням заданного кубического уравнения (3.1).
Блок-схема решения кубического уравнения представлена на рис. 3.16.
(рис 3.16) Алгоритм решения кубического уравнения
Описание блок-схемы. В блоке 1 вводятся коэффициенты кубического уравнения, в блоках 2—3 рассчитываются коэффициенты канонического и приведённого уравнений. Блок 4 предназначен для вычисления дискриминанта. В блоке 5 проверяется знак дискриминанта кубического уравнения. Если он отрицателен, то корни вычисляются по формулам (3.5) (блоки 6—7). При положительном значении дискриминанта расчёт идет по формулам (3.4) (блок 9, 10). Блоки 8 и 11 предназначены для вывода результатов на экран.
Текст программы с комментариями приведён
var a, b, c, d, r, s, t, p, q, ro, f i, x1, x2, x3, u, v, h, g : real; begin //Ввод коэффициентов кубического уравнения. write ( ’ a= ’ ); readln ( a ); write ( ’ b= ’ ); readln ( b ); write ( ’ c= ’ ); readln ( c ); write ( ’ d= ’ ); readln ( d ); //Расчёт коэффициентов канонического уравнения по 3.2. r :=b/a; s := c /a; t :=d/a; //Вычисление коэффициентов приведённого уравнения 3.3. p :=(3 * s - r * r ) / 3; q:=2* r * r * r /27- r * s/3+ t; //Вычисление дискриминанта кубического уравнения. d :=( p /3) * sqr ( p/3)+ sqr ( q / 2 ); //Проверка знака дискриминанта, //ветка then реализует формулы (3.5), //ветка else — формулы 3.4 if d<0 then begin ro := sqrt (-p * p * p / 27 ); //Следующие два оператора реализуют расчёт угла fi, //сначала вычисляется величина косинуса угла, //затем вычисляется его арккосинус через арктангенс. fi :=-q /(2 * ro ); fi := pi /2-arctan ( fi / sqrt (1 - fi * fi ) ); //Вычисление действительных корней уравнения x1, x2 и x3 x1 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3) - r / 3; x2 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+2 * pi /3) - r / 3; x3 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+4 * pi /3) - r / 3; writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, x2 : 1 : 3, ’ x3= ’, x3 : 1 : 3 ); end else begin //Вычисление u и v с проверкой знака // подкоренного выражения. if -q/2+ sqrt ( d)>0 then u:=exp (1/3 * ln (-q/2+ sqrt ( d ) ) ) else if -q/2+ sqrt ( d)<0 then u:=-exp (1/3 * ln ( abs(-q/2+ sqrt ( d ) ) ) ) else u : = 0; if -q/2- sqrt ( d)>0 then v:=exp (1/3 * ln (-q/2- sqrt ( d ) ) ) else if -q/2- sqrt ( d)<0 then v:=-exp (1/3 * ln ( abs(-q/2- sqrt ( d ) ) ) ) else v : = 0; //Вычисление действительного корня кубического уравнения. x1:=u+v-r / 3; //Вычисление действительной и // мнимой части комплексных корней. h:=-(u+v)/2 - r / 3; g :=(u - v )/2 * sqrt ( 3 ); writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, h : 1 : 3, ’+i* ’, g : 1 : 3, ’ x3= ’, h : 1 : 3, ’ -i* ’, g : 1 : 3 ); end end.
Входные данные: a, b, c. Выходные данные: x1, x2, x3, x4.
Для решения биквадратного уравнения необходимо заменой $$y = x^2$$ привести его к квадратному уравнению $$ay^2 + by + c = 0$$ и решить это уравнение.
Опишем алгоритм решения этой задачи (рис. 3.17):
(рис 3.17) Алгоритм решения биквадратного уравнения
Текст программы на языке Free Pascal с комментариями:
//Описание переменных: //a,b,c - коэффициенты биквадратного уравнения, //d - дискриминант, //x1,x2,x3,x4 - корни биквадратного уравнения, //y1,y2 - корни квадратного уравнения ay^2+by+c=0. var a, b, c, d, x1, x2, x3, x4, y1, y2 : real; begin //Ввод коэффициентов уравнения. writeln ( ’Введите коэффициенты биквадратного уравнения ’ ); readln ( a, b, c ); //Вычисление дискриминанта. d:=b * b-4*a*c; //Если он отрицателен, if d<0 then //вывод сообщения "Корней нет" writeln ( ’Корней нет ’ ) //Если дискриминант >= 0, else begin //вычисление корней квадратного уравнения. y1:=(-b+sqrt ( d ) ) / 2 / a; y2:=(-b-sqrt ( d ) ) / ( 2 * a ); //Если оба корня квадратного уравнения < 0, if ( y1 <0) and ( y2 <0) then //вывод сообщения "Корней нет". writeln ( ’Корней нет ’ ) //Если оба корня квадратного уравнения >= 0, else if ( y1>=0) and ( y2>=0) then begin //вычисление четырех корней биквадратного уравнения. x1:= sqrt ( y1 ); x2:=-x1; x3:= sqrt ( y2 ); x4:=-sqrt ( y2 ); //Вывод корней биквадратного уравнения на экран. writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 ); writeln ( ’X3= ’, x3 : 6 : 3, ’ X4= ’, x4 : 6 : 3 ); end //Если не выполнились оба условия // 1. y1<0 И y2<0 // 2. y1>=0 И y2>=0, //то проверяем условие y1>=0 else if ( y1>=0) then //Если оно истинно begin x1:= sqrt ( y1 ); x2:=-x1; writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 ); end else //Если условие y1>=0 ложно, то begin x1:= sqrt ( y2 ); x2:=-x1; writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 ); end end end.
Оператор варианта case необходим в тех случаях, когда в зависимости от значений какой-либо переменной надо выполнить те или иные операторы.
case выражение of значение_1 : оператор_1; значение_2 : оператор_2; ... значение_N : оператор_N else альтернативный_оператор end;
Здесь выражение — переменная перечислимого типа (включая char и boolean), значение_1, значение_2,..., значение_N — это конкретное значение управляющей переменной или выражение, при котором необходимо выполнить соответствующий оператор, игнорируя остальные варианты. Значения в каждом наборе должны быть уникальны, то есть они могут появляться только в одном варианте. Пересечение наборов значений для разных вариантов является ошибкой.
Оператор работает следующим образом. Вычисляется значение выражения. Затем выполняется оператор, помеченный значением, совпадающим со значением выражения. То есть, если выражение принимает значение_1, то выполняется оператор_1. Если выражение принимает значение_2, то выполняется оператор_2 и так далее. Если выражение не принимает ни одно из значений, то выполняется альтернативный_оператор, расположенный после ключевого слова else.
Альтернативная ветвь else может отсутствовать, тогда оператор имеет вид:
case выражение of значение_1 : оператор_1; значение_2 : оператор_2; ... значение_N : оператор_N; end;
Кроме того, в операторе case допустимо использование составного оператора. Например:
case выражение of значение_1 : begin оператор_A; оператор_B; end; значение_2 : begin оператор_C; оператор_D; оператор_E; end; ... значение_N : оператор_N; end;
Рассмотрим применение оператора варианта на следующих примерах.
D, при условии, что в месяце 31 день и первое число — понедельник.Для решения задачи воспользуемся операцией mod, позволяющей вычислить остаток от деления двух чисел, и условием, что первое число — понедельник. Если в результате остаток от деления (обозначим его R) заданного числа D на семь будет равен единице, то это понедельник, двойке — вторник, тройке — среда и так далее. Следовательно, при построении алгоритма необходимо использовать семь условных операторов, как показано на рис. 3.18.
Решение задачи станет значительно проще, если при написании программы воспользоваться оператором варианта:
var d : byte;
begin
write ( ’Введите число D= ’ );
readln (D);
case D mod 7 of {Вычисляется остаток от деления D на 7.}
{В зависимости от полученного значения}
{на печать выводится название дня недели}
1 : writeln ( ’ПОНЕДЕЛЬНИК ’ );
2 : writeln ( ’ВТОРНИК ’ );
3 : writeln ( ’СРЕДА ’ );
4 : writeln ( ’ЧЕТВЕРГ ’ );
5 : writeln ( ’ПЯТНИЦА ’ );
6 : writeln ( ’СУББОТА ’ );
0 : writeln ( ’ВОСКРЕСЕНЬЕ ’ );
end;
end.
(рис 3.18) Алгоритм решения задачи 3.7
В предложенной записи оператора варианта отсутствует ветвь else. Это объясняется тем, что переменная R может принимать только одно из указанных значений, т. е. 1, 2, 3, 4, 5, 6 или 0.
m вывести на печать название времени года.Для решения данной задачи необходимо проверить выполнение четырёх условий. Если заданное число m равно 12, 1 или 2, то это зима, если m попадает в диапазон от 3 до 5, то весна; лето определяется принадлежностью числа m диапазону от 6 до 8, и, соответственно, при равенстве переменной m 9, 10 или 11 это осень. Понятно, что область возможных значений переменной m находится в диапазоне от 1 до 12, и если пользователь введет число, не входящее в этот интервал, то появится сообщение об ошибке. Для этого в операторе case программы предусмотрена альтернативная ветка else.
var m: byte; begin write ( ’Введите номер месяца m= ’ ); readln (m); //Проверка области допустимых значений переменной m. case m of //В зависимости от значения m на печать //выводится название времени года. 1 2, 1, 2 : writeln ( ’ЗИМА ’ ); } 3.. 5 : writeln ( ’ВЕСНА ’ ); } 6.. 8 : writeln ( ’ЛЕТО ’ ); } 9.. 1 1 : writeln ( ’ОСЕНЬ ’ )} //Если значение переменной m выходит за пределы области //допустимых значений, то выдается сообщение об ошибке. else writeln ( ’ОШИБКА ПРИ ВВОДЕ ! ! ! ’ ); end end.
Понятно, что чем меньше в программе ошибок, тем она лучше. В очень хорошей программе ошибок нет вообще. А это значит, что программист должен не только основательно продумать алгоритм поставленной задачи, но и предугадать ошибки, которые может допустить пользователь, работая с программой.
Если пользователь допустил ошибку, например, при вводе данных, его необходимо проинформировать об этом. Для этого можно воспользоваться функцией MessageDlg, которая выводит сообщение в отдельном окне. В общем виде функцию записывают так:
MessageDlg (сообщение, тип_сообщения, [ список_кнопок ], справка );
где
сообщение — текст, который будет отображён в окне сообщения;тип_сообщения — определяет внешний вид окна (табл. 3.2);список_кнопок — константы (перечисляются через запятую), определяющие тип кнопок окна сообщения (табл. 3.3);справка — номер окна справочной системы, которое будет выведено на экран, если нажать F1; параметр равен нулю, если использование справки не предусмотрено.| Параметр | Тип окна сообщения |
|---|---|
mtInformation |
информационное |
mtWarning |
предупредительное |
mtError |
сообщение об ошибке |
mtConfirmation |
запрос на подтверждение |
mtCustom |
обычное |
| Константа | Кнопка |
|---|---|
mbYes |
Да |
mbNo |
Нет |
mbOk |
Ок |
| mbCancel | Отмена |
mbAbort |
Прервать |
mbRetry |
Повторить |
mbIgnore |
Пропустить |
mbHelp |
Помощь |
(рис 3.19) Конструирование формы для решения квадратного уравнения
Вернёмся к задаче решения квадратного уравнения (задача 3.3). Нами был рассмотрен алгоритм решения этой задачи и написана программа на языке программирования Free Pascal. Реализуем эту задачу в среде Lazarus.
Создадим новый
Для организации ввода коэффициентов уравнения внедрим на форму четыре объекта типа надпись (Label1, Label2, Label3, Label4) и три поля ввода (Edit1, Edit2, Edit3). Корни уравнения или сообщение об их отсутствии будем выводить в надпись
Label5 невидимой, для чего свойство Visible установим в false (Label5.Visible:=false).Button1.
При вводе данных в программе могут возникнуть следующие ошибки:
a равно
Для того чтобы не допустить подобные ошибки, необходимо контролировать данные, вводимые пользователем. Применим для этой цели встроенную процедуру Val(S,X,Kod), которая преобразовывает строку S в число. Тип этого числа (целое или вещественное) зависит от типа переменной X. Если преобразование прошло успешно, то параметр Kod принимает значение, равное нулю, а результат преобразования записывается в переменную X. В противном случае Kod содержит номер позиции в строке S, где произошла ошибка, и содержимое переменной X не меняется. Далее приведён фрагмент программы с подробными комментариями:
procedure TForm1. Button1Click ( Sender : TObject ); var a, b, c, d, x1, x2 : real; kod1, kod2, kod3 : integer; begin //Ввод значений коэффициентов квадратного уравнения. //Из поля ввода Edit1 считывается строка символов и //преобразовывается в вещественное число, //если преобразование прошло успешно, то kod1=0 и //полученное число записывается в переменную a. val ( Edit1. Text, a, kod1 ); val ( Edit2. Text, b, kod2 ); val ( Edit3. Text, c, kod3 ); //Если преобразования прошли успешно, то if ( kod1=0) and ( kod2=0) and ( kod3=0) then //проверить чему равен первый коэффициент. //Если значение первого коэффициента равно нулю, то if a=0 then //выдать соответствующее сообщение. MessageDlg ( ’Введите не нулевое значение а ’, mtInformation, [ mbOk], 0 ) else //иначе перейти к решению квадратного уравнения begin d:=b * b-4*a*c; Label5. Visible := true; if d<0 then Label5. Caption := ’В уравнении ’ +chr (13)+ ’нет действительных корней ’ else begin x1:=(-b+sqrt ( d ) ) / 2 / a; x2:=(-b-sqrt ( d ) ) / ( 2 * a ); Label5. Caption := ’X1= ’+FloatToStr ( x1 ) +chr (13)+ ’X2= ’+FloatToStr ( x2 ); end; end else //Преобразование не выполнено, выдать сообщение. MessageDlg ( ’Введите числовое значение ’, mtInformation, [ mbOk ], 0 ); end.
(рис 3.20) Обработка ошибки ввода данных — коэффициент a равен 0
Решение квадратного уравнения $$3x^2+4x+5=0$$
(рис 3.21) Решение квадратного уравнения(корней нет)
(рис 3.22) Обработка ошибки ввода данных — в поле ввода строка, которую невозможно преобразовать в число
(рис 3.23) Вычисление корней квадратного уравнения
Результаты работы программы показаны на рис. 3.20,рис. 3.21,рис. 3.22,3.23.
Метод обработки ошибок ввода, изложенный в этом примере, не единственный. Можно попытаться контролировать символы, поступающие в поля ввода. Набор символов, которые могут быть преобразованы в число, невелик. Это все цифры от 0 до 9, знак минус и символ запятой. Кроме того, пользователь должен иметь возможность удалять символы из поля ввода. Для этого ему понадобится клавиша BackSpace (#8 — символ с кодом восемь). Представим набор перечисленных символов в виде множества, и если окажется, что введённый символ не принадлежит ему, то будем выводить сообщение об ошибке при вводе.
Возможность контролировать ввод символов обеспечивает событие OnKeyPress. Если выделить компонент Edit2 и на вкладке инспектора объектов дважды щёлкнуть возле события нажатия клавиши OnKeyPress, то будет создана процедура обработки этого события. Текст процедуры с комментариями:
//Обработка события ввод символа в поле Edit2. procedure TForm1. Edit2KeyPress ( Sender : TObject; var Key : Char ); begin //Если символ не принадлежит //множеству допустимых символов,то if not ( Key in [#8, ’, ’, ’_ ’, ’ 0 ’.. ’ 9 ’ ] ) then begin //выдать сообщение и MessageDlg ( ’Введите числовое значение ’, mtInformation, [ mbOk ], 0 ); Abort; //прервать выполнение подпрограммы. end; end.
Циклический процесс, или просто цикл — это повторение одних и тех же действий. Последовательность действий, которые повторяются в цикле, называют телом цикла. Один проход цикла называют шагом, или
При написании циклических алгоритмов следует помнить следующее. Во-первых, чтобы цикл имел шанс когда-нибудь закончиться, содержимое его тела должно обязательно влиять на условие цикла. Во-вторых, условие должно состоять из корректных выражений и значений, определённых ещё до первого выполнения тела цикла.
В языке Free Pascal для удобства программиста предусмотрены три оператора, реализующих циклический процесс: while, repeat..until и for.
На рис. 3.24 изображена блок-схема алгоритма цикла с предусловием.
Оператор, реализующий этот алгоритм, в языке Free Pascal имеет вид:
while выражение do оператор;
(рис 3.24) Алгоритм циклической структуры с предусловием
Выражение должно быть логической константой, переменной или логическим выражением.
Работает оператор while следующим образом. Вычисляется значение выражения.
Если оно истинно (True), выполняется оператор. Выполнение цикла заканчивается, если условие ложно, после этого управление передается оператору, следующему за телом цикла. Выражение вычисляется перед каждой итерацией цикла. Если при первой проверке выражение ложно (False), цикл не выполнится ни разу.
Если в цикле надо выполнить более одного оператора, необходимо использовать составной оператор:
while условие do begin оператор_1; оператор_2; ... оператор_n; end;
Рассмотрим пример. Пусть необходимо вывести на экран значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0;\pi]$$ с шагом 0.1.
Применим цикл с предусловием :
var x, y : real;
begin
{Присваивание параметру цикла стартового значения.}
x : = 0;
{Цикл с предусловием.}
while x<=pi do {Пока параметр цикла не превышает}
{конечное значение, выполнять тело цикла.}
begin
{Вычислить значение y.}
y:=exp ( sin ( x ) ) * cos ( x );
{Вывод на экран пары х и y.}
writeln ( ’ x= ’, x, ’ y= ’, y );
{Изменение параметра цикла - переход к следующему значению x.}
x:=x + 0.1;
end; {Конец цикла.}
end.
В результате работы данного фрагмента программы на экран последовательно будут выводиться сообщения со значениями переменных x и y:
x= 0; y=1 x= 0.1; y =1.0995 ... x= 3.1; y= -1.0415
Если в цикле с предусловием проверка условия осуществляется до тела цикла, то в цикле с постусловием условие проверяется после тела цикла (см. рис. 3.25). Сначала выполняются операторы, являющиеся телом цикла, после чего проверятся условие, если последнее ложно, то цикл повторяется. Выполнение цикла прекратится, если условие станет истинным.
(рис 3.25) Алгоритм циклической структуры с постусловием
В языке Free Pascal цикл с постусловием реализован конструкцией
repeat оператор; until выражение;
или, если тело цикла состоит более чем из одного оператора
repeat оператор_1; оператор_2; ... оператор_N; until выражение;
Выражение должно быть логическим.
Работает цикл следующим образом. В начале выполняется оператор, представляющий собой тело цикла. Затем вычисляется значение выражения. Если оно ложно (False), оператор тела цикла выполняется ещё раз. В противном случае цикл завершается, и управление передается оператору, следующему за циклом.
Таким образом, нетрудно заметить, что цикл с постусловием всегда будет выполнен хотя бы один раз, в отличие от цикла с предусловием, который может не выполниться ни разу.
Если применить цикл с постусловием для создания подпрограммы, которая выводит значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1, получим:
var x, y : real;
begin
{Присваивание параметру цикла стартового значения.}
x : = 0;
{Цикл с постусловием.}
repeat {Начало цикла}
y:=exp ( sin ( x ) ) * cos ( x );
writeln ( ’ x= ’, x, ’ y= ’, y );
x:=x + 0.1; {Изменение значения параметра цикла.}
until x > pi; {Закончить работу цикла,}
{когда параметр превысит конечное значение.}
end.
Операторы цикла с условием обладают значительной гибкостью, но не слишком удобны для организации "строгих" циклов, которые должны быть выполнены заданное число раз. Оператор цикла for..do используется именно в таких случаях:
for параметр_цикла:=начальное_значение to конечное_значение do оператор; for параметр_цикла:=конечное_значение downto начальное_значение do оператор;
где оператор — любой оператор языка, параметр_цикла — имя переменной целочисленного или перечислимого типов, начальное_значение и конечное_значение должны быть того же типа, что и параметр_цикла.
Шаг изменения цикла for всегда постоянен и равен интервалу между двумя ближайшими значениями типа параметра цикла (при целочисленном значении параметра цикла шаг равен 1).
В случае если тело цикла состоит более чем из одного оператора, необходимо использовать составной оператор:
(рис 3.26) Алгоритм работы цикла for..do
(рис 3.27) Представление цикла for...do с помощью блок-схемы
for параметр_цикла:=начальное_значение to конечное_значение do begin оператор_1; оператор_2; ... оператор_N; end;
Опишем алгоритм работы цикла for..do (рис. 3.26).
Параметру_цикла присваивается начальное_значение.параметра_цикла превосходит конечное_значение, то цикл завершает свою работу. В противном случае выполняется п. 3.оператор.параметра_цикла изменяется на соответствующий шаг и осуществляется переход к п. 2, и т. д.Понятно, что этот алгоритм представляет собой цикл с предусловием.
В дальнейшем, чтобы избежать создания слишком громоздких алгоритмов, в блок-схемах цикл for будем изображать так, как показано на рис. 3.27
Фрагмент подпрограммы, приведённый далее, демонстрирует применение цикла for:
var i : integer; c : char;
begin
{Вывод на экран чисел от 1 до 10.}
for i :=1 to 10 do
writeln ( i );
{Вывод на экран чисел от 10 до -10.}
for i :=10 downto -10 do
writeln ( i );
{Вывод на экран символов от a до r.}
for c := ’ a ’ to ’ r ’ do
writeln ( c );
end.
Вернёмся к задаче вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1. Как видим, здесь количество повторений цикла явно не задано. Однако это значение, можно легко вычислить. Предположим, что параметр цикла $$x$$ принимает значения в диапазоне от $$x_n$$ до $$x_k$$, изменяясь с шагом $$dx$$, тогда количество повторений тела цикла можно определить по формуле:
$$n = \round\left(\frac{x_k-x_n}{dx}\right)+1$$округлив результат деления до целого числа. Следовательно, фрагмент программы вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1 будет иметь вид:
var i, n : integer; x, y : real;
begin
n:=round ( ( pi - 0)/0.1)+1; {Количество повторений цикла.}
x : = 0; {Начальное значение аргумента.}
{Цикл с известным числом повторений,}
{i - параметр цикла, изменяется от 1 до n с шагом 1.}
for i :=1 to n do
begin {Начало цикла.}
y:=exp ( sin ( x ) ) * cos ( x ); {Вычисление значения функции }
{для соответствующего значения аргумента.}
writeln ( ’ x= ’, x, ’ y= ’, y );
x:=x + 0.1; {Вычисление нового}
{значения аргумента.}
end; {Конец цикла.}
end.
Операторы передачи управления принудительно изменяют порядок выполнения команд. В языке Free Pascal таких операторов пять: goto, break, continue, exit и halt.
Оператор goto метка, где метка — обычный идентификатор, применяют для безусловного перехода, он передаёт управление оператору с меткой.
метка : оператор;
Обычно применение оператора goto приводит к усложнению программы и затрудняет отладку. Использование оператора нарушает принцип структурного
Операторы break и continue используют только внутри циклов. Оператор break осуществляет немедленный выход из циклов repeat, while, for, и управление передается оператору, находящемуся непосредственно за циклом. Оператор continue начинает новую итерацию цикла, даже если предыдущая не была завершена.
Оператор exit осуществляет выход из подпрограммы.
Оператор halt прекращает выполнение программы.
Рассмотрим использование циклических операторов на конкретных примерах.
Входные данные: A и B. Выходные данные: А — НОД.
Для решения поставленной задачи воспользуемся алгоритмом Евклида: будем уменьшать каждый раз большее из чисел на величину меньшего до тех пор, пока оба значения не станут равными, так, как показано в таблице 3.4.
В блок-схеме решения задачи, представленной на рис. 3.28, для решения поставленной задачи используется цикл с предусловием, то есть тело цикла повторяется до тех пор, пока А не равно В. Следовательно, при создании программы воспользуемся циклом while..do.
| Исходные данные | Первый шаг | Второй шаг | Третий шаг | НОД(A,B)=5 |
|---|---|---|---|---|
А=25 |
А=10 |
А=10 |
А=5 |
|
В=15 |
В=15 |
В=5 |
В=5 |
(рис 3.28) Алгоритм поиска наибольшего общего делителя двух чисел
Программа на языке Free Pascal, реализующая поставленную задачу:
var a, b : word;
begin
writeln ( ’введите два натуральных числа ’ );
write ( ’A= ’ ); readln ( a );
write ( ’B= ’ ); readln ( b );
{Если числа не равны, выполнять тело цикла.}
while a<>b do
{Если число А больше, чем В, то уменьшить его значение на В,}
if a>b then
a:=a-b
{иначе уменьшить значение числа В на А.}
else
b:=b-a;
writeln ( ’НОД= ’,A);
end.
(рис 3.29) Алгоритм вычисления факториала
Входные данные: N — целое число, факториал которого необходимо вычислить.
Выходные данные: factorial — значение факториала числа N, произведение чисел от 1 до N, целое число.
Промежуточные переменные: i — параметр цикла, целочисленная переменная, последовательно принимающая значения 2, 3, 4 и так далее до N.
Блок-схема приведена на рис. 3.29.
Итак, вводится число N. Переменной factorial, предназначенной для хранения значения произведения последовательности чисел, присваивается начальное значение, равное единице. Затем организуется цикл, параметром которого выступает переменная i. Если значение параметра цикла меньше или равно N, то выполняется оператор тела цикла, в котором из участка памяти с именем factorial считывается предыдущее значение произведения, умножается на текущее значение параметра цикла, а результат снова помещается в участок памяти с именем factorial. Когда параметр i становится больше N, цикл заканчивается, и на печать выводится значение переменой factorial, которая была вычислена в теле цикла.
Ниже представлен текст программы вычисления факториала на языке Free Pascal.
var factorial, n, i : integer; begin write ( ’ n= ’ ); readln ( n ); factorial : = 1; for i :=2 to n do factorial := factorial * i; writeln ( factorial ); end.
Входные данные: a — вещественное число, которое необходимо возвести в целую положительную степень n. Выходные данные: p (вещественное число) — результат возведения вещественного числа a в целую положительную степень n. Промежуточные данные: i — целочисленная переменная, принимающая значения от 1 до n с шагом 1, параметр цикла. Блок-схема приведена на рис. 3.30.
(рис 3.30) Алгоритм возведения вещественного числа в целую степень
Известно, что для того, чтобы получить целую степень n числа a, нужно умножить его само на себя n раз. Результат этого умножения будет храниться в участке памяти с именем p. При выполнении очередного цикла из этого участка предыдущее значение будет считываться, умножаться на основание степени a и снова записываться в участок памяти p. Цикл выполняется n раз.
В таблице 3.5 отображён протокол выполнения алгоритма при возведении числа 2 в пятую степень: a=2, n=5. Подобные таблицы, заполненные вручную, используются для тестирования — проверки всех этапов работы программы.
| i | 1 | 2 | 3 | 4 | 5 | |
| P | 1 | 2 | 4 | 8 | 16 | 32 |
Далее приведён текст программы, составленной для решения поставленной задачи.
var a, p : real; i, n : word; begin write ( ’Введите основание степени a= ’ ); readln ( a ); write ( ’Введите показатель степени n= ’ ); readln ( n ); p : = 1; for i :=1 to n do p:=p * a; writeln ( ’P= ’,P : 1 : 3 ); end.
Входные данные: N — целое число.
Выходные данные: S — сумма четных чисел.
Промежуточные переменные: i — параметр цикла, принимает значения 2, 4, 6, 8 и так далее, также имеет целочисленное значение.
При сложении нескольких чисел необходимо накапливать результат в определённом участке памяти, каждый раз считывая из этого участка предыдущее значение суммы и прибавляя к нему следующее слагаемое. Для выполнения первого оператора накапливания суммы из участка памяти необходимо взять такое число, которое не влияло бы на результат сложения. Перед началом цикла переменной, предназначенной для накапливания суммы, необходимо присвоить значение нуль (s=0). Блок-схема решения этой задачи представлена на рис. 3.31.
Так как параметр цикла i изменяется с шагом 2, в блок-схеме, построенной для решения данной задачи (рис. 3.31), использован цикл с предусловием, который реализуется при составлении программы с помощью оператора while..do:
(рис 3.31) Алгоритм вычисления суммы чётных натуральных чисел
var n, i, S : word; begin write ( ’ n= ’ ); readln ( n ); S : = 0; i : = 2; while i<=n do begin S:=S+i; i := i +2; end; writeln ( ’ S= ’, S ); end.
Эту же задачу можно решить иначе, используя цикл for..do:
var n, i, S : word;
begin
write ( ’ n= ’ );
readln ( n );
S : = 0;
for i :=1 to n do
{Если остаток от деления параметра цикла на 2 равен 0,}
{то это число четное, следовательно,}
{происходит накапливание суммы.}
if i mod 2 = 0 then
S:=S+i;
writeln ( ’ S= ’, S );
end.
В таблице 3.6 приведены результаты тестирования программы для n=7. Несложно заметить, что при нечётных значениях параметра цикла значение переменной, предназначенной для накапливания суммы, не изменяется.
i |
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
S |
0 | 0 | 2 | 2 | 6 | 6 | 12 | 12 |
Входные данные: N — целое число.
Выходные данные: целое число K — количество делителей N.
Промежуточные переменные: i — параметр цикла, возможные делители числа N.
В блок-схеме, изображённой на рис. 3.32, реализован следующий алгоритм: в переменную K, предназначенную для подсчёта количества делителей заданного числа, помещается значение, которое не влияло бы на результат (k=0). Далее организовывается цикл, в котором изменяющийся параметр i выполняет роль возможных делителей числа N. Параметр цикла меняется от 1 до N/2 c шагом 1. Если заданное число делится нацело на параметр цикла, это означает, что i является делителем N, и значение переменной K следует увеличить на единицу. Цикл необходимо повторить N/2 раз.
В таблице 3.7 отображены результаты тестирования алгоритма при определении делителей числа N=12.
i |
1 | 2 | 3 | 4 | 5 | 6 | |
K |
0 | 1 | 2 | 3 | 4 | 4 | 5 |
(рис 3.32) Алгоритм подсчёта делителей натурального числа
Текст программы, соответствующий описанному алгоритму:
var N, i,K: word;
begin
write ( ’N= ’ );
readln (N);
K: = 0;
for i :=1 to N div 2 do
{Если N делится нацело на i, то}
if N mod i= 0 then
k:=K+1; {увеличить счетчик на единицу.}
writeln ( ’ K= ’,K);
end.
ЗАДАЧА 3.14. Дано натуральное число $$N$$. Определить, является ли оно простым. Натуральное число $$N$$ называется простым, если оно делится нацело без остатка только на единицу и $$N$$. Число 13 — простое, так как делится только на 1 и 13, N = 12 не является простым, так как делится на 1, 2, 3, 4, 6 и 12.
Входные данные: N — целое число.
Выходные данные: сообщение.
Промежуточные данные: i — параметр цикла, возможные делители числа N.
Алгоритм решения этой задачи (рис. 3.33) заключается в том, что необходимо определить, есть ли у числа N делители среди чисел от 2 до N/2. Если делителей нет — число простое. Предположим, что число N является простым (Pr:=true). Организуем цикл, в котором переменная i будет изменяться от 2 до N/2. В цикле будем проверять, делится ли N на i. Если делится, то мы нашли делитель, N не является простым (Pr:=false). Проверка остальных делителей не имеет смысла, аварийно покидаем цикл.
(рис 3.33) Алгоритм определения простого числа
В алгоритме предусмотрено два выхода из цикла. Первый — естественный, при исчерпании всех значений параметра, а второй — досрочный. После выхода из цикла надо проверить значение Pr. Если Pr=true, то число N — простое, иначе N не является простым числом.
При составлении программы на языке Free Pascal досрочный выход из цикла удобно выполнять при помощи оператора break:
var
N, i : integer;
Pr : boolean;
begin
write ( ’N= ’ );
readln (N);
Pr:= true; {Предположим, что число простое.}
for i :=2 to N div 2 do
{Если найдется хотя бы один делитель, то}
if N mod i = 0 then
begin
Pr:= false; {число простым не является и}
break; {досрочный выход из цикла.}
end;
{Проверка значения логического параметра, и}
if Pr then
{вывод на печать соответствующего сообщения.}
writeln ( ’Число ’,N, ’ - простое ’ )
else
writeln ( ’Число ’,N, ’ простым не является ’ );
end.
Алгоритм решения данной задачи представлен на рис. 3.34.
Обратите внимание, что здесь осуществляется проверка корректности ввода исходных данных. Если границы интервала не положительны, или значение N превышает M, ввод данных повторяется в цикле с постусловием до тех пор, пока не будут введены корректные исходные данные. Далее для каждого числа из указанного интервала (параметр i принимает значения от N до M) происходит проверка. Если число является простым, то переменная k увеличивается на единицу. Подробно определение простого числа описано в задаче 3.14.
Программа на языке Free Pascal, реализующая алгоритм подсчёта количества простых чисел в заданном диапазоне:
var N,M, i, j, k : longint;
Pr : boolean;
begin
repeat
write ( ’N= ’ );
readln (N);
write ( ’M= ’ );
readln (M);
until (N>0) and (M>0) and (N<M);
k : = 0; {Количество простых чисел.}
for i :=N to M do {Параметр i принимает значения от N до M.}
begin
{Определение простого числа.}
Pr:= true;
for j :=2 to i div 2 do
if i mod j = 0 then
begin
Pr:= false;
break;
end;
{Если число простое, увеличиваем количество на 1.}
if Pr then
k:=k+1;
end;
if k=0 then writeln ( ’Простых чисел в диапазоне нет ’ )
else writeln ( ’Простых чисел в диапазоне ’, k );
end.
(рис 3.34) Алгоритм определения простых чисел в заданном интервале
Входные данные: N — целое число.
Выходные данные: kol — количество цифр в числе.
Промежуточные данные: M — переменная для временного хранения значения N.
Для того чтобы подсчитать количество цифр в числе, необходимо определить, сколько раз заданное число можно разделить на десять нацело. Например, пусть N=12345, тогда количество цифр kol = 5. Результаты вычислений сведены в таблицу 3.8. Алгоритм определения количества цифр в числе представлен на рис. 3.35.
| kol | N |
|---|---|
| 1 | 12345 |
| 2 | 12345 div 10=1234 |
| 3 | 1234 div 10=123 |
| 4 | 123 div 10=12 |
| 5 | 12 div 10=1 |
| 1 div 10=0 |
Текст программы, реализующей данную задачу, можно записать так:
var
M,N: longint;
kol : word;
begin
{Так как речь идёт о натуральных числах,}
{при вводе предусмотрена проверка.}
{Закончить цикл, если введено положительное число,}
{иначе повторить ввод}
repeat
write ( ’N= ’ );
readln (N);
until N>0;
M:=N; {Сохранить значение переменной N. }
kol : = 1; {Пусть число состоит из одной цифры.}
while M div 10 > 0 do
{Выполнять тело цикла, пока число делится нацело на 10.}
begin
kol := kol +1; {Счётчик количества цифр.}
M:=M div 1 0; {Изменение числа.}
end;
writeln ( ’ kol= ’, kol );
end.
(рис 3.35) Алгоритм определения количества цифр в числе
Входные данные: N — целое число.
Выходные данные: pos — позиция цифры в числе.
Промежуточные данные: i — параметр цикла, M — переменная для временного хранения значения N.
В связи с тем, что разряды в числе выделяются, начиная с последнего, то для определения номера разряда в числе, необходимо знать количество цифр в
Блок-схема алгоритма решения данной задачи показана на рис. 3.36.
(рис 3.36) Алгоритм решения задачи 3.17
| i | Число М | Цифра | Номер позиции |
|---|---|---|---|
| 1 | 120405 div 10=12040 |
120405 mod 10=5 |
6 |
| 2 | 12040 div 10=1204 |
12040 mod 10=0 |
5 |
| 3 | 1204 div 10=120 |
1204 mod 10=4 |
4 |
| 4 | 120 div 10=12 |
120 mod 10=0 |
3 |
| 5 | 12 div 10=1 |
12 mod 10=2 |
2 |
| 6 | 1 div 10=0 |
1 mod 10=1 |
1 |
Текст программы, реализующей данный алгоритм:
var
M,N: longint;
i, pos, kol : word;
begin
{Так как речь идет о натуральных числах,}
{при вводе предусмотрена проверка.}
{Закончить цикл, если введено положительное число,}
{иначе повторить ввод}
repeat
write ( ’N= ’ );
readln (N);
until N>0;
//Определение kol - количества разрядов.
M:=N; {Сохранить значение переменной N.}
kol : = 1; {Пусть число состоит из одной цифры.}
while M div 10 > 0 do
{Выполнять тело цикла, пока число делится нацело на 10.}
begin
kol := kol +1; {Счётчик количества цифр.}
M:=M div 1 0; {Изменение числа.}
end;
writeln ( ’ kol= ’, kol );
M:=N;
pos : = 0; {Пусть в числе нет нулей.}
for i :=1 to kol do
begin
{Выделение цифры из числа и сравнение её с нулем.}
if (M mod 10 = 0) then
begin
pos := kol-i +1; {Позиция нуля в числе.}
writeln ( ’Ноль в ’, pos, ’м- разряде. ’ );
end;
M:=M div 1 0; {Изменение числа.}
end;
if pos=0 then writeln ( ’Число не содержит цифру 0. ’ );
end.
Окно ввода — это стандартное диалоговое окно, которое появляется на экране в результате вызова функции InputBox. В общем виде оператор ввода данных с использованием этой функции записывают так:
имя:= InputBox (заголовок_окна,подсказка,значение );
где
заголовок_окна — строка, определяющая название окна;подсказка — текст поясняющего сообщения;значение — строка, которая будет находиться в поле ввода при появлении окна на экране;имя — переменная строкового типа, которой будет присвоено значение из поля ввода.После выполнения фрагмента программы
var S : string; begin S:= InputBox ( ’ЗАГОЛОВОК ОКНА ’, ’Подсказка : введите исходные данные ’, ’Данное значение ’ ); end;
появится окно, представленное на рис. 3.37.
(рис 3.37) Окно ввода
У пользователя есть возможность изменять текст в поле ввода. Щелчок по кнопке ОК приведёт к тому, что в переменную, указанную слева от оператора присваивания, будет занесена строка, находящаяся в поле ввода. В данном случае в переменную S будет записана строка ’Данное значение’. Щелчок по кнопке Cancel закроет окно ввода.
Учитывая, что функция InputBox возвращает строковое значение, при вводе числовых данных применяют функции преобразования типов:
var S : string; gradus, radian : real; begin S:= InputBox ( ’Ввод данных ’, ’Введите величину угла в радианах ’, ’ 0,000 ’ ); gradus := StrToFloat ( S ); radian := gradus * pi / 1 8 0; MessageDlg ( ’Величина угла в градусах ’ +FloatToStr ( radian ), MtInformation, [ mbOk ], 0 ); end;
Можно применять диалоговое окно при решении задач, обрабатывающих некоторые числовые последовательности. Рассмотрим несколько таких задач.
Входные данные: N — целое число; Х — вещественное число, определяет текущий элемент последовательности.
Выходные данные: Max — вещественное число, элемент последовательности с наибольшим значением.
Промежуточные переменные: i — параметр цикла, номер вводимого элемента последовательности.
Алгоритм поиска наибольшего элемента в последовательности следующий (рис. 3.38).
В памяти компьютера отводится ячейка, например, с именем Max, в которой будет храниться наибольший элемент последовательности — максимум. Вводим количество элементов последовательности и первый элемент последовательности. Предполагаем, что первый элемент последовательности наибольший, и записываем его в Max. Затем вводится второй элемент последовательности и сравнивается с предполагаемым максимумом. Если окажется, что второй элемент больше, его записываем в ячейку Max. В противном случае никаких действий не предпринимаем. Потом переходим к вводу следующего элемента последовательности, и алгоритм повторяется с начала. В результате, в ячейке Max будет храниться элемент последовательности с наибольшим
Разместим на форме объект типа надпись Label1 и кнопку Button1 (см. рисунок 3.39). Щелчок по кнопке приведёт к выполнению следующей процедуры:
(рис 3.38) Алгоритм поиска наибольшего числа в последовательности
procedure TForm1. Button1Click ( Sender : TObject ); var i,N: integer; max,X: real; S : string; begin //Ввод количества элементов последовательности. S:= InputBox ( ’Ввод ’, ’Введите_количество элементов в последовательности. ’, ’ 0 ’ ); N:= StrToFloat ( S ); //Ввод первого элемента последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’Введите число. ’, ’ 0 ’ ); X:= StrToFloat ( S ); //Предположим, что первый элемент максимальный — Max=X. max:=X; //Параметр цикла принимает стартовое значение i=2, //т.к. первый элемент уже введён. for i :=2 to N do begin //Ввод следующих элементов последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’Введите число. ’, ’ 0 ’ ); X:= StrToInt ( S ); //Если найдётся элемент, превышающий максимум, //записать его в ячейку Max — //теперь он предполагаемый максимум. if X>max then max:=X; end; //Вывод наибольшего элемента последовательности. MessageDlg ( ’Значение наибольшего элемента - ’ +FloatToStr (max ), MtInformation, [ mbOk ], 0 ); end;
Результаты работы программы представлены на рис. 3.39,3.40,3.41,3.42.
(рис 3.39) Первое окно диалога к задаче 3.18
(рис 3.40) Второе окно диалога к задаче 3.18
(рис 3.41) Третье окно диалога к задаче 3.18
(рис 3.42) Результат работы программы к задаче 3.18
Блок-схема решения задачи приведена на рис. 3.43.
(рис 3.43) Алгоритм поиска минимального положительного числа в последовательности
Далее приведён текст подпрограммы с подробными
procedure TForm1. Button1Click ( Sender : TObject ); var N, k, min : integer; S : string; begin //Ввод первого элемента последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’введите_число. 0 - конец последовательности ’, ’ 0 ’ ); N:= StrToInt ( S ); //Предположим, что положительных чисел нет — K=0. В переменной //K будет храниться количество минимумов среди положительных //чисел. k : = 0; //Пока введённое число не равно нулю, выполнять тело цикла. while N<>0 do begin //Проверяем, является ли введённое число положительным. if N>0 then begin //Если N>0 и K=0, поступил 1-й положительный элемент, //предположим, что он минимальный Min=N, соответственно, //количество минимумов равно 1. if k=0 then begin k : = 1; min:=N; end //Если элемент не первый, сравниваем его //с предполагаемым минимумом; //если элемент меньше, записываем его //в Min и обнуляем счетчик. else if N<min then begin min:=N; k : = 1; end //Если элемент равен минимуму, //увеличиваем количество минимумов на 1. else if N=min then k:=k+1; end; //Ввод следующего элемента последовательности. S:= InputBox ( ’Ввод элементов последовательности ’, ’введите число. 0 - конец последовательности ’, ’ 0 ’ ); N:= StrToInt ( S ); end; //Конец цикла. if k<>0 then //Если значение счётчика не равно нулю, //выводим значение минимального элемента и //количество таких элементов, MessageDlg ( ’MIN = ’+IntToStr ( min)+ ’ K= ’+IntToStr ( k ), MtInformation, [ mbOk], 0 ) else //в противном случае сообщаем, //что положительных чисел нет. MessageDlg ( ’Положительных чисел нет ’, MtInformation, [ mbOk ], 0 ); end;
Чтобы решить задачу, нужно попарно перемножать элементы последовательности. Если результат произведения пары чисел — отрицательное число, значит, эти числа имеют разные знаки.
Пусть $$k$$ — количество смен знака последовательности — равно 0.
Пусть в переменной А хранится текущий элемент последовательности, а в переменой В — предыдущий. Введём N — количество элементов последовательности. Организуем цикл (переменная i меняется от 1 до N). В цикле будем делать следующее: вводим очередной элемент последовательности (A); если это это первый элемент последовательности (i=1), то сравнивать его не с чем, и просто переписываем переменную A в переменную B (B:=A). Если это не первый последовательности ($$i \not = 1$$), то проверяем знак произведения $$A \cdot B$$ (текущего и предыдущего элемента последовательности). Если произведение отрицательно, то счётчик k увеличиваем на 1. После чего не забываем в переменную B записать A. Блок-схема алгоритма приведена на рис. 3.44.
Разметим на форме два объекта типа надпись Label1 и Label2, объект поле ввода Edit1 и кнопку Button1 (рис. 3.45).
Текст подпрограммы, которая будет выполнена при обращении к кнопке, приведён далее.
(рис 3.44) Алгоритм к задаче 3.20
(рис 3.45) Форма программы решения задачи 3.20
procedure TForm1. Button1Click ( Sender : TObject ); var N, A, B, i, k : integer; S : string; begin N:= StrToInt ( Edit1. Text ); S:= InputBox ( ’Ввод элементов последовательности ’, ’Введите число. 0 - конец_последовательности ’, ’ 0 ’ ); A:= StrToInt ( S ); k : = 0; for i :=1 to N do begin S:= InputBox ( ’Ввод элементов последовательности ’, ’введите число. 0 - конец последовательности ’, ’ 0 ’ ); A:= StrToInt ( S ); if ( i <>1) then if A*B<0 then k:=k+1; B:=A; end; MessageDlg ( ’K = ’+IntToStr ( k ), MtInformation, [ mbOk ], 0 ); end;
Результаты работы программы представлены на рис. 3.46, 3.47.
(рис 3.46) Второе окно диалога к задаче 3.20
(рис 3.47) Результат работы программы к задаче 3.20
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.