Материалы, опубликованные в журналах и не входящие в статьи, можно увидеть на страницах номеров:

26 апреля 2020

Блок-схема - портрет программы

ИГОРЬ ДАНИЛОВ, кандидат технических наук

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

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

Рассмотрим конкретный пример. Как известно, корни квадратного уравнения \(ax2-bx+c=0\) вычисляются по формулам:

.

Где здесь исходные данные? Набор коэффициентов \(a\), \(b\), \(c\). Чем определяется искомый результат? Двумя приведенными формулами. В чем заключается процесс переработки исходных данных? В вычислениях по этим формулам.

Читатель, научившийся приводить расчетные формулы к «машинному» виду, легко сделает это и на сей раз:



Эта последовательность формул и будет уточненным алгоритмом.

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

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

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

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

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

Значит, нужно научить нашего электронного помощника, как поступать в столь каверзных ситуациях! Иначе говоря, предусмотреть в алгоритме все мыслимые варианты исходных данных.

Ясно, что раз при \(a = 0\) расчеты следует производить по другим формулам, значит, нужно вставить в программу блок, где машина бы проверяла коэффициент \(а\) на равенство нулю и в зависимости от результатов проверки выбирала путь решения. Может далее статься, что и \(a=0\) и \(b=0\). Тогда из уравнения выпадает неизвестная величина \(x\), и решать его вообще не имеет смысла. Нужно научить машину реагировать и на такое сочетание коэффициентов.

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

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

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

Прежде чем приступить к написанию программы по блок-схеме, последнюю нужно детализировать, заменив словесные описания последовательностью формул. Чтобы различать отдельные части блок-схемы, мы пометили некоторые ее узлы цифрами. Детализация той ветви, что лежит между узлами 11 и 12, уже проведена: сюда надо просто вставить формулы из первого варианта блок-схемы. Для ветви 5—6 никаких формул не надо — вся работа на этом этапе заключается в выводе сообщения: «Корней нет». Остались две ветви. Для одной из них, 3—4, требуется всего одна формула:

.

А вот формулы для последней ветви 9—10:


Здесь \(x_{r}\) и \(x_{im}\) — действительная и мнимая части комплексных корней, которые с помощью так называемой мнимой единицы, величины \(i=\sqrt{-1}\), выражаются формулами:

\(x_{1}=x_{r}+ix_{im}\) ; \(x_{2}=x_{r}-ix_{im}\). 

Легко видеть, что в формулах для ветвей 9—10 и 11—12 много общего. Это означает, что одни и те же последовательности команд будут написаны дважды. Можно ли обойтись без такого дублирования? Да. Целесообразно выполнять общие для каких-то ветвей вычисления еще до разделения ветвей. Заметим также, что во всех формулах коэффициент с используется со знаком минус. Казалось бы, все равно, какую операцию использовать -— сложение или вычитание. Но здесь надо учитывать специфику микрокалькулятора. Перед вычитанием пришлось бы правильно расставить по регистрам стека вычитаемое и уменьшаемое: первое — в X, второе — в Y. При сложении расстановка слагаемых значения не имеет, поэтому сложение предпочтительнее. Целесообразно заблаговременно сменить знак коэффициента, лучше всего сразу после его ввода. Все эти соображения учтены в новом варианте блок-схемы.

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

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

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

a → A; b → B; c (c₁=-c) → C; x₁ (\(x_{r}\)) 1(X); x₂ (\(x_{im}\)) → 2(Y)

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

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

Мы уже знаем, что в микрокалькуляторе используются и такие символы для записи шестнадцатиричных чисел, которые не спутаешь ни с одной десятичной цифрой. Оказывается, есть возможность, формально выполняя некоторые «противозаконные» операции, получать на индикаторе и запоминать в адресуемых регистрах комбинации этих символов с обычными цифровыми. Их-то и удобно использовать в качестве сообщений; как их получать, скажем позже, а пока договоримся использовать следующие шифры: Е00 — «Корней нет», Е01 — «Один корень», Е02 — «Два действительных корня» и Г. — «Корни комплексные». Для хранения шифров тоже нужны регистры. Поэтому в дополнение к предварительному распределению памяти запишем: Е00 → 0, Е02 → 4, Е01 → 3, Г. → 5. (Цифрами 0, 3, 4, 5, как и раньше, обозначены номера адресуемых регистров.)

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

Остановимся на такой структуре ввода-вывода: коэффициенты вводятся в естественной последовательности — \(a\), \(b\), \(c\); окончанием каждого ввода является нажатие клавиши С/П; после останова на индикаторе появляется шифрованное сообщение о характере результата, затем, после нажима С/П и следующего останова, высвечивается один корень, а после нажима клавиши ↔️ — второй (если он есть).

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

«Ввод а». Эта операция выполняется перед пуском программы. Величина а набирается на клавиатуре. Набор заканчивается нажимом клавиши С/П.

0. Запись \(а\) в адресуемый регистр А.
01. Останов для ввода \(b\). Набираем значение коэффициента на клавиатуре и снова нажимаем С/П.
02. Подготовка стека для приема значения \(c\).
03. Введен третий параметр уравнения, коэффициент \(c\). Ввод закончен. Теперь клавиша С/П запускает программу на счет.
04. Вычисляем \(c\)₁=-\(c\)
(Внимание: задавать \(с\) в экспоненциальном виде нельзя; в этом случае команда 04 изменит знак не мантиссы, а показателя.)
05. Проще всего вызвать \(а\) из «собственного» регистра А.
05. Проще всего вызвать а из «собственного» регистра А.
06—07. В стеке ничего не меняется. Мы лишь проверили, равно ли нулю содержимое регистра X. Если да, то есть если уравнение вырожденное, будем выполнять команду по адресу 08 (ветвь А₁—B₁). Если нет — перейдем к команде, записанной по адресу 23 (ветвь А₂—В₂).
08—09. Две команды использованы только для того, чтобы вернуть в регистр X значение \(b\). Казалось бы, можно обойтись и одной — ИП В. Но нужно «помнить о будущем» — скоро придется делить \(c\)₁ на \(b\), а при таком распределении чисел в стеке, как теперь, для этого все подготовлено.
10—11. Если \(b\)=0, то перейдем к команде по адресу 19 (на ветвь 5— 6), иначе — по адресу 12.
12—18. Вычисления по ветви 3—4.
12. Вот где пригодилось допущенное «излишество» (команды 08 и 09). Теперь мы сэкономили вызов величины \(c\)₁ и перемену местами содержимого регистров X и У. Кроме того, чтобы вызвать величину \(c\)₁ из адресуемого регистра, ее надо было бы предварительно туда записать. Мы же обходимся пока без записи величины \(c\)₁. Она к нашим услугам прямо в стеке.
13—14. Вычисления по ветви 3—4 закончены Вызываем в регистр X сообщение Е01 из регистра 3, останавливаем программу, чтобы его можно было прочесть. Иначе говоря, реализуем блок «Вывод «Один корень».
15—16. После нажатия клавиши С/П отрабатываем «Вывод Х₁». Величина корня — на индикаторе.
17—18. Эта команда замыкает ветвь 3—4, управление передается последней команде программы (блоку «Конец»), все остальные ветви обходятся, и работа программы заканчивается.
19—22 Сюда мы попадаем только в том случае, если \(a\)=0 и \(b\)=0. Вычислений проводить не надо. Просто выводим на экран сообщение Е00, что означает «Корней нет», и замыкаем ветвь, подобно предыдущей.
23—34. Ветвь 7—8.
23. Если мы уж попали на эту команду, значит, уравнение невырожденное. Надо вычислять дискриминант, а потом корни по одной из двух ветвей. Кстати, вас не смущает, что командой 12 мы вроде бы распрощались с величиной \(c\)₁? Ведь она в адресуемый регистр так и не записана... Но не волнуйтесь, все в порядке. Если мы и попадаем на адрес 23, то обязательно сразу после команды по адресу 07, а все промежуточные команды не выполняются. Поэтому и содержимое стека такое же, как и до команды перехода. Все готово для умножения \(ac\)₁ Вот после этой команды величина \(c\)₁ потеряна для нас навсегда. Но она больше и не нужна.
24. Выдвигаем величину \(b\) на первый план. Она теперь — объект работы нескольких команд.
25—27. Вводим в регистр X число 2, делим на него \(b\) и запоминаем результат в регистре B.
28—29. Величина B возведена в квадрат, дискриминант вычислен. Однако прежде чем перейти к его анализу, нужно получить величину \(x_{r}\), так как она понадобится нам в обеих ветвях.
30. Извлекаем величину В из ее хранилища — регистра В.
31. Для деления нужна величина \(а\). Проще всего вызвать ее в стек заново.
32—34. Теперь все вычисления по ветви 7—8 закончены. Величина \(x_{r}\) отправлена на хранение в регистр 1, можно переходить к анализу величины \(d\), благо она рядом.
35—37. Делаем последнее сравнение в программе. Если \(d\)>0, то корни действительные и надо перейти на ветвь 11—12 (команда 48). Если же \(d\)<0, то корни комплексные, надо вычислять их по формулам ветви 9—10.
38—47. Ветвь 9—10.
38—39. Так как величина \(d\) меньше нуля, то, чтобы вычислить корни, нужно сначала изменить ее знак.
40. Для вычисления \(x_{im}\) нужна величина \(а\). Проще всего опять-таки вызвать ее из регистра А.
41. Величина  вычислена и находится в регистре X.
42—43. Все готово для вывода результата. Можно останавливать ПМК и считывать \(x_{im}\) и \(x_{r}\) с индикатора, но мы еще не вывели на индикатор сообщения о том, что за величины получены. Приходится отодвигать готовые результаты и переносить в регистр X шифрованное сообщение: Г. — «Корни комплексные».
44—45. Сообщение прочитано. Возвращаем результаты вычислений на старое место и останавливаем программу, чтобы считать их.
46—47. Ветвь 9—10 замкнута.
48—60. Последняя ветвь 11—12.
48. Поскольку \(d\) находится в регистре X (как и после команды 35), то сразу же извлекаем квадратный корень.
49—50. Вычисляем вспомогательную величину .
51. Получаем первый корень \(х\)₁. Величина перешла в регистр предыдущего результата X1.
52—54. Вычисляем второй корень \(x_{r}\). Расчеты закончены.
55—58. Вывод результатов организуется так же, как и в предыдущей ветви.
59—60. Вот и последние команды, реализация блока «Конец». Они подготавливают программу для приема новой информации, передавая управление на начало. Можно вводить новые данные и повторять расчет.
Вернемся к распределению памяти. Окончательная картина такова:

a → A; b → B;  \(x_{r}\) → 1; \(x_{r}\), \(x_{1}\) → Y; \(x_{im}\), \(x_{2}\) → X


Итак, три регистра удалось сэкономить. Если бы нам понадобилось ввести в оставшуюся часть программной памяти еще одну программу, то «лишние» регистры очень бы пригодились.

Теперь, как и было обещано, о получении шифрограмм. Сообщение Е00 получается, если в режиме вычислений выполнить следующие действия. Сначала набрать 100 ВП 99. На индикаторе, естественно, загорается ЕГГОГ. Не смущаясь, продолжаем: ВП↑. На индикаторе то, что надо: Е00. Нажимаем П0 — и шифрограмма отправляется на хранение в регистр 0.

Е01 и Е02 получаются аналогично, только вместо числа 100 нужно набрать соответственно 101 или 102. Алгоритм же для получения сообщения Г. другой: Сх ↑ ፥ (здесь, конечно, опять ЕГГОГ, ведь делится ноль на ноль), ВП ВП ↑ . На индикаторе — то, что нужно. Можно теперь записать Г. в регистр 5.

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

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

1. Ввести программу.
2. Установить режим вычислений (F АВТ).
3. Ввести шифры:
100 ВП 99 ВП ↑ ПО
101 ВП 99 ВП ↑ ПЗ
102 ВП 99 ВП ↑ П4
Сх ↑ ፥ ВП ВП ↑ П 5
4. Очистить программный указатель (В/О).
5. Ввести исходные данные: \(а\) С/П; \(b\) С/П; \(с\) С/П.
6. Вывод: после первого останова на индикаторе появляется сообщение:
Е00 — корней нет;
Е01—уравнение линейное, корень только один;
Е02 — два действительных корня;
Г. — корни комплексные.
7. Если корней нет, то для продолжения расчетов перейти к п. 5. Если корни есть, то нажать С/П. После останова на индикаторе — значение первого корня (если корни действительные) или мнимой части комплексных. Для чтения другого корня или действительной части нажать ↔️.
8. Для продолжения расчетов перейти к п. 5.

Контрольный пример:

Ввод: 
a=2; b=5; c=3 
а=1; b=-4; c=5 
а=0; b=8; c=3 
а=0; b=0; c=J

Вывод: 
Е02; —1; —1,5 
Г; 1; 2 
Е01; —3,75-10⁻¹ 
Е00

Комментариев нет:

Отправить комментарий

Последняя добавленная публикация:

Дом в декаду | ТМ 1939-01

Вл. ДЛУГАЧ и Як. ШУР Перед вами прекрасное четырехэтажное здание новой школы. Трудно поверить, что это огромное строение возведено в декад...

Популярные публикации за последний год

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