ОСНОВЫ АЛГОРИТМИЗАЦИИ И ПРОГРАММИРОВАНИЯ
Сферы применения компьютеров, которые рассмотрены в предыдущих темах курса «Основы информатики и вычислительной техники» (создание текстовых документов, баз данных, графических иллюстраций и др.), позволяют решать, хотя и важный, но весьма ограниченный набор типовых задач без овладения навыками программирования. Вместе с тем, стремительный научно-технический прогресс был бы невозможен без широкого использования вычислительной техники для автоматизации проектирования самых разнообразных изделий, управления их производством и проведения научных исследований. Существует необозримое множество как сложных, так и относительно простых задач, которые имеют частный, прикладной характер и для решения которых недостаточно навыков, полученных при решении типовых задач. Целью данной темы курса является развитие понимания того, как правильно ставить и решать прикладные задачи. Это важно не только с точки зрения более профессионального использования компьютера, но ценно и в интеллектуальном плане, как приобретение умения грамотно формулировать и эффективно решать различные, в том числе и некомпьютерные задачи. 1. ИНФОРМАЦИОННАЯ МОДЕЛЬ 1.1
Понятие информационной модели Окружающий нас мир чрезвычайно сложен и законы, по которым он существует, до конца не познаны. Однако знания, которые накапливает каждый человек в отдельности и все человечество в целом, развиваются от «простого к сложному». Ребенок начинает постигать мир с помощью игрушек, которые являются упрощенной моделью животных, машин, строений и т. д. Конструкторы самолета изучают нагрузки, которые он будет испытывать в полете, продувая модель самолета в аэродинамической трубе. Аналогично поступают кораблестроители, испытывая модели судов в специальных бассейнах. Таких примеров можно привести множество. В общем случае Моделью (в широком смысле) называется представление наиболее существенных свойств объекта в такой упрощенной форме, которая позволяет выполнить теоретические и экспериментальные исследования. Модель по определению является упрощенным представлением, но упрощение допустимо лишь в такой степени, пока оно не искажает существенных свойств. Какие из свойств являются существенными, а какими можно пренебречь – зависит от целей, для достижения которых создается модель. Рассмотрим относительно простой объект – светильник, который должен обеспечить заданную освещенность на заданном расстоянии. Модель такого объекта должна в первую очередь учитывать число ламп, яркость их свечения, отражающую способность внутренней поверхности светильника, поглощение света плафоном. Однако, кроме светотехнических характеристик, при проектировании светильника необходимо учитывать и теплотехнические характеристики, поскольку большая часть потребляемой электроэнергии превращается в тепло. С этой точки зрения существенным свойством лампы является не яркость свечения, а количество выделяемого тепла, в частности, передаваемого за счет теплопроводности патрону. Учет этого свойства важен для правильного выбора патрона и в конечном итоге может повлиять на выбор числа и мощности ламп. Понятие объекта в определении модели весьма многообразно. Оно может означать материальные объекты (как упомянутые выше модели самолета и корабля). В этом случае речь идет о материальных (предметных) моделях. Однако по мере накопления знаний появляется возможность описать (в допустимо упрощенной форме) строение, свойства и поведение реальных объектов теоретическими закономерностями, в частности, математическими соотношениями, то есть заменить материальные модели математическими моделями. «Книга жизни написана на языке математики»,– эти слова принадлежат Галилею – великому естествоиспытателю прошлого – и развитие науки подтверждает их справедливость. Математической моделью объекта называются математические соотношения (например, уравнения), которые описывают поведение объекта и определяют его существенные свойства. Замена материальных моделей математическими моделями получила очень широкое распространение, поскольку позволяет значительно быстрей получить требуемые сведения об объекте, чем изготовление и натурные испытания его материальной модели. Наглядным примером может служить рассмотренный объект «светильник», который можно спроектировать на основе известных физических законов, представленных математическими соотношениями, без изготовления материальной модели. Необходимо также отметить, что математическое моделирование позволяет предсказать свойства объекта не только в обычных, но и в критических ситуациях, приводящих к разрушению материальных моделей.
1
Вместе с тем, к математическим моделям следует относиться как к фундаменту, который сам по себе еще не является готовым сооружением. Рассмотрим два объекта. Первый – электрическая цепь, содержащая резисторы, конденсаторы, индуктивные элементы и усилители. Второй – подвеска автомобиля, состоящая из рычагов, рессор, амортизаторов и колес с эластичными шинами. Несмотря на все очевидные различия этих объектов, у них есть принципиальное сходство. Оно заключается в том, что оба объекта могут быть описаны математическими уравнениями, принадлежащими к одному и тому же классу, то есть они имеют сходные математические модели. Аналогично на фундаментах, заложенных по одному и тому же принципу, можно построить разные сооружения, имеющие различное функциональное назначение. В случаях, когда математическая модель описывается уравнением, поведение объекта определяется его решением. Однако из курса алгебры известно, что даже такой простой класс уравнений, как алгебраические уравнения n-ой степени не имеют аналитического решения для n > 4. Поэтому фундаментальный закон, описывающий функционирование объекта каким-либо уравнением, должен быть при необходимости дополнен методом его приближенного решения, который должен обеспечить заданную точность. Такой метод составляет содержание вычислительной модели. Вычислительной моделью объекта называются математические соотношения, которые описывают конкретный численный метод реализации математической модели (например, приближенное решение уравнения, если точное аналитическое решение не существует или является слишком громоздким). Если же математические соотношения, которые описывают объект моделирования, относительно просты и сами по себе достаточны для получения окончательного результата без привлечения специальных численных методов, то математическая и вычислительная модели становятся тождественными понятиями. Обе модели – математическая и вычислительная – в совокупности можно образно представить как фундамент, на котором уже возведены стены, перекрытия и даже размещено внутри некоторое оборудование. Все в целом свидетельствует о функциональном назначении сооружения как предприятия для выпуска такой-то продукции. Однако нет сырья, не определены требования к нему, полуфабрикатам и готовой продукции, не предусмотрено, в каких количествах необходимо их хранить. Аналогия с предприятием вполне оправдана, так как постановка и решение прикладной задачи (в частности, задачи моделирования) является творческим «производственным процессом», итогом которого должна быть интеллектуальная «продукция» – результаты решения задачи. Таким образом, математическая и вычислительная модели должны быть дополнены не менее важной моделью данных для описания исходных данных (сырья) и результатов их обработки моделью объекта (готовой продукции), а также схемы передачи данных (полуфабрикатов) от одного этапа обработки к следующему. Модель данных должна четко определять не только содержание, но и форму представления данных и обеспечивать при необходимости формирование исходных данных. В терминах использованной выше аналогии модель данных играет роль службы снабжения и сбыта, складов сырья, полуфабрикатов и готовой продукции. Модель данных описывает совокупность входных, промежуточных и выходных данных, с которыми оперирует объект, с учетом их типа и структуры. Объединение трех рассмотренных моделей обладает новым качеством и получило название информационной модели. Информационная модель объединяет три модели – математическую, вычислительную и данных – то есть, содержит основную информацию о том, что и каким методом необходимо обрабатывать для получения окончательного результата. К обрабатываемым данным применимо понятие информации, независимо от их сущности. Поэтому процесс моделирования можно рассматривать как процесс преобразования входной информации в выходную информацию, где сам моделируемый объект выступает в качестве некоторого преобразователя информации. Действительно, модель объекта, которая описывается какими-либо математическими соотношениями, справедлива для различных данных, лишь бы они принадлежали множеству допустимых значений. Модель «не знает», какие именно данные поступят, поэтому конкретные входные данные, уменьшая первоначальную неопределенность, в принципе являются информацией независимо от своего содержательного смысла. С другой стороны, создатель модели либо не знает точно, что получит в результате моделирования, либо для специального проверочного примера знает, что должен получить. В первом случае выходные данные содержат новую для него информацию, а во втором случае выходные данные либо подтверждают правильность модели (если совпадают с ожидаемыми данными), либо свидетельствуют о её неадекватности (если значительно отличаются от ожидаемых данных), что также является ценной информацией. Сказанное выше относится не только к решению собственно задач моделирования. Необходимость решения любой прикладной задачи возникает в случаях: 2
когда эта задача еще никем не решена (что бывает весьма редко); когда имеющиеся решения не удовлетворяют по каким-то критериям и требуется найти новое, более эффективное решение. В любом случае решение прикладной задачи должно отличаться новизной. А новизна – неотъемлемое свойство информации. -
1.2 Способы описания прикладной задачи Из приведенного определения информационной модели следует многообразие возможных вариантов. Один и тот же объект можно представить различными информационными моделями в зависимости от поставленной цели и, наоборот, сходные математические и вычислительные модели могут соответствовать совершенно различным объектам. Поэтому не существует единственного универсального и, вместе с тем, понятного и наглядного способа описания. Поскольку информационная модель является лишь частью прикладной задачи, в дальнейшем будем рассматривать описание не только модели, но также постановки и решения прикладной задачи. 1.2.1
Основные требования к описанию
Для того чтобы «виртуальное предприятие», с которым выше ассоциировалась информационная модель, выпустило требуемую «продукцию», то есть, был получен результат решения поставленной задачи, «производственный процесс» должен быть четко организован, то есть, должны быть детально описаны сама задача и процесс её решения. Описание должно удовлетворять ряду требований: 1) описание должно быть понятно всем участникам «производственного процесса»; 2) описание должно быть исчерпывающим и охватывать все этапы решения задачи; 3) при декомпозиции (разбиении) сложной задачи на несколько более простых подзадач соответственно должно быть структурировано и описание; 4) описание должно предусматривать все возможные ситуации, которые могут встретиться при решении задачи, во избежание неправильных результатов, аварийного завершения или «зависания» компьютера. Первое требование отнюдь не означает, что описание должно быть составлено в такой форме, которая одновременно понятна и человеку, и компьютеру. Речь идет об описании, составление которого включает несколько стадий. На первой стадии составляется неформальное словесное описание (понятное человеку, но непонятное компьютеру). На последующих стадиях описание детализируется и становится более формализованным, вплоть до последней стадии – описания в виде исполняемого .exe-файла (понятного компьютеру, но совершенно «недружественного» человеку). Таким образом, с переходом на следующую стадию уровень абстракции описания понижается по сравнению с предыдущей стадией. На каждой стадии описания должны использоваться общепринятые (для данного уровня абстракции) язык, термины, обозначения. Разработчик прикладной задачи не должен применять какие-либо элементы описания, введенные им самим и непонятные другим. Такой последовательный процесс позволяет все более полно удовлетворить и второе требование. Следует подчеркнуть, что изменение содержания описания (добавление новых сведений об особенностях решения задачи) при переходе на следующую стадию является результатом творческих усилий разработчика. Если же при переходе на следующую стадию содержание описания по существу не изменяется, а изменяется только форма его представления, то соответствующие преобразования описания можно, в принципе, выполнить автоматически без непосредственного участия разработчика. Программы, выполняющие такое преобразование, называются трансляторами (по аналогии с трансляторами текста на одном языке в другой). Трудней всего выполнить последнее требование (особенно для достаточно сложных прикладных задач). На практике эта проблема решается путем возможно более широкого тестирования разработанной программы и внесения изменений в описания, которые устраняют замеченные недостатки. Такой процесс доработки описаний называется отладкой. Ниже будет показано, что составление описания может быть ограничено 2 – 3 стадиями. 1.2.2
Неформальное словесное описание
При формулировании постановки задачи, а также на начальной стадии формирования информационной модели используется описание на обычном языке в виде текста и математическое описание в виде формул. Рассмотрим в качестве примера следующую простую прикладную задачу. ЗАДАЧА 1.1. Расчет средней успеваемости. 1)
Для конкретного ученика (с указанием фамилии и инициалов) вычислить среднюю успеваемость за семестр по какомулибо предмету на основе полученных баллов.
2) 3)
Учесть, что число оценок у разных учеников может быть различным. Результаты расчета вывести в форме: Фамилия И.О. – средний балл. 3
Казалось бы, эту задачу можно легко решить с помощью электронной таблицы, например, Excel. Однако условие (2) вынуждает для каждого ученика вычислять усредненную оценку индивидуально с учетом количества оценок и конкретных ячеек, куда они записаны, что усложняет вычисления. Целью данной задачи является упрощение вычислений по сравнению с использованием электронной таблицы. Не обсуждая целесообразности подобного метода подведения итогов, отметим неполноту этого описания. Прежде всего, требует уточнения слово «средняя». Существуют варианты: среднее арифметическое, среднее взвешенное, среднее геометрическое. Пусть можно ограничиться самым простым вариантом – вычислением среднего арифметического значения. Тогда описание дополняется математической моделью
mb =
b + ... + bn 1 n bi = 1 , ∑ n i =1 n
(1.1)
где n – число оценок, bi – i-я оценка, mb – искомая средняя оценка. Выражение (1.1) можно непосредственно использовать для вычисления среднего балла. Однако в этом случае необходимо вначале определить число оценок n, запомнить все оценки b1,…,bn в массиве, затем просуммировать их и разделить сумму на n. Целесообразно поступить иначе и реализовать вычислительной модели, которую запишем в виде уточнения.
математическую
модель (1.1) с помощью
Уточнение. Вычисление среднего значения mb реализовать путем: А) последовательного накопления в переменной sb суммы вводимых оценок
sb ← sb + очередная оценка
(1.2)
(начальное значение sb = 0); Б) последовательного накопления в переменной n числа оценок
n← n+1
(начальное значение n = 0); В) после окончания ввода оценок вычислить mb = sb n
(1.3)
(1.4)
Здесь стрелка означает «заменить предыдущее значение переменной новым значением». Из постановки задачи, а также принятой математической и вычислительной модели вытекает модель данных: Входная информация: - Фамилия И.О. – текст, который необходимо запомнить в переменной sn; - Совокупность оценок – числа, каждое из которых при вводе необходимо запомнить в переменной b. Выходная информация: - содержимое переменной sn (Фамилия И.О.): - содержимое переменной mb (средний балл) – число, которое при расчете по формуле (1.4) может оказаться дробным. Тогда его следует округлить до ближайшего целого числа. Промежуточные данные: - переменные sb, n – целые числа. Приведенное выше описание содержит постановку задачи и необходимые сведения в виде информационной модели, которые уточняют, что и как необходимо обработать для получения конечного результата. Оно является типичным примером первой ступени описания. Такое описание можно ввести в компьютер с помощью текстового редактора, включив в него и математические выражения. Однако это ни на шаг не приблизит решение задачи. Современные системы искусственного интеллекта ещё не достигли такого уровня развития, чтобы из словесного описания выделить смысловое содержание и четко формализовать его конкретную последовательность команд процессора. Если бы такие системы и существовали, то, очевидно, что они были бы слишком сложными и дорогими и применение их для решения задач, подобных описанной, было бы совершенно неоправданным. Поэтому вторую стадию описания, как и первую, должен выполнять сам разработчик. 1.2.3
Алгоритмическое описание
На данной стадии необходимо дополнить описание сведениями, которые на первой стадии не были учтены, так как их можно было считать несущественными. Применительно к рассматриваемой задаче требуется дополнительно учесть: • Как, не вводя явно число оценок n, определить, закончен ли уже ввод оценок? • Поскольку нерационально запускать эту задачу для каждого ученика в отдельности, то следует обеспечить её повторное выполнение для группы учеников. Но тогда необходимо определить, по какому признаку прекратить выполнение, если все ученики уже учтены? Здесь наглядно видна необходимость организации повторяющихся действий. 4
Отметим, что в конкретизации нуждается не только словесное описание, но и математические выражения. Например, выражение (1.1), содержащее знак суммирования Σ, необходимо представить последовательностью циклически повторяющихся операций суммирования. Математическую запись 2ab, в которой содержатся три сомножителя, а знаки умножения опущены, необходимо преобразовать в форму, где они явно указаны в виде звездочки «*», то есть, 2*a*b. Кроме того, все математические выражения должны быть представлены в форме однострочного описания. Например, вычисление выражения
a +b c 1+ d после изучения курса алгебры не вызывает затруднений. Однако полученные знания о том, в какой последовательности и какие действия требуется выполнить, на данной стадии описания нуждаются в формализации: вычисления должны выполняться слева направо с учетом старшинства операций, причем для изменения естественной последовательности действий должны применяться круглые скобки. Формализованное однострочное описание должно иметь вид
(a + b) (1 + c d ), где знак деления в виде горизонтальной черты заменен знаком деления «/». В целом, важнейшее дополнение, которое требуется сделать на второй стадии описания, заключаются в том, чтобы содержащиеся в информационной модели сведения «что и как» (в принципе) необходимо обрабатывать, описать конкретной последовательностью действий для выполнения требуемой обработки. Очевидно, что хорошо продуманная организация процесса решения задачи не менее важна, чем правильная постановка задачи. Теперь можно ввести понятие алгоритма как фундаментального понятия программирования. Алгоритмом называется четкое описание последовательности действий (или выполнения), обеспечивающих решение поставленной задачи за конечное число шагов.
правил
их
Полагают, что слово «алгоритм» произошло от имени персидского математика ІХ века Мохамеда альХорезми (которое в латинизированном виде превратилось в Algorithmi). Хорезми принадлежат основополагающие труды по арифметике и алгебре, которые были переведены с арабского языка на латинский в ХІІ веке. По этим трудам в Европе познакомились с десятичной позиционной системой счисления и основными правилами алгебры. Из определения алгоритма следует, что решение задачи является дискретным (прерывным) процессом, то есть, должно быть описано в виде отдельных конкретных действий. Свойство дискретности принципиально важно для реализации алгоритма с помощью компьютера, функционирование которого заключается в выполнении отдельных команд. Благодаря этому свойству выполнение алгоритма можно прервать на любом действии и затем продолжить. Выполнение с прерыванием очень часто используется при отладке алгоритма и позволяет контролировать изменение интересующих данных. Выполняемые действия должны быть детерминированными, то есть должно быть четко (строго и недвусмысленно) определено не только каждое действие, но и порядок их следования. Правильная организация последовательности действий является одним из важнейших условий правильности алгоритма. Необходимо отметить, что алгоритм допускает в ряде случаев произвольное изменение порядка некоторых действий, если только конечный результат этих действий от перестановки не изменяется. Например, в соответствии с переместительным (коммутативным) законом сумма слагаемых или произведение сомножителей не изменяется от перемены мест операндов. В рассмотренной задаче 1.1 выполнение таких действий, как присваивание нулевых начальных значений переменным n (число оценок) и sb (сумма баллов), можно поменять местами. То есть, последовательность действий n ← 0, sb ← 0 эквивалентна последовательности действий sb ← 0, n ← 0. Однако данные действия должны обязательно предшествовать действиям накопления числа оценок и суммы баллов: n ← n + 1, sb ← sb + b или sb ← sb + b, n ← n + 1. Описанная алгоритмом последовательность преобразования входных данных в выходные данные должна обеспечивать получение результата путем выполнения конечного числа действий. Если при выполнении алгоритма происходит «зацикливание», то есть, результат не достигается при сколь угодно большом числе действий, то такой алгоритм, безусловно, является ошибочным. Ошибка может заключаться не только в неправильном описании правильной информационной модели, но может быть следствием неправильного выбора вычислительной модели. Строгому изучению теории алгоритмов посвящен специальный раздел дискретной математики, в котором, в частности, обосновывается отсутствие алгоритмов для решения некоторых задач. Однако такие 5
задачи образуют особые классы, куда не входят прикладные задачи, представляющие интерес на практике. Поэтому будем полагать, что алгоритм существует и нужно лишь уметь его составить. Понятие алгоритма не содержит никаких требований к форме его описания. Таким образом, допустимы различные описания одного и того же алгоритма. Использование обычного языка на данной стадии описания практически исключается, поскольку, во-первых, нельзя гарантировать полное отсутствие смысловых неточностей и, во-вторых, даже для относительно несложных задач описание на обычном языке получается слишком громоздким и трудно понимаемым. Для преодоления этих недостатков разработаны специальные алгоритмические языки, о которых будет сказано ниже в пункте 1.2.4. Значительно более удобным и наглядным является сочетание языковых элементов описания с графическим представлением. Такое описание обычно называется блок-схемой, где каждый блок соответствует определенному действию (или четко определенной совокупности действий), начертание блока отражает характер действий, а соединительные линии между блоками указывают последовательность выполнения действий. Конкретное действие, которое выполняет блок, указывается либо внутри блока, либо рядом с ним (справа от блока). Описание алгоритма в виде блок-схемы стандартизировано и весьма широко распространено. Графические символы блоков предусмотрены, например, в системе инженерной графики Visio и даже в виде графических примитивов в текстовом редакторе Word. Часто слово «алгоритм» употребляется как синоним блок-схемы. В дальнейшем будем использовать именно такое описание алгоритма. В качестве примера на рис.1.1 изображена блок-схема алгоритма решения поставленной выше задачи 1.1. Обозначения блоков алгоритма и процесс его синтеза будут подробно рассмотрены в разделе 4. Здесь же блок-схема приведена с целью проиллюстрировать, как она 1 обычно выглядит. Данный алгоритм работает следующим Початок образом: 2 - блок 3 вводит Фамилию И.О. ученика; Поч. циклу 1 - блок 5 подготавливает накопление данных; - блок 7 вводит очередную оценку; 3 - блоки 6 – 9 обеспечивают повторение ввода оценок и Введ. sn накопление информации, пока вместо оценки не будет введено число 0 как признак того, что все оценки уже 4 ні введены; sn <> sp - блок 10 вычисляет средний балл, а блок 11 выводит Фамилию так И.О. и средний балл ученика; 5 блоки 2, 3, 4 и 12 обеспечивают повторение вычислений для n:=0 sb:=0 других учеников, пока вместо фамилии не будет введена пустая строка. 6 Поч. циклу 2 7
8
9
10
11
12
Введ. b n:=n+f(b) sb:=sb+b Кін. циклу 2: b=0 mb:= Round(sb/n) Вивед. sn, mb Кін. циклу 1: sn = sp
13 Кінець
Рис. 1.1
Алгоритм задачі 1.1
Нетрудно видеть, что алгоритмическое описание весьма компактно. Вместе с предварительным описанием переменных b, mb, n, sb, sn, sp (которое состоит из имени, типа, начального значения и задано моделью данных), а также функции f(b) (которая возвращает 0, если b=0 и 1, в противном случае) данное описание является исчерпывающим. После изучения основных алгоритмических структур приведенная блок-схема станет полностью понятной. Таким образом, алгоритмическое описание удовлетворяет требованиям, изложенным в пункте 1.2.1, и представляет собой законченную вторую стадию описания решаемой задачи. Возвращаясь к аналогии процесса решения задачи с некоторым виртуальным «производственным процессом», алгоритмическое описание можно уподобить чертежу интеллектуального изделия, который полностью его описывает. Подобно чертежной документации на сложное изделие, состоящей из сборочного чертежа и чертежей составных узлов, алгоритм решения сложной задачи состоит из основного (управляющего) алгоритма и алгоритмов, описывающих решение составных подзадач. Каждый из таких алгоритмов описывается блок-схемой, подобной рис.1.1. Сказанное выше позволяет дать понятию «алгоритм» ещё одно определение.
Алгоритм в виде блок-схем, дополненных описанием данных, представляет собой полностью формализованное описание информационной модели.
6
Поскольку алгоритмическое описание обеспечивает четкость и однозначность всех действий, необходимых для решения задачи, то эти действия, в принципе, могут быть выполнены в режиме интерпретации. Интерпретацией называется последовательное (оператор за оператором) выполнение всех элементов описания без формирования общей совокупности команд процессора в виде .exe-файла. Для реализации этого режима требуются специальные программы – интерпретаторы. До недавнего времени алгоритмическое описание составлялось лишь на бумаге и правильность его трудно было проверить. Реализация алгоритма возлагалась на некоего Исполнителя, роль которого играла система программирования на каком-либо языке высокого уровня. Все это умаляло важность алгоритмического описания и создавало мнение о его второстепенности и даже бесполезности для программирования. В настоящее время появились предпосылки для коренного изменения такого отношения к алгоритмам. Это оказалось возможным благодаря разработке и объединению в одну систему двух интерпретаторов – арифметического и алгоритмического. В результате создана система АЛГОРИТМ – принципиально новая система визуального программирования, которая обеспечивает эффективное проектирование и непосредственное выполнение алгоритмов.
Рис. 1.2 Головне вікно системи АЛГОРИТМ
На рис.1.2 изображено главное окно системы АЛГОРИТМ, большую часть которого занимает поле алгоритма. В данном случае приведен алгоритм, который полностью соответствует блок-схеме на рис.1.1, но несколько отличается по оформлению: - блоки расположены не в одну, а в две колонки; - операторы блоков (описывающие выполняемые действия) размещены не внутри блоков, а находятся в двух колонках справа от блоков. Такое раздельное отображение позволяет, не изменяя размеры блока, «вставлять» в него оператор с весьма длинным описанием, а также добавлять краткий комментарий. Для облегчения взаимной увязки блока с описанием оператора используется условное обозначение блока (УОБ), которое состоит из буквы и цифры, 7
например, A1, R2 и т.п. Буква указывает функциональное назначение блока, а цифра – порядковый номер в соответствующей функциональной группе. В дальнейшем система АЛГОРИТМ будет служить основным инструментальным средством изучения всех вопросов алгоритмизации и будет подробно рассмотрена.
1.2.4
Описание с помощью языков программирования
Компьютер, с точки зрения своих аппаратных возможностей, понимает только такое описание хода решения задачи, которое представляется последовательностью команд процессора типа «извлечь данные из такой-то ячейки памяти – выполнить такую-то операцию – поместить результат в такой-то регистр процессора или такую-то ячейку памяти» и т.п. Такое описание содержится в .exe-файле программы и закодировано последовательностью «0» и «1». Непосредственное двоичное кодирование программ разработчиком относится к самому началу развития вычислительной техники и в настоящее время является достоянием истории. Для автоматизации этого процесса вначале были разработаны языки программирования низкого уровня, типичным представителем которых является язык Ассемблер (Assembler). Ассемблер позволяет программисту пользоваться алфавитными мнемоническими кодами операций (машинных команд), по своему усмотрению присваивать символические имена регистрам процессора и памяти, задавать удобные схемы адресации и др. Этот язык является машинно-ориентированным и программа, написанная для одного типа процессора, непригодна для процессоров иного типа. Другим серьезным недостатком Ассемблера является то, что он не помогает программисту в творческом процессе – в разработке алгоритма –, а лишь облегчает кодирование хода вычислений в машинные команды. В настоящее время Ассемблер применяется для создания специальных программ, критичных к быстродействию и требующих максимально полного учета особенностей команд процессора (например, различных драйверов). Существенным шагом на пути преодоления этих недостатков стала разработка 50-40 лет тому назад языков программирования Алгол (от algorithmic language) и Фортран (от formula translation). Эти языки были, во-первых, машинно-независимыми и, во-вторых, облегчали организацию хода решения задачи с помощью удобного описания таких характерных особенностей, как циклическое повторение некоторых фрагментов, разветвление вычислений, а также облегчали структурирование программ. Эти языки, являясь алгоритмическиориентированными, послужили прообразом для создания многих языков программирования высокого уровня, из которых наибольшее распространение получили языки Паскаль (Pascal), Бейсик (Basic) и Си (С). Описание программы на этих языках состоит из последовательности строк (обычно называемых «строки программного кода»), которые вводятся с клавиатуры с помощью текстового редактора, входящего в состав каждой системы программирования. Однако каждый язык предусматривает собственный набор служебных слов, которые имеют четко определенное смысловое значение. Например, в языке Паскаль: unit, var, begin, end, if then, repeat и еще несколько десятков слов. Для каждого языка четко определен алфавит символов, который включает в себя буквы (в большинстве случае – только латиница), цифры, знаки операций, знаки препинания и специальные символы, которые можно ввести с клавиатуры. Например, фрагмент программы на языке Паскаль, который соответствует блокам 6 – 9 алгоритма на рис.1.1, имеет такой вид: …………. repeat readln(b); n := n + f(b); sb := sb + b; until b = 0; …………. Хотя собственно алфавит разных языков во многом совпадает, однако разные языки существенно отличаются синтаксисом и семантикой (например, фигурные скобки { } в языках Паскаль и Си имеют совершенно разное назначение). На основе языков высокого уровня возник и развился стиль так называемого процедурноориентированного программирования (ПОП), который не утратил своей актуальности и в настоящее время. Одной из основных особенностей этого стиля является раздельное структурирование данных и процедур их обработки. Вместе с тем, последние 10 – 15 лет ознаменовались значительным расширением возможностей языков высокого уровня за счет объединения данных и процедур обработки в так называемые объекты (или классы). Такой подход стимулировал развитие и внедрение нового стиля программирования, который называется объектно-ориентированным программированием (ООП). Необходимо подчеркнуть, что стиль ООП не отменяет ПОП, а включает последний в себя как неотъемлемую составную часть. Важно ясно представлять различия между алгоритмическим описанием и описанием на зыке программирования. Описание решения прикладной задачи на языке программирования является по существу лишь формой реализации алгоритма решения данной задачи. Один и тот же алгоритм на разных языках имеет разное описание.
8
Собственно написание программы на языке высокого уровня по готовому алгоритму требует значительно меньших творческих усилий, чем создание самого алгоритма, и, в принципе, может быть автоматизировано. Для каждого языка высокого уровня разработана система программирования, которая интегрирует в себе редактор для ввода текста программы, компилятор и средства для отладки программы. Компиляция отличается от интерпретации тем, что программное описание транслируется непосредственно в машинный код, в частности, в виде .exe-файла, который может быть выполнен после компиляции. Следовательно, для разных типов процессоров требуется создание разных систем программирования, использующих один и тот же язык высокого уровня. Вместе с тем, необходимо отметить важное достоинство использования систем программирования на языках высокого уровня. Время решения прикладной задачи с помощью созданного .exe-файла существенно (на два – три порядка) меньше, чем время решения данной задачи в режиме интерпретации. Таким образом, описание на языке программирования высокого уровня является последней, третьей стадией описания прикладной задачи. В терминах процесса решения задачи как виртуального «производственного процесса», программу на языке высокого уровня следует рассматривать как технологическую документацию на изготовление интеллектуального изделия в соответствии с разработанным чертежом – алгоритмом. Очевидно, что эта документация зависит от конкретного оборудования – выбранного языка программирования. 1.3 Этапы решения прикладной задачи Решение поставленной прикладной задачи разделяется на несколько этапов, которые, в целом, соответствуют рассмотренным выше стадиям описания задачи. На рис.1.3 схематически показано содержание и последовательность выполнения этапов. Первый этап – детализация постановки задачи. Входной информацией для него служит неформализованное описание постановки задачи. На этом этапе составляется информационная модель задачи (подэтап Д1) и в общих чертах обосновывается, как следует организовать решение задачи (подэтап Д2). Если поставленная задача достаточно сложна, то обосновывается разделение её на ряд более простых подзадач. Второй этап – алгоритмизация задачи играет ключевую роль в обеспечении эффективного решения задачи. На этом этапе составляется алгоритм решения задачи (подэтап А1), осуществляется отладка алгоритма (подэтап А2) на основе результатов его выполнения (подэтап А3). Для отладки используется специальный режим выполнения, в котором предусмотрены приостановки процесса выполнения и вывод текущих значений интересующих данных. Процесс отладки представляет собой процесс последовательных приближений (итераций): - вычисляются и выводятся интересующие данные для текущего варианта алгоритма; - на основе их анализа в алгоритм вносятся необходимые изменения и анализируются новые результаты. Этот процесс повторяется до тех пор, пока не будут устранены замеченные ошибки. Циклическое повторение подэтапов А1, А3, А2, А1,… характерно также в ситуации, когда функциональность алгоритма наращивается постепенно путем добавления новых блоков. В ходе отладки может возникнуть необходимость не только в коррекции алгоритма (переход от А2 к А1), но и в коррекции модели данных, вычислительной модели и общей организации процесса решения (возврат от А2 к первому этапу). Однако следует стремиться к уменьшению числа шагов приближения к правильно работающему алгоритму за счет более тщательной детализации постановки задачи и разработки самого алгоритма. В принципе, выполнение отлаженного алгоритма в режиме интерпретации (подэтап А3) позволяет получить требуемые результаты решения задачи. Третий этап – программирование задачи оказывается ненужным, если решение поставленной задачи уже получено на втором этапе. Однако в ряде случаев этот этап необходим, в частности: - когда получение результатов связано с большим объемом вычислений и выполнение его в режиме интерпретации алгоритма займет слишком много времени; - когда заказчику требуется не столько результаты решения, сколько исполняемый .exe-файл, с помощью которого он сам намерен получить требуемые результаты; - когда задача слишком сложна и возможностей алгоритмического интерпретатора недостаточно для её решения. Исходной информацией для третьего этапа служит алгоритм, разработанный на втором этапе. Готовый, отлаженный алгоритм нетрудно описать на одном из языков высокого уровня, с помощью соответствующей 9
системы программирования (подэтап П1). Компилятор и средства отладки, входящие в состав таких систем, позволяют отладить программу (подэтапы П3, П2). Процесс отладки в данном случае существенно облегчается, поскольку связан только с устранением ошибок программирования заведомо работоспособного алгоритма. Исполняемый файл программы, полученный в результате компиляции, либо сам по себе может служить решением поставленной задачи, либо с его помощью обеспечивается получение конкретных результатов решения (подэтап П4).
Неформалізована постановка задачі ДЕТАЛІЗАЦІЯ ПОСТАНОВКИ ЗАДАЧІ Д1
Створення інформаційної моделі
Д2
Попередній опис процесу вирішення задачі
АЛГОРИТМІЗАЦІЯ ЗАДАЧІ А1 А2
Розробка алгоритму вирішення задачі А3
Налагоджування алгоритму
ПРОГРАМУВАННЯ П1 П2
Виконання алгоритму в режимі інтерпретації
ЗАДАЧІ
Опис алгоритму на мові програмування високого рівня П3
Налагоджування програми
П4
Компіляція програми
Виконання скомпільованої програми
Результати вирішення задачі Рис. 1.3 Етапи вирішення прикладної задачі
В заключение следует ещё раз подчеркнуть важность алгоритмизации задачи. К сожалению, очень часто тщательной проработкой алгоритма пренебрегают и после первого этапа – детализации постановки 10
задачи – переходят сразу к третьему этапу – программированию. Не имея четко формализованного описания задачи в виде алгоритма, процесс программирования приобретает характер «проб и ошибок» и напоминает изготовление какого-либо изделия из узлов и деталей, размеры и взаимодействие которых «подгоняются» по ходу дела. В данном случае написание программного кода и отладка фрагментов программы тесно переплетаются и неизбежно приходится удалять ошибочные фрагменты программы и писать новый код. Очевидно, что суммарное время написания программы tП и ее отладки tНП увеличивается при усложнении задачи, причем увеличение времени tП + tНП значительно больше, чем пропорциональная зависимость от общего объёма программы. Такой вариант решения задачи схематически показан на рис.1.4а, где tД – время, затраченное на детализацию задачи. обсяг програми tД tП + tНП
обсяг програми tД tА
час а) Без розробки алгоритму
tНА tП* tНП* час б) З попередньою розробкою алгоритму
Рис. 1.4 Ефективність програмування Обозначим tА – время, затраченное на разработку алгоритма, и tНА – на его отладку. В этом случае график зависимости объёма кода от времени можно схематически изобразить в виде рис. 1.4б. Суммарное время программирования для этих вариантов подчиняется соотношению * tП + tНП > t А + tНА + tП* + tНП
(1.5)
В пользу справедливости данного соотношения можно привести следующие доводы. -
Составление программы по готовому алгоритму требует значительно меньше времени (tП*), поскольку, вопервых, на языках высокого уровня легко реализуются основные алгоритмические структуры (которые в алгоритме уже созданы) и, во-вторых, не нужно выбрасывать фрагменты программ и заново их писать. Поэтому зависимость объёма кода от времени является монотонно возрастающей.
-
Затраты времени на отладку (tНП*) также существенно сокращаются, поскольку для работоспособного алгоритма остается исправить лишь ошибки программирования.
-
Графическое представление алгоритма в виде блок-схемы более наглядно отображает его структуру, чем программа в виде текстового документа. Поэтому синтезировать алгоритм легче (значит и быстрее) в виде блок-схем, чем в виде текста.
-
Поскольку в программе, которая составляется без четкого алгоритма, в конце концов, он реализуется «на ощупь», то на это требуется значительно больше времени, чем на целенаправленный синтез и отладку алгоритма (tА + tНА).
Таким образом, суммарное время в левой части выражения (1.5) фактически включает в себя те же составляющие, что и в правой части, но каждая из них увеличивается в результате неэффективной организации труда. «Экономя» на составлении алгоритма, разработчик программы значительно проигрывает в общем затраченном времени. Этот проигрыш может быть многократным и по мере усложнения задачи стремительно увеличивается. Ввиду важности грамотной алгоритмизации задачи, вопросам создания алгоритмов в дальнейшем будет уделено первоочередное внимание. 1.4 Автоматизированная разработка алгоритма Возможность получения результатов решения прикладной задачи без традиционного программирования имеет большое значение как с практической, так и с принципиальной точек зрения. В практическом плане обеспечивается значительная экономия времени за счет исключения слагаемых tП* и tНП* в правой части неравенства (1.5). Однако в принципиальном отношении более ценным является то обстоятельство, что разработчик, освобождаясь от технической работы (написание программы по готовому алгоритму), может уделить основное внимание сугубо творческому процессу – созданию алгоритма. Именно развитие алгоритмического мышления является главной целью рассматриваемой темы «Основы алгоритмизации и программирования». Упомянутая выше система АЛГОРИТМ предназначена для облегчения процесса создания алгоритма путем автоматизации основных составных частей этого процесса: построения алгоритма, его анализа, отладки, редактирования, выполнения и отображения полученных результатов. Последовательность работы в системе АЛГОРИТМ, в целом, совпадает с последовательностью разделов главного меню (см. рис. 1.2). Каждый из разделов состоит из нескольких подразделов (команд). Подразделы, которые наиболее часто используются, дублируются инструментальными кнопками, расположенными под главным меню. Кнопки разделены на 6 функциональных групп.
11
• • • • • • •
Создание нового алгоритма производится в следующей последовательности. Вначале выполняется описание используемых переменных, массивов и функций, а также объявление процедур, если решаемую задачу можно разделить на несколько подзадач (вторая группа инструментальных кнопок). Затем производится синтез алгоритма путем ввода блоков, задания связей между ними и построения соединительных линий (третья группа кнопок). После создания алгоритма требуется выполнить анализ правильности его структуры (четвертая группа кнопок). Если ошибки в структуре алгоритма не обнаружены, может быть запущено выполнение алгоритма в обычном или отладочном режиме. В данном разделе меню обеспечивается также отображение результатов выполнения в символьной и графической форме (шестая группа кнопок). При необходимости отладки алгоритма могут быть заданы точки останова и перечень данных, значение которых необходимо контролировать (пятая группа кнопок). Для устранения замеченных ошибок обеспечивается возможность редактирования как блок-схемы алгоритма, так и всех видов описаний. С помощью раздела меню файлы (первая группа инструментальных кнопок) задается режим создания нового алгоритма, ввод из файла ранее созданного алгоритма или сохранение в файле текущего алгоритма.
В последующих разделах подробно описываются особенности создания алгоритма, типы данных и разновидности алгоритмических структур. 2.
ПРОСТЫЕ ТИПЫ ДАННЫХ
Как было показано в разделе 1, модель данных является неотъемлемой составной частью информационной модели. При составлении алгоритма решения прикладной задачи в первую очередь требуется четко формализовать используемые данные, то есть, описать их имена, типы и начальные значения. Рассмотрим здесь простые типы данных, которые наиболее широко используются на практике и с которыми оперирует система АЛГОРИТМ. Сложные структуры данных рассмотрим в разделе ?. 2.1
Двоичные данные
Вследствие особенностей физических процессов, которые происходят в компьютере, его процессор, запоминающие устройства и устройства ввода-вывода непосредственно оперируют с двоичными данными. Минимальный элемент таких данных называется бит и может принимать только два значения: 0 или 1. Данные, состоящие из двух битов, могут принимать 2⋅2 = 4 значения (00, 01, 10 и 11). В общем случае двоичные данные, содержащие n бит, могут принимать 2n различных значений. Эти значения можно записать в виде n-разрядного двоичного числа, младший разряд которого (правый) имеет номер 0, а старший разряд (левый) имеет номер n – 1. Подобно любой позиционной системе счисления, значение двоичного числа складывается из суммы значений всех его разрядов, умноженных на весовые коэффициенты разрядов. В данном случае i-й разряд имеет весовой коэффициент 2 i. Например, 4-разрядное двоичное число 0101 имеет значение в десятичной системе счисления 0⋅23 + 1⋅22 + 0⋅21+ 1⋅20 = 0 + 4 + 0 + 1 = 510 . Современные персональные компьютеры (ПК) имеют, как правило, 32-разрядные процессоры. Они могут оперировать не только с 32-разрядными данными, но и с данными, состоящими как из меньшего числа разрядов (например, 8, 16), так и с данными, состоящими из большего числа разрядов (например, 64 и более). Общепринятой единицей объема двоичных данных является байт, который состоит из 8 бит. Таким образом, данные, объем которых равен одному байту, могут иметь 2 8 = 256 значений с десятичными эквивалентами от 0 до 255. Каждая адресуемая ячейка памяти также имеет объем, равный одному байту. Объем памяти, равный 1 килобайт (1kB), содержит 210 = 1024 байта, 1 мегабайт (1MB) содержит 220 = 1048576 байт, 1 гигабайт (1GB) содержит 230 = 1073741824 байта. В памяти компьютера различные данные записываются, как правило, непосредственно друг
aА 0
aБ 1
0
0
дані А
1
1
1
0
0
aВ 1
1
1
0
0
1
1
дані Б Рис. 2.1 Розташування даних у пам'яті комп'ютера
1
0
1
0
0
0
1
0
0
1
0
0
1
0
1
1
дані В 12
за другом, образуя длинную последовательность 0 и 1, например, приведенную на рис. 2.1.
-
Для правильной записи, считывания и обработки данных компьютеру необходимо указать: адрес ai соответствующих i-х данных; тип данных и объем в байтах.
Адрес данных задается в виде так называемого указателя, который представляет собой номер ячейки памяти, начиная с которой размещаются данные. Однако в современных системах программирования обращение к данным осуществляется не с помощью адресов, а с помощью имен. Имена должны различаться для различных данных даже одного и того же типа. При решении конкретной прикладной задачи система программирования составляет внутренние таблицы имен, в которых для каждого имени записывается указатель на соответствующие данные. Тип данных и занимаемый ими объем памяти необходимы для правильной обработки данных, поскольку в общем случае нет однозначного взаимного соответствия между типом и объемом данных. В некоторых случаях данные одинакового типа могут иметь разный объем (например, массивы и строки). И, наоборот, одинаковый объем могут иметь данные разных типов. Например, двоичные данные 01000001 объемом один байт могут обозначать: - целое число 65; - большую букву латиницы «A»; - один из цветов какой-либо точки экрана дисплея (пиксела) при 8-битном кодировании цвета (палитра из 256 цветов). Поэтому При детализации постановки задачи необходимо обязательно задать имена и типы используемых данных, а также в некоторых случаях их начальные значения. 2.2
Правила именования данных, функций и процедур
Как отмечено в п. 2.1, всем используемым данным должны быть присвоены уникальные имена (некоторые исключения указаны в п. ?). При этом необходимо соблюдать следующие правила. • • • •
В соответствии с традициями математики в именах принято использовать латинские буквы от A до Z, кроме того, допускается использование цифр от 0 до 9 и знака подчеркивания “_”. Первым символом имени должна быть буква или знак подчеркивания. Никакие другие символы, в том числе “пробел”, недопустимы. Буквы могут быть как строчными (a .. z), так и прописными (A .. Z), причем в имени важны сами буквы, а не их регистр (размер). Длина имени не имеет значения, однако на практике вполне достаточно ограничиться 6-8-символьными именами. В системе АЛГОРИТМ длина имени не должна превышать 8 символов. Имена не должны совпадать со служебными (зарезервированными) словами, набор которых в различных системах программирования различен, а также со словами, которые описывают значение данных, например, True, False, Pi (число «пи»), Random (случайное число).
Указанные правила относятся также и к именам функций и процедур, которые создаются самим пользователем. Примеры правильных имен X
ALPHA Vector
a01 nes_f
_fnc
Rez_Obch
Примеры неправильных имен: 2num – имя начинается с цифры; str,name – используется недопустимый символ «запятая»; kin zn – внутри имени содержится «пробел» (недопустимы пробелы также в начале и в конце имени). 2.3
Числовые данные Рассмотрим наиболее распространенные типы данных, значениями которых являются числа.
2.3.1 Целочисленный тип Существуют разновидности целочисленного типа, занимающие в памяти 1, 2, 4 или 8 байт. Наиболее распространенной можно считать 4-байтовую разновидность (32 двоичных разряда). Старший разряд рассматривается как признак знака: 0 – положительное число, 1 – отрицательное число. Оставшиеся разряды определяют диапазон значений, который в данном случае составляет от –2147483648 до +2147483647.
13
В описании данных будем обозначать целочисленный тип как :I (эквивалент типа Integer в языке Object Pascal). Пример описания целочисленной переменной с именем «a0» и начальным значением –25: a0:I=–25 При описании положительных чисел знак «+» принято опускать. Целочисленный тип используется в случаях, когда данные принципиально не могут иметь дробные значения, например, число учеников в классе, номер ученика в журнале, порядковый номер буквы в алфавите, число повторений некоторого циклического процесса и т.п. Часто этот тип используется и для представления целой части данных, которые имеют также дробную часть, но для которых важна именно целочисленная часть, а не дробная. Например, интервал времени не относится к данным целого типа. Однако если необходимо указать в каком интервале произошло некоторое событие (не уточняя, в какой момент конкретно), то можно использовать целое число. Например, год рождения 1985, месяц 10, день 23. 2.3.2 Вещественный тип Вещественный тип используется для представления чисел, которые могут иметь не только целую, но и дробную части. Такие данные обычно занимают в памяти 4 или 8 байт, записываются в экспоненциальной форме и называются также числами с плавающей точкой. Экспоненциальная форма означает, что число представлено двумя составными частями – мантиссой и порядком. В случае 8-байтового числа 64 разряда распределены следующим образом: 63-й (старший) разряд – знак порядка; 53 .. 62-й разряды (10 разрядов) – значение порядка; 52-й разряд – знак мантиссы; 0 .. 51-й разряды (52 разряда) – значение мантиссы. Такое представление позволяет оперировать с числами, абсолютное значение которых находится в диапазоне примерно от 5⋅10–324 до 1.7⋅10308 и содержит не менее 15 значащих цифр. В описании данных будем обозначать вещественный тип как :R (эквивалентный типу Double в языке Object Pascal). Вещественные числа можно вводить в виде обычного десятичного числа, в котором целая и дробная части отделяются символом «.» (точка, но не запятая), например, –0.0012 21.037. Слишком маленькие или большие числа удобно вводить в экспоненциальной форме, где десятичный порядок обозначается латинской буквой E (или e). Например, приведенные выше числа могут быть введены в форме –1.2Е–3 (или 12Е–4)
2.1037Е1 (или 21037Е-3).
Какие-либо другие символы (в том числе, «пробел») при вводе вещественных чисел недопустимы. Вещественный тип применяется во всех случаях обработки числовых данных, когда нельзя использовать целочисленный тип. 2.4
Простые нечисловые данные
2.4.1 Логический тип Логические данные могут принимать только два значения: true (истина) и false (ложь). В принципе, для их кодирования достаточно одного двоичного разряда, однако вследствие особенностей адресации памяти в современных системах программирования для них отводится 2 байта. Значение false кодируется нулями во всех разрядах, а значение true – единицами во всех разрядах. При сокращенном представлении логических данных значению false сопоставляется 0, а значению true сопоставляется 1. В описании данных будем обозначать логический тип как :B (эквивалент типа Boolean в языке Object Pascal). При вводе значений логических данных слова true и false можно вводить как маленькими, так и большими буквами. Логический тип применяется в логических выражениях и для проверки, по какому из двух возможных путей следует продолжать выполнение алгоритма. 2.4.2
Символьный тип
Символьный тип применяется для кодирования текстовой информации. В большинстве случаев для запоминания одного символа в памяти компьютера выделяется 1 байт, то есть, можно использовать 256 различных символов с десятичными номерами от 0 до 255. Символы с кодами от 0 до 31 являются 14
управляющими. Они не имеют печатных эквивалентов и соответствующих клавиш на клавиатуре (за исключением клавиш: Backspace – код 8, Tab – код 9, Enter – код 13). Символы с кодами 32 .. 127 являются интернациональными, соответствуют стандарту ASCII (American Standard Code for Information Interchange) и включают в себя буквы латиницы, цифры, знаки препинания, знаки арифметических операций и ряд дополнительных символов. Они выводятся на экран дисплея и печать и вводятся с клавиатуры нажатием соответствующих клавиш или комбинации Shift + клавиша. Символы с кодами 128 .. 255 являются национальными и зависят от так называемой кодовой страницы (Code Page, сокращенно – CP), которую поддерживает операционная система. Среди них находятся и символы кириллицы. К сожалению, в настоящее время нет единого стандарта кодировки кириллицы. В операционной системе MS-DOS наиболее часто используется кодовая страница CP866, а в системе Windows – CP1251. При передаче текстовой информации через Интернет иногда используется кодовая таблица КОИ-8. Узнать, каким конкретно кодам соответствуют различные символы, нетрудно с помощью функции Chr (см. п. ?). В описании данных будем обозначать символьный тип как :C (эквивалент типа Char в языке Object Pascal). Для устранения неудобств, связанных с применением различных национальных кодовых страниц, в последнее время разработан новый международный стандарт Unicode. Этот стандарт предусматривает кодирование символов 16-разрядным кодом (двумя байтами), то есть, позволяет использовать 2 16 = 65536 различных символов. Однако он еще не получил широкого распространения.
2.5 Данные с однородной структурой Рассмотренные выше простые типы данных с точки зрения операций их обработки выступают в роли переменных под именами, которые им присвоены. Обращаясь к ним по имени, получаем одно какое-либо значение из допустимого диапазона значений. Соответственно в памяти компьютера для таких переменных отводится столько байт, сколько требуется конкретному типу данных (из рассмотренных типов максимум 8 байт для вещественного типа :R). В отличие от переменных, имя данных с однородной структурой сопоставляется с множеством значений, однако все элементы множества имеют одинаковый тип. Если для хранения данных некоторого типа требуется B байт памяти, а множество состоит из M элементов, то для таких данных отводится в памяти N = M⋅B байт. Данные с однородной структурой обеспечивают доступ ко всему множеству своих значений, а также к значениям отдельных элементов. Рассмотрим два типа таких данных: массивы и строки. 2.5.1
Массивы
Массивы задаются своим именем, типом элементов и способом организации элементов во множество. Каждый элемент массива может использоваться в различных выражениях точно так же, как и переменная соответствующего типа. Имя массива подчиняется описанным выше правилам (см. подраздел 2.2). В большинстве случаев (и в данном учебнике) тип элемента принадлежит к одному из простых типов данных (:I, :R, :B, :C). Допускается также тип строка, который рассматривается ниже в п.2.5.2. По способу организации массивы разделяются на 1мерные, 2-мерные и т.д. В одномерном массиве все элементы располагаются друг за другом. Элемент массива определяется следующим образом ИмяМассива[Индекс]. Индексом может служить: целое число, целочисленная переменная, в общем случае – арифметическое выражение, имеющее целочисленное значение. Индекс в одномерном массиве является просто порядковым номером элемента. Нумерация индекса начинается с 1. В двумерном массиве элемент определяется двумя индексами ИмяМассива[Индекс1,Индекс2]. Содержимое двумерного массива можно рассматривать как матрицу, где Индекс1 указывает номер строки, а Индекс2 – номер столбца. В математических выражениях элемент некоторой матрицы A, который находится на пересечении i-й строки и j-го столбца обозначается как Ai,j, что соответствует однострочной записи A[i,j]. Пусть массив A является матрицей из 2-х строк и 3-х столбцов
15
A1,1 A1,2 A = A2,1 A2,2
A1,3 A2,3
Элементы этого массива размещаются в памяти в следующей последовательности A[1,1] A[2,1] A[1,2] A[2,2] A[1,3] A[2,3]. В трехмерном массиве элемент определяется аналогично ИмяМассива[Индекс1,Индекс2,Индекс3]. В общем случае элементы располагаются в памяти так, что при последовательном переходе от одного элемента к следующему быстрее всех изменяется первый из индексов, затем второй и т.д. Примеры правильных ссылок на элементы массивов: m[10]
x1[i,j]
RANG[i1,h+k,n].
Примеры неправильных ссылок на элементы массивов: M1(2,11) – индексы должны заключаться в квадратные скобки; M1[2;11] или M1[2.11] – индексы должны разделяться запятыми; M1[6/3,11] – индексы должны иметь целочисленные значения, а выражение 6/3 имеет вещественный тип (хотя численно равно 2). При обращении к элементам массива необходимо следить за тем, чтобы значения индексов были не меньше 1 и не превышали заданных максимальных значений для каждой размерности. 2.5.2 Строки Строковый тип данных является по существу одномерным массивом, элементами которого являются символьные данные (тип :C). От обычного массива символов этот тип данных отличается динамическим размером. То есть, размер строки может изменяться в процессе выполнения алгоритма от «пустой» строки, содержащей 0 символов, до 65535 символов. Элемент строки задается как обычный элемент массива ИмяСтроки[Индекс]. Однако со строковыми данными можно выполнять ряд операций, которые запрещены для массивов. Поэтому строки принято рассматривать как своеобразную разновидность переменной, которую можно индексировать. В описании данных будем обозначать строковый тип как :S (эквивалент типа String в языке Object Pascal). Значение строковой переменной вводится как последовательность символов, которая начинается и заканчивается символом апострофа ( ‘ – символ с кодом 39), например ‘Значение строковой переменной’. Начальный и конечный апострофы в число элементов не включаются, то есть, длина строки с учетом пробелов в данном случае равна 29 символов. При необходимости ввода самого символа апострофа как элемента строки следует ввести его два раза подряд (в состав строки он входит как один символ). “Пустая” строка вводится тоже как два апострофа подряд. 2.6 Описание данных Как было отмечено в подразделе 1.3, первым шагом при создании нового алгоритма является описание используемых данных на основе модели данных, составленной на этапе детализации постановки задачи. Вся совокупность данных образует две группы – переменные и массивы –, описание которых выполняется раздельно. 2.6.1
Описание переменных
В качестве переменных выступают данные пяти типов: целочисленные (:I), вещественные (:R), логические (:B), символьные (:C) и строковые (:S). В системе АЛГОРИТМ описание переменных выполняется с помощью диалогового окна «Список переменных», которое открывается одним из способов: • С помощью меню «Описание | Переменных…» (что означает выбор в главном меню раздела «Описание», затем выбор подраздела «Переменных…»). • Щелчком на инструментальной кнопке «Описание переменных». • Нажатием функциональной клавиши F5. Диалоговое окно «Список переменных» показано на рис.2.2. 16
Рис. 2.2 Вікно опису змінних В строку ввода «Имя» вводится имя переменной в соответствии с правилами, приведенными в подразделе 2.2. В строку ввода «Значение» вводится начальное значение переменной. При этом в режиме автоматического определения типа (который установлен по умолчанию) тип переменной определяется непосредственно по информации, введенной в эту строку. Например, если введены цифры без десятичной точки, то переменной присваивается целочисленный тип (:I), если же цифры разделены десятичной точкой или символом десятичного порядка Е, то переменной присваивается вещественный тип (:R). Можно выключить режим автоматического определения типа и задать его явно, выбрав из списка «Выбор типа». Введенная переменная добавляется в список с помощью кнопки «Добавить». При этом буквы ее имени преобразуется в большие и переменные упорядочиваются по алфавиту. Если в списке выделить какую-либо переменную, щелкнув мышкой на ее имени, то активизируются кнопки «Удалить» и «Изменить», с помощью которых можно отредактировать список. После выполнения алгоритма в окне «Список переменных» отображаются их окончательные значения, что в ряде случаев позволяет отказаться от оператора вывода непосредственно в алгоритме. На рис. 2.2 показан список переменных, которые необходимы для решения задачи 1.1 (см. п.1.2.2). В этом списке B, MB, N, SB – целочисленные переменные с начальным значением 0, а SN, SP – строковые переменные со значением «пустая строка». 2.6.2 Описание массивов Описание массивов в системе АЛГОРИТМ выполняется с помощью диалогового окна «Список массивов», которое открывается одним из следующих способов: • С помощью меню «Описание | Массивов…». • Щелчком на инструментальной кнопке «Описание массивов». • Нажатием комбинации клавиш Ctrl+F5. Диалоговое окно «Список массивов» показано на рис. 2.3. Прежде всего с помощью раскрывающегося списка «Выбор типа» необходимо выбрать тип элементов массива, затем в строку ввода «Имя» ввести имя массива, а в строки ввода (с кнопками увеличения и уменьшения) «Индекс 1:» и т.д. ввести максимальное значение индекса по каждой размерности. Введенное описание добавляется к списку массивов с помощью кнопки «Добавить». При этом маленькие буквы имени трансформируются в большие и список упорядочивается по алфавиту. Число элементов массива не должно превышать 10000. Однако во избежание существенного увеличения времени обработки массивов рекомендуется использовать массивы, состоящие не более, чем из 1000 элементов. При щелчке мышью на какой-либо строке списка активизируются кнопки «Удалить», «Изменить» и «Показать». Первые две позволяют редактировать список массивов, а последняя обеспечивает доступ к элементам массива. При нажатии кнопки «Показать» в строки ввода «Индекс 1:» и т.д. следует ввести индексы интересующего элемента, затем щелкнуть на строке «Значение элемента» и в ней будет показано текущее значение выбранного элемента массива. Это значение при необходимости можно изменить. Выход из режима доступа к элементам массива осуществляется нажатием кнопки «Вывод закончить».
17
Рис. 2.3 Вікно опису масивів
3.
ОПЕРАЦИИ С ДАННЫМИ
Для преобразования данных в процессе решения задачи необходимо учитывать особенности, которые зависят от типа данных, и правильно организовать последовательность преобразований. Вначале уточним понятия, которые будут использоваться в дальнейшем. 3.1
Операции, операнды и выражения
Операции – действия, которые допустимы для преобразования определенных типов данных (например, сложение чисел). Операнды – «участники» операций (например, слагаемые операции сложения). Операндами могут быть: - непосредственно значения данных; - переменные; - элементы массивов; - функции; - выражения (см. ниже). Переменные, массивы и функции образуют три группы данных и должны быть предварительно описаны (в противном случае при выполнении алгоритма будет выдано сообщение об ошибке). Ссылки на них в каждой группе осуществляются по именам, которые должны удовлетворять правилам, описанным в подразделе 2.2. Еще раз напомним, что для избежания конфликтов между группами имена должны быть уникальными. Выражение – записанная в строчку последовательность операндов и операций, которая обеспечивает требуемое преобразование данных. Она должна учитывать старшинство операций и при необходимости использовать круглые скобки для указания очередности вычислений. При этом необходимо строго соблюдать правило – число открывающих и закрывающих скобок должно быть равным. При одинаковом старшинстве операций и отсутствии скобок выражение анализируется и выполняется слева направо. 18
Выражение следует рассматривать как вычислительный фрагмент алгоритма и стремиться по возможности оптимизировать вычисления. 3.2
Арифметические выражения
3.2.1 Арифметические операции В арифметических выражениях можно использовать следующие операции: + сложение; – вычитание; * умножение; / деление; ^ возведение в степень (основание^степень); : целочисленное деление, которое возвращает целое частное от деления целых чисел (эквивалентна операции DIV языка Паскаль); % целочисленный остаток от деления целых чисел (эквивалентна операции MOD языка Паскаль). Порядок старшинства операций: 1) Возведение в степень (^). 2) Унарный минус (–Х). 3) Умножение (*), деление (/), целочисленное деление (:) и целочисленный остаток (%). 4) Сложение (+) и вычитание (–). В операциях «+», «–», «*», «/», «^» могут участвовать как вещественные, так и целочисленные данные, причем операции «/» и «^» всегда возвращают вещественный результат. В операциях «:» и «%» могут участвовать только целочисленные данные. Результат вычислений имеет целочисленный тип только тогда, когда все операнды являются целочисленными и не используются операции «/» и «^». В остальных случаях результат имеет вещественный тип. Следует отметить, что операция возведения в степень (a ^ x = ax) выполняется путем логарифмирования и потенцирования, поэтому основание (a) должно быть положительным числом. Отрицательное основание допускается только при целочисленной степени (x). Для сокращения объема вычислений при возведении в квадрат и извлечении квадратного корня рекомендуется использовать библиотечные функции соответственно Sqr(x) и Sqrt(x) (см. п. 3.2.2). Приведенные ниже примеры иллюстрируют указанные правила. Математическая запись 2⋅3 4
Однострочное выражение 2*3/4/5 = 0.3
5 2 ⋅3
2*3/(4/5) = 7.5
4 5
a ⋅b c + c a ⋅b
a*b/c+c/(a*b)
x + y a2 ⋅ a1 x − y
(x+y)/a1*a2/(x–y)
a ⋅ x3 + b ⋅ x 2 + c ⋅ x + d
((a*x+b)*x+c)*x+d
Последний пример иллюстрирует оптимальный вариант вычисления полинома 3-й степени по сравнению с вариантом a*x^3+b*x^2+c*x+d, в котором такое же количество операций умножения и сложения, однако добавляются две сложные операции возведения в степень. Для более наглядного отображения арифметических выражений между операндами и операциями можно вставлять символ «пробел», например, a+b допустимо записывать как a + b. В приведенном наборе арифметических операций две последние не встречаются в школьном курсе математики. Поясним полезность целочисленных операций «:» и «%» на следующем примере. Пусть на автоматизированном складе товар размещается в ячейках стеллажа, который имеет 5
19
ярусов и 6 рядов, то есть всего 5⋅6 = 30 ячеек. Пронумеруем ярусы, ряды и ячейки, начиная с 0. Схема такого склада показана в виде таблицы на рис. 3.1. Предположим, что роботу задано достать товар из 17-й ячейки. Вместо того, чтобы последовательно перемещаться от ячейки к ячейке снизу вверх и слева направо, пока не будет найдена 17-я ячейка, можно сразу определить номер ряда и номер яруса, куда должен переместиться робот Номер ряда = 17 : 5 = 3
Номер ярусу
Номер яруса = 17 % 5 = 2.
Номер ряду 0
1
2
3
4
5
4
4
9
14
19
24
29
3
3
8
13
18
23
28
2
2
7
12
17
22
27
1
1
6
11
16
21
26
0
0
5
10
15
20
25
Рис. 3.1 Схема складського стелажа Аналогично можно определить, в каком подъезде многоэтажного дома, и на каком этаже находится интересующая квартира (с поправкой, что этажи, подъезды и квартиры нумеруются, начиная с 1). 3.2.2 Библиотечные математические функции Каждая система программирования имеет встроенную библиотеку основных математических функций, состав которой, однако, не стандартизован и может существенно отличаться. В библиотеку системы АЛГОРИТМ включены следующие функции (с указанием типа аргумента и возвращаемого результата). Abs(x:I,R):I,R – абсолютное значение аргумента x (целого или вещественного); ArcCos(x:R):R – арккосинус x (arccos x); ArcSin(x:R):R – арксинус x (arcsin x); ArcTan(x:R):R – арктангенс x (arctg x); ArcTan2(y:R,x:R):R – арктангенс угла, образованного между точкой (x,y), началом координат и осью абсцисс x с учетом квадранта; Cos(x:R):R – косинус x (cos x); Cotan(x:R):R – котангенс x (ctg x); Exp(x:R):R – экспонента x (ex); Ln(x:R):R – логарифм натуральный x (ln x); Sin(x:R):R – синус x (sin x); Sqr(x:R):R – квадрат x (x2); Sqrt(x:R):R – корень квадратный x (√x); Tan(x:R):R – тангенс x (tg x). Кроме указанных общеупотребительных функций в библиотеке имеются функции, выполняющие действия с целой и дробной частью вещественного аргумента x: Ceil(x:R):I – округление x до большего целого (Ceil(2.1) = 3, Ceil(–2.1) = –2); Floor(x:R):I – округление x до меньшего целого (Floor(4.7) = 4, Floor(–4.7) = –5); Frac(x:R):R – дробная часть x (Frac(3.4) = 0.4); Int(x:R):R – целая часть x (Int(3.4) = 3);
20
Round(x:R):I – округление x до ближайшего целого (Round(1.4) = 1, Round(1.5) = 2); Trunc(x:R):I – округление x в сторону 0 (Trunc(10.3) = 10, Trunc(–9.7) = –9). 3.2.3 Библиотечные специальные функции Для расширения вычислительных возможностей системы АЛГОРИТМ в состав библиотеки включены три специальные функции Ifa(ls,ts,fs) – условш ная функция с аргументами-значениями, где
ls – логическая переменная или логическое выражение (например, x > 0); ts – арифметическое выражение, значение которого данная функция возвращает, если ls имеет значение «истина» (true); fs – арифметическое выражение, значение которого данная функция возвращает, если ls имеет значение «ложь» (false). Тип возвращаемого значения соответствует типу значений аргументов ts, fs. В данном случае значения всех аргументов вычисляются в момент обращения к функции. То есть, функция Ifa( ) просто выбирает одно из двух значений ts или fs в зависимости от результата проверки значения ls. Например, с помощью Ifa( ) можно реализовать функцию, которая описывает единичную ступеньку
1, если x ≥ 0 Step (x ) = 0, если x < 0 В данном случае она имеет вид Ifa(x>=0,1,0). Iff(ls,_f1(x),_f2(x)) – условная функция с аргументами-функциями, где
ls – логическая переменная или логическое выражение; _f1(x) – функция пользователя, которая вычисляется, если ls имеет значение «истина»; _f2(x) – функция пользователя, которая вычисляется, если ls имеет значение «ложь». Аргументы функции должны иметь вещественный тип. Integral(x0,x1,_f(x)) – определенный интеграл, где
x0, x1 – нижний и верхний пределы интегрирования; _f(x) – подынтегральная функция пользователя, которая возвращает вещественное значение. Создание собственных функций пользователя рассматривается в ?.
3.3 Логические выражения Основными логическими операциями являются: ~ инверсия или логическое отрицание («НЕ» – эквивалентна операции NOT языка Паскаль); & конъюнкция или логическое умножение («И» – эквивалентна операции AND языка Паскаль); | дизъюнкция или логическое сложение («ИЛИ» – эквивалентна операции OR языка Паскаль). Операндами в указанных операциях являются логические данные. Кроме того, к логическим операциям относятся также операции отношения: = равно; > больше; < меньше; >= больше или равно; <= меньше или равно; <> не равно. Операндами операций отношения могут выступать числовые, символьные и строковые данные, однако пара операндов должна иметь одинаковый тип. Необходимо также отметить, что шесть операций отношения составляют три пары взаимно инверсных операций: Операция отношения
Инверсная операция
= > <
<> <= >=
Порядок старшинства операций: 1) Операция «~» имеет такой же порядок, как и унарный минус. 21
2) Операция «&» имеет такой же порядок, как и «*», «/», «:» и «%». 3) Операция «|» имеет такой же порядок, как и «+», «–». 4) Операции отношения имеют самый низкий приоритет. Для изменения порядка выполнения логических операций следует, как и в арифметических выражениях, применять круглые скобки. Все логические операции возвращают результат логического типа (true или false). Принято сопоставлять логическим данным числовые эквиваленты: значению false сопоставляется 0; значению true сопоставляется 1. Результаты выполнения логических операций иллюстрируются таблицами истинности, где А и В – операнды, а С – результат (все представлены числовыми эквивалентами логических величин).
Операция: А В
Инверсия С= ~A C= ~B
Конъюнкция C=A&B
Дизъюнкция C=A|B
0 0 1 1
1 1 0 0
0 0 0 1
0 1 1 1
0 1 0 1
1 0 1 0
При составлении логических выражений равносильности, известные как законы Моргана
могут
быть
полезными
соотношения
~ (A | B) = (~A) & (~B) и ~(A & B) = (~A) | (~B). Например, необходимо проверить истинность неравенств 20 ≤ x ≤ 30 для какого-то значения x. Эти неравенства справедливы, если x ≥ 20 и, кроме того, x ≤ 30. Соответствующее логическое выражение имеет вид (x >= 20) & (x <= 30). В соответствии с законом Моргана и очевидным соотношением ~(~A) = A его можно изменить (x >= 20) & (x <= 30) = ~(~(x >= 20) | ~(x <= 30)) = ~((x < 20) | (x > 30)). Таким образом, неравенства 20 ≤ x ≤ 30 истинны, если не имеет место соотношение x < 20 или x > 30. С помощью логических выражений и таблиц истинности можно, в принципе, описать функционирование всех элементов компьютера, оперирующих с двоичной информацией. В программировании логические выражения используются для проверок различных условий, от результатов которых зависит ход дальнейших вычислений.
3.4
Строковые выражения
В отличие от массивов, которые не допускают над собой никаких арифметических и логических операций, строки позволяют выполнять разнообразные преобразования. Из рассмотренных выше операций допустимы: + слияние; операции отношения (=, <, >, >=, <=, <>). Операция «+» позволяет к содержимому строки-левого операнда добавить содержимое строки-правого операнда. Пусть переменная S1 строкового типа содержит слово «Информатика». Тогда строковое выражение S1 + ‘ является одним из предметов’
(3.1)
возвращает строку, содержащую «Информатика является одним из предметов». В библиотеке математических функций системы АЛГОРИТМ имеется ряд функций для преобразования строковых и символьных данных. Chr(x:I):C – возвращает символ, код которого равен x. Copy(Str:S,Ind:I,Num:I):S – возвращает Num элементов строки Str, начиная с элемента с индексом Ind. Например, для приведенной выше строки S1 функция Copy(S1,1,6) возвратит строку «Информ». Delete(Str:S,Ind:I,Num:I):S – возвращает часть строки Str, из которой удалено Num элементов, начиная с индекса Ind. Пусть содержимое выражения (3.1) присвоено строковой переменной S2. 22
Тогда функция Delete(S2,22,8) удалит часть строки «одним из» и возвратит значение «Информатика является предметов». Пусть этот результат присвоен строковой переменной S3. Insert(Sub:S,Str:S,Ind:I):S – возвращает результат вставки подстроки Sub в строку Str, начиная с индекса Ind. Например, Insert(‘важным’,S3,22) возвратит значение «Информатика является важным предметов». Пусть этот результат присвоен строковой переменной S4. Length(Str:S):I – возвращает количество символов (длину) строки Str. Например, Length(S4) = 37. Для исправления последней буквы строки «в» на «м» достаточно элементу строки S4[37] присвоить символ «м» (то есть, ‘м’ – с учетом того, что при вводе символов и строк они должны начинаться и заканчиваться апострофами). Ord(Ch:C):I – возвращает целочисленный код символа Ch. Pos(Sub:S,Str:S):I – ищет вхождение подстроки Sub в строку Str и возвращает номер элемента (индекс), с которого начинается подстрока Sub. Если подстрока Sub не найдена, то функция возвращает 0. Strf(Val:V,W:I,D:I):S – преобразует число Val (целое или вещественное) в строку с форматированием. Аргумент W определяет общее число позиций для представления числа. Если W больше, чем число позиций, требуемых для представления числа, то лишние позиции (слева) заполняются пробелами. Аргумент D (только для вещественного числа) определяет число знаков, отображаемых после десятичной точки. При нулевых значениях W, D число Val преобразуется без форматирования с максимальной точностью. Valf(Str:S):V – преобразует строковый аргумент Str, который описывает целое или вещественное число, во внутреннее представление соответствующего типа (:I или :R). Подобно числам, строки могут участвовать в операциях отношения (=, <, >, <>, <=, >=). В этом случае строки анализируются слева направо посимвольно с учетом кодировки символов. Если одна строка короче другой, недостающим символам короткой строки присваивается значение #0. Например, справедливы следующие отношения (возвращают true): ‘A’ > ‘100’‘Алгоритм’ > ‘algorithm’
‘1’ < ‘?’.
Возможность применения операций отношения позволяет сортировать строковые данные, в частности, по алфавиту. 3.5 Функции пользователя При решении прикладной задачи может возникнуть необходимость многократно вычислять одно и то же выражение для различных значений некоторых операндов. В таких случаях целесообразно создать собственную функцию, аргументами которой являются изменяемые операнды, а сама функция описывается требуемым выражением. В системе АЛГОРИТМ предусмотрена возможность создания функций пользователя с помощью диалогового окна «Список функций», которое открывается одним из способов: • С помощью меню «Описание | Функций…». • Щелчком на инструментальной кнопке «Описание функций пользователя». • Нажатием клавиш Shift+F5. На рис. 3.2 показано окно для создания функций пользователя. Окно содержит две страницы, которые соответствуют функциям пользователя (эта страница активна по умолчанию) и библиотечным функциям. Страницы можно выбрать с помощью нижней закладки. Страница функций пользователя содержит: - пары строк «Имя» и «Тип» функции; - аналогичные пары строк для 1-го, 2-го и 3-го аргументов; - строку ввода «Комментарий»; - строку ввода «Выполняемые операции»; - окно списка созданных функций; - кнопки «Добавить», «Удалить», «Изменить», «Вычислить», «Вывод закончить» и «Закрыть». Переходить от строки к строке в указанной последовательности удобно с помощью клавиши Tab.
23
Рис. 3.2 Вікно для опису функцій користувача -
-
-
• • • • • •
При создании функций пользователя необходимо учитывать следующие особенности. Выражение, которое описывает функцию, должно содержать переменные, заданные как аргументы функции. Область действия переменных-аргументов ограничивается только функцией. То есть, другая (внешняя) функция с тем же именем, что и аргумент функции, не изменяет своего значения при вычислении функции. Тем не менее, переменные-аргументы должны быть описаны в списке переменных. Выражение, которое описывает функцию, может зависеть не только от аргументов этой функции, но и от любых внешних данных, которые рассматриваются как константы. Таким образом, ограничение на максимальное число аргументов (3) легко обойти. В некоторых случаях в роли аргумента может выступать ссылка на другую функцию. В этом случае имя функции, которая подставляется в качестве аргумента, должно начинаться со знака подчеркивания «_». Описание функции выполняется в следующей последовательности. Введите имя функции в строку ввода «Имя». Выберите тип результата, который возвращается функцией, из строки выбора «Тип». Аналогично введите имя и тип каждого аргумента. В строку «Выполняемые операции» введите выражение, которое должно вычисляться функцией. Введите краткое пояснение в строку «Комментарий» (необязательно). Щелкните на кнопке «Добавить».
При щелчке мышью на одной из введенных функций в окне списка, активизируются кнопки «Удалить», «Изменить» и «Вычислить», с помощью которых можно отредактировать функцию или вычислить ее значение. Действие этих кнопок аналогично соответствующим кнопкам в окне «Список массивов». На рис. 3.2 показано описание функции f(b), которая используется при решении задачи 1.1. Она возвращает значение 0, если аргумент b = 0, и 1 в противном случае. 3.6 Проверка выражений Овладение навыками правильного составления арифметических и других выражений является одним из обязательных условий эффективного программирования. Для развития таких навыков в системе АЛГОРИТМ предусмотрена возможность автономного составления выражений и проверки результатов их вычислений еще до использования выражений непосредственно в
24
алгоритме. Эта возможность реализуется с помощью диалогового окна «Ввод и вычисление выражений», которое открывается одним из способов: • Из меню «Описание | Выражений…». • Нажатием комбинации клавиш Shift+Ctrl+F5. На рис. 3.3 показан вид окна для описания выражений.
Рис. 3.3 Вікно для введення та обчислення виразів Данное окно содержит: строку для ввода выражения; строку для вывода результата вычисления; текстовую метку «Тип», в которой отображается тип полученного результата; кнопки «Вычислить» и «Закрыть». Если в проверяемом выражении участвуют какие-либо переменные, элементы массивов, функции, то они должны быть предварительно описаны (см. подразделы 2.6 и 3.5). Приведенный рисунок иллюстрирует проверку отношения двух строковых данных и полученный логический результат. -
4.
АЛГОРИТМЫ
Понятие алгоритма, его основные свойства и обоснование важности алгоритмического описания прикладной задачи были рассмотрены в подразделах 1.2 и 1.3. Данный раздел посвящен изучению основных алгоритмических структур, а также рассмотрению особенностей практической работы с алгоритмами. 4.1
Блок-схема алгоритма
4.1.1 Графическое представление блок-схемы В разделе 1 на рис. 1.1 приведена в качестве примера блок-схема алгоритма решения задачи вычисления среднего балла учеников (см. постановку задачи в п.1.2.2). Каждый блок соответствует либо выполнению определенных действий с данными (ввод и вывод, преобразование данных и др.), либо выполнению служебных функций, обеспечивающих организацию этих действий (начало, конец, выделение циклических фрагментов и др.). Последовательность выполнения блоков задается соединительными линиями между ними. Направление соединительных линий «сверху вниз» и «слева направо» по умолчанию считается естественным и не требует применения стрелок. Каждый блок в блок-схеме условно занимает одну клетку некоторой «таблицы». На рис. 1.1 блок схема изображена в виде одной колонки «таблицы», содержащей 13 клеток. Подобным образом может быть представлена блок-схема любого алгоритма. Однако для более эффективного использования площади экрана целесообразно отображать блок-схему в виде 2 – 3 колонок блоков в зависимости от разрешающей способности дисплея. При этом следующий блок по отношению к предыдущему должен размещаться ниже предыдущего, а не справа от него (за исключением перехода от конца предыдущей к началу следующей колонки). Блок-схема на рис. 1.1 соответствует стандартному представлению, когда внутри блока указывается действие, выполняемое блоком. Однако на практике описание действия может быть не только кратким, но и весьма громоздким. Для того чтобы избежать использования блоков разных размеров (такая блок-схема «неэстетичная» и затрудняется ее автоматическая прорисовка), допускается размещать описание выполняемого действия справа от блока. Для четкого позиционирования блоков и их описаний поле алгоритма в системе АЛГОРИТМ имеет вид, показанный на рис. 4.1 (для двух колонок блоков). 1-а колонка блоків
2-а колонка блоків
1-а колонка описів
2-а колонка описів
Блок 1
Блок k+1
Опис блоку 1
Опис блоку k+1
25
Блок 2
----------
Опис блоку 2
-------------
------
Блок N
------------
Опис блоку N
Блок k
Опис блоку k
Рис. 4.1 Схематичне подання поля алгоритму у вигляді таблиці Число строк «таблицы» не ограничено, то есть, алгоритм может состоять из достаточно большого числа блоков. Когда фактическое число строк превышает число видимых строк, появляется полоса вертикальной прокрутки. Аналогично, если в поле алгоритма не помещаются все колонки «таблицы», появляется полоса горизонтальной прокрутки. В каждой клетке описания блока можно отобразить 3 текстовых строчки: в 1-й строчке – условное обозначение блока (см. ниже) и краткий комментарий, во 2-й и 3-й строчках – собственно описание действий, выполняемых блоком (описание оператора). Такое представление блок-схемы позволяет: - сохранить наглядность отображения структуры алгоритма; - одновременно обеспечить полноту функционального описания каждого блока. Кроме того, в зависимости от состояния блока и режима отображения изменяется цвет заливки блока, что улучшает наглядность отладки и облегчает редактирование алгоритма. Рис. 1.2 иллюстрирует «табличное» представление стандартной блок-схемы, изображенной на рис. 1.1. В дальнейшем блок-схему алгоритма для краткости будем называть просто алгоритмом. Это вполне оправдано, поскольку блок-схема полностью описывает всю совокупность действий, необходимых для решения поставленной задачи. 4.1.2 Графические символы блоков Для наглядного представления функционального назначения графические символы блоков, перечень которых приведен на рис. 4.2.
№
Символ
1
блоков
принято
использовать
Назва
Призначення
ТЕРМІНАТОР
Позначає початок та кінець виконання алгоритму або його окремого фрагмента, наприклад, процедури
ПРОЦЕС
Позначає виконання операцій, таких як переміщення даних та обчислення виразів, що не потребують розгалуження або циклічних повторень
ДАНІ
Позначає введення або виведення даних
РІШЕННЯ
Позначає вибір одного з двох можливих варіантів продовження роботи алгоритму в залежності від істинності чи хибності умови, яка перевіряється
XXX 2 XXX
3 XXX 4 XX X 5
XXX
ПОЧАТОК ЦИКЛУ
6
XXX
КІНЕЦЬ ЦИКЛУ
Використовується для створення циклічної структури алгоритму, яка забезпечує повторне виконання одного чи декількох блоків, розташованих між символами початку та кінця циклу
26
7
ПРОЦЕДУРА
X XXX
Позначає перехід до виконання алгоритму на іншому рівні ієрархії, після чого управління передається наступному блоку
Рис. 4.2 Перелік графічних символів блоків Внутри и снаружи графических символов указываются параметры блоков, которыедетализируют тип блока и определяют взаимные связи. Необходимо отметить, что принципиально важными являются лишь три символа – ПРОЦЕСС, РЕШЕНИЕ и ПРОЦЕДУРА. Остальные символы фактически служат для улучшения «читабельности» блоксхемы: -
символ ДАННЫЕ выделяет операции обмена данными с внешними устройствами (клавиатурой, дисплеем и др.), оставляя за символом ПРОЦЕСС обмен данными между процессором и памятью компьютера;
-
символы НАЧАЛА и КОНЦА ЦИКЛА введены сравнительно недавно с целью упростить отображение циклических структур алгоритма за счет исключения соединительных линий от конца цикла к началу. В принципиальном отношении для организации циклов достаточно символа РЕШЕНИЕ;
-
символ ТЕРМИНАТОР применительно к алгоритму в целом выполняет лишь функции указания, откуда начинается и где заканчивается выполнение алгоритма. Вместе с тем, применительно к фрагменту алгоритма – процедуре – терминатор НАЧАЛО процедуры содержит описание данных, которые служат для связи процедуры с вызывающим алгоритмом.
Помимо указанных символов, применяются и другие, которые детализируют выполняемые действия. Однако в рамках задач, которые рассматриваются в учебнике, вполне достаточно приведенных семи символов. 4.1.3
Параметры блоков
Для однозначной увязки графического изображения блока (в колонке блоков) и описания выполняемых действий (в колонке описаний) используется условное обозначение блока (УОБ), которое на рис. 4.2 показано в виде ХХХ. Оно состоит из буквы (букв), указывающей тип блока, и цифр – порядкового номера в группе блоков соответствующего типа. Например, А2 – 2-й блок, выполняющий оператор присваивания. Для указания взаимных связей блоков задаются параметры: - mb – метка, то есть, уникальный номер блока в алгоритме; - msb – адрес перехода, то есть, метка блока, который должен выполняться после данного («далее»); - msb1 – альтернативный адрес перехода в блоке РЕШЕНИЕ («иначе»). Указанные параметры отображаются рядом с блоком, как показано на рис. 4.3.
mb
УПБ
mb msb
Нmsb1 УПБ Дmsb
mb
Дmsb1 УПБ Нmsb
Рис. 4.3 Параметри блоків Блок РЕШЕНИЕ имеет два выхода: нижний («далее») и боковой («иначе»). Адреса переходов, расположенные справа от блока, соответствуют: - нижний адрес – нижнему выходу; - верхний адрес – боковому выходу (который может располагаться справа или слева, что автоматически определяет система АЛГОРИТМ). Первая буква в адресах перехода соответствует выполнению «Д» (да) или невыполнению «Н» (нет) проверяемого условия. По умолчанию нижний выход блока РЕШЕНИЕ помечается буквой «Д», то есть, переход «далее» осуществляется при выполнении проверяемого условия, а переход «иначе» от бокового выхода – при невыполнении условия. При необходимости поменять местами переходы можно поступить двумя способами: - оставить прежними назначение нижнего выхода (Д) и бокового выхода (Н), но изменить проверяемое условие на противоположное, например, использовать инверсную операцию отношения (см. подраздел 3.3); - оставить неизменным проверяемое условие, но изменить назначение выходов (нижнего – на «Н», бокового – на «Д») путем редактирования блока с помомощью диалогового окна «Характеритстики и описание блока» (см. п.4.1.5).
27
Метки блоков (mb) формируются автоматически при вводе блоков, а адреса переходов устанавливаются при задании связей блоков (см. п.4.1.5). На основе заданных связей осуществляется автоматическое построение соединительных линий. 4.1.4
Типы блоков Каждый блок реализует какой-либо оператор.
Оператором называется описание совокупности действий, которые выполняет отдельный блок алгоритма. Все возможные операторы разделяются на группы, которые соответствуют графическим символам блоков. Вместе с тем, одному и тому же символу может соответствовать несколько различных операторов, то есть, одно и то же графическое начертание могут иметь различные типы блоков. Для облегчения распознавания типов блоков (операторов) служит условное обозначение блока (УОБ), состоящее из трех букв или одной буквы и цифр. В системе АЛГОРИТМ используются 13 типов блоков, которые перечислены в таблице на рис. 4.4. №
УПБ
Символ
Тип блоку – оператор
1
Нач
ТЕРМІНАТОР
Початок алгоритму або процедури
2
Кон
ТЕРМІНАТОР
Кінець алгоритму або процедури
3
NOP
ПРОЦЕС
«Пустий блок»
4
A..
ПРОЦЕС
Присвоювання (Assignment)
5
I..
ДАНІ
Введення даних (Input)
6
O..
ДАНІ
Виведення даних (Output)
7
D..
РІШЕННЯ
Перевірка виконання умови (Decision)
8
F..
ПОЧАТОК ЦИКЛУ
Початок циклу з параметром (For)
9
W..
ПОЧАТОК ЦИКЛУ
Початок циклу з передумовою (While)
10
R..
ПОЧАТОК ЦИКЛУ
Початок циклу з післяумовою (Repeat)
11
E..
КІНЕЦЬ ЦИКЛУ
Кінець циклу з параметром чи передумовою (End)
12
U..
КІНЕЦЬ ЦИКЛУ
Кінець циклу з післяумовою (Until)
13
P..
ПРОЦЕДУРА
Виклик процедури (Procedure)
Рис. 4.4 Позначення типів блоків «Пустой блок» NOP не выполняет никаких действий, кроме передачи управления следующему блоку. Он может быть использован для резервирования места в блок-схеме, куда позднее планируется вставить «функциональный» блок, или для задания точки останова перед блоком «конец цикла». Двоеточие в УОБ обозначает цифры, которые автоматически присваиваются в процессе синтеза алгоритма. Детальное описание операторов будет дано ниже при рассмотрении различных алгоритмических структур. 4.1.5 Построение блок-схемы алгоритма Построение блок-схемы нового алгоритма выполняется после описания переменных (а также массивов и функций, если последние используются в алгоритме). Построение включает в себя три этапа: - ввод блоков алгоритма; - задание связей между блоками; - построение соединительных линий. Если алгоритм имеет иерархическую структуру, эти этапы выполняются для каждого уровня иерархии. Особенности построения иерархических алгоритмов рассматриваются в подразделе ?. Следует усвоить важное правило. Перед тем, как вводить блок-схему в компьютер, алгоритм необходимо максимально полно продумать, нарисовать блок-схему на бумаге, пронумеровать блоки и адреса переходов, выбрать тип каждого блока и описать его оператор. Такая предварительная подготовка существенно повышает эффективность синтеза алгоритма. 28
Ввод блоков алгоритма Блоки алгоритма следует вводить в порядке возрастания их номеров. Ввод блоков осуществляется с помощью специальной инструментальной процедуры… . Типы блоков:», которая вызывается одним из способов: • Из меню «Синтез | Ввод блоков» • Щелчком на инструментальной кнопке «Выбор типов, размещение и описание блоков» • Нажатием клавиши F6.
панели
«Синтез
На панели находятся 13 кнопок, которые позволяют выбрать блок нужного типа (см. п.4.1.4). Вид инструментальной панели показан на рис. 4.5.
Рис. 4.5 Панель вибору типу блоку • •
Для ввода блока требуется выполнить следующие операции: Щелкнуть мышью на кнопке блока заданного типа (на рис. 4.5 выбран блок «Ввод данных» – тип I). Щелкнуть мышью на пустой клетке в поле алгоритма, которая выделена рамкой из точек. В этой клетке появится графический символ блока с условным обозначением (УОБ) и в нижней части экрана откроется диалоговое окно «Характеристики и описание блока», которое показано на рис. 4.6.
Рис. 4.6 Вікно для введення оператора блоку і редагування параметрів • •
В строку ввода в данном окне ввести описание соответствующего оператора (и необязательно – краткие разъяснения в строку «Комментарий»). Щелкнуть на кнопке «Ввод» или нажать клавишу Enter.
Введенный оператор блока появится на поле алгоритма в соответствующей клетке колонки описаний. Повторяя эти операции для всех блоков, нетрудно создать основу блок-схемы алгоритма, которая должна быть дополнена связями блоков. Задание связей между блоками Связи между блоками задаются с помощью специального режима отображения поля алгоритма, который устанавливается одним из способов: • Из меню «Синтез | Задание связей» • Щелчком на инструментальной кнопке «Задание связей блоков» • Нажатием комбинации клавиш Ctrl+F6. • •
• • •
Для указания, куда должен передать управление каждый блок алгоритма, кроме блока РЕШЕНИЕ: Щелкните на блоке, откуда передается управление. В правом нижнем углу соответствующей клетки появится вопросительный знак красного цвета. Щелкните на блоке, куда необходимо передать управление. Метка (номер) этого блока заменит вопросительный знак, то есть, отобразится в качестве параметра msb блока – источника связи. Для задания связей блока РЕШЕНИЕ: Щелкните на блоке РЕШЕНИЕ. Вначале появится красный вопросительный знак в нижнем правом углу соответствующей клетки (переход «далее»). Щелкните на блоке, с которым должен быть соединен нижний выход блока РЕШЕНИЕ. Метка блокаадресата заменит вопросительный знак в параметре msb и появится вопросительный знак в параметре msb1 (переход «иначе»). Щелкните на блоке, с которым должен быть соединен боковой выход блока РЕШЕНИЕ. Метка блокаадресата заменит вопросительный знак в параметре msb1.
Примечание. Последний блок алгоритма – терминатор КОНЕЦ – никуда не должен передавать управление. Построение соединительных линий •
Данный режим включается или выключается одним из следующих способов: С помощью меню «Синтез | Соединительные линии» 29
• •
Щелчком на инструментальной кнопке «Построение/удаление соединительных линий» Нажатием комбинации клавиш Shift+F6.
Если между какими-либо блоками нельзя построить соединительную линию по техническим причинам, то соответствующий адрес перехода отображается синим цветом (однако на функционирование алгоритма это никак не влияет). Перечисленные операции синтеза алгоритма весьма просты и после овладения ими ввод блок-схемы алгоритма не будет представлять никаких затруднений. 4.1.6 Редактирование отдельных блоков Введенная блок-схема может содержать ошибки, допущенные как при составлении алгоритма, так и непосредственно в процессе ввода. Система АЛГОРИТМ предоставляет возможность редактирования отдельных блоков и группы блоков. Рассмотрим здесь первый вариант, поскольку он может быть востребован в первую очередь. Редактированию группы блоков посвящен п. ?. Изменение или удаление блока • • •
Щелкните правой кнопкой мыши на требуемом блоке. В соответствующей клетке откроется всплывающее меню с двумя командами: «Редактировать блок» и «Удалить блок». Если щелкнуть левой кнопкой мыши на команде «Редактировать…», то откроется диалоговое окно «Характеристики и описание блока» (см. рис. 4.6), в котором можно изменить оператор блока (параметры блока, как правило, в изменении не нуждаются). Если щелкнуть левой кнопкой мыши на команде «Удалить блок», то появится запрос на подтверждение удаления. При ответе «Да» выбранный блок удаляется, и последующие блоки сдвигаются вверх, заполняя освободившееся место.
Вставка и замена блока • Откройте панель типов блоков как в случае ввода блоков (см. п.4.1.5). • Выберите нужный тип блока. Место куда требуется вставить блок, зависит от того, должен ли он находиться перед или после какого-либо блока. • Если требуется вставить новый блок перед заданным, щелкните левой кнопкой мыши на данном блоке. В открывшемся окне «Вставка и замена блока» щелкните на кнопке «Перед выделенным». Новый блок вставляется на указанное место, а заданный и последующие блоки сдвигаются вниз. • Если требуется вставить новый блок после заданного, щелкните левой кнопкой мыши на данном блоке. В открывшемся окне «Вставка и замена блока» щелкните на кнопке «После выделенного». Заданный блок останется на своем месте, а все последующие блоки сдвигаются вниз, освобождая место для нового блока. • Если требуется заменить заданный блок новым, щелкните на данном блоке. В открывшемся окне «Вставка и замена блока» щелкните на кнопке «Вместо выделенного». В любом случае после указанных операций открывается окно «Характеристики и описание блока» (см. рис. 4.6), с помощью которого вводится оператор нового блока. Примечание. При вставке и удалении блока изменение адресов переходов выполняется автоматически. Однако рекомендуется проверить корректность этих изменений и при необходимости откорректировать связи блоков. 4.1.7 Структура алгоритма Как уже отмечалось выше, блоки алгоритма рекомендуется вводить и размещать в том порядке, в каком должна решаться поставленная задача. При этом общую задачу целесообразно разделить на частные подзадачи таким образом, чтобы фрагмент алгоритма, соответствующий каждой подзадаче, имел только один вход и только один выход. Алгоритм в целом и его фрагменты могут иметь различную структуру. Алгоритмическая структура называется целостной, если входом для нее служит вход первого блока фрагмента, а выходом – выход последнего блока. Таким образом, алгоритм в целом по определению является целостной структурой, поскольку первым блоком алгоритма является терминатор НАЧАЛО, а последним блоком – терминатор КОНЕЦ. Выполнение условия целостности для отдельных фрагментов облегчает их отладку и редактирование, а также реализацию на каком-либо языке программирования высокого уровня. -
Целостные структуры, в свою очередь образуют несколько разновидностей: линейные структуры (см. подраздел 4.2); ветвящиеся структуры (см. подраздел 4.3); 30
-
циклические структуры (см. подраздел 4.4); иерархические структуры (см. подраздел 4.5).
На практике часто какой-либо фрагмент алгоритма (и весь алгоритм в целом) содержит одновременно несколько различных структур, то есть, имеет комбинированную структуру. Для анализа корректности структуры в системе АЛГОРИТМ предусмотрены специальные средства диагностики (см. подраздел ?). Рассмотрение алгоритмических структур начнем с самой простой разновидности – линейной структуры. 4.2
Линейная структура Практически любой алгоритм содержит в себе фрагменты, имеющие линейную структуру.
Линейной называется такая целостная алгоритмическая структура, которая не содержит в себе ветвящихся и циклических структур. Блоки линейного фрагмента образуют цепочку, в которой каждый блок передает управление следующему блоку. Любая часть линейного фрагмента алгоритма является целостной структурой, которая, в свою очередь, тоже является линейной. -
В линейной структуре алгоритма используются только три типа блоков: присваивание (тип A); ввод данных (тип I); вывод данных (тип O).
4.2.1
Присваивание
Блок присваивания является одним из основных «строительных блоков» алгоритма. Он обеспечивает преобразование данных, запоминание и перемещение их в оперативной памяти компьютера. Данный блок изображается графическим символом ПРОЦЕСС и его условное обозначение (УОБ) начинается буквой A (от Assignment). Блок присваивания выполняет один или несколько операторов присваивания. Каждый из них имеет в общем случае следующую форму Ліва частина:=Права частина Комбинация символов «:=» обозначает «становится равным» и должна вводиться без пробела, однако до и после этой комбинации пробел допустим. Можно вставлять в один блок несколько операторов присваивания, которые должны разделяться точкой с запятой «;». В этом случае они выполняются последовательно, слева направо. Такой блок по существу эквивалентен нескольким блокам присваивания, следующим друг за другом, что позволяет создавать более компактные блок-схемы. В зависимости от характера левой части допустимы следующие варианты правой части. 1) Если левая часть является переменной или элементом массива, то правой частью может быть: - значение; - переменная; - элемент массива; - любое выражение, которое возвращает единственное значение. Тип данных в левой и правой части должен быть одинаковым, однако допускается присваивать вещественным данным (типа R) целочисленные значения (типа I), но не наоборот, а также строковым данным (типа S) символьные значения (типа C), но не наоборот. Примеры правильных операторов присваивания: i := 1
x := d*sin(f)
b[j] := b[j] + r0/a[j]
Здесь i, x, d, f, r0, j – переменные, a, b – одномерные массивы. 2) Если в левой части указан массив или подмассив, то и правая часть должна быть массивом или подмассивом с таким же числом элементов и того же типа. При этом передача данных между массивом и подмассивом возможна только из последовательно расположенных ячеек памяти. Пусть сформирован двумерный массив М размерности 3х2, элементы которого равны
31
11 12 M = 21 22 31 32
а также одномерные массивы А и В размерности 3, причем значения элементов массива А равны А = (1 2 3). Учитывая, что элементы массива М записаны в памяти по столбцам, то есть в последовательности 11, 21, 31, 12, 22, 32, можно рассматривать каждый столбец как подмассив и нумеровать уже одним индексом – номером столбца. Тогда допустимы, например, операторы присваивания M[2] := A ;
B := M[1].
В результате их выполнения будут получены следующие значения элементов массивов
11 M = 21 31
1 2 3
B = (11 21 31)
Значения элементов массива А не изменяются. Возможность передачи сразу всего массива или подмассива существенно упрощает составление алгоритма, где необходимы подобные операции. 4.2.2 Ввод данных Блок ввода данных графически изображается символом ДАННЫЕ. Ему присвоено условное обозначение I (от Input). В общем случае оператор ввода данных имеет следующую форму Введення:Елем.введ.1,Елем.введ.2,… где Ввод: – преамбула, которая создается автоматически. В окне «Характеристики и описание блока» достаточно в строке описания оператора перечислить лишь элементы ввода (Элем.ввода1,…). Элементом ввода может быть: 1) переменная; 2) элемент массива; 3) конструкция вида Имя_массива[‘Имя_файла’], которая обеспечивает чтение данных из заданного файла в массив с заданным именем. Данные должны быть записаны в файл оператором вывода. Одним оператором можно ввести несколько данных. В этом случае элементы ввода разделяются запятыми. Для ввода значения каждой переменной или элемента массива открывается специальное диалоговое окно, в котором указывается, какой блок алгоритма выполняет ввод и какой элемент вводится. Если элементом ввода является элемент массива, то индексы могут быть заданы арифметическими выражениями, в которых значения всех операндов должны быть определены к моменту ввода. Примеры допустимых операторов ввода: Ввод: b
(приведен на рис. 4.6)
Ввод: n, a[i], b[n-i] Перед выполнением этих операторов переменные b и n могут иметь произвольные значения и в результате ввода им будут присвоены новые значения. Перед выполнением второго оператора переменная i уже должна иметь допустимое значение. В случае ввода из файла структура, объем и тип данных должны соответствовать параметрам заполняемого массива. 4.2.3
Вывод данных
Блок вывода данных графически изображается символом ДАННЫЕ. Ему присвоено условное обозначение O (от Output). В общем случае оператор ввода данных имеет следующую форму Виведення:Елем.вивед.1,Елем.вивед.2,… где Вывод: – преамбула, которая создается автоматически. В окне «Характеристики и описание блока» достаточно в строке описания оператора перечислить лишь элементы вывода (Элем.вывода1,…). Элементом вывода может быть: 1) переменная; 2) элемент массива; 3) выражение; 32
4) текстовое сообщение; 5) конструкция вида Имя_массива[‘Имя_файла’]; 6) признак конца строки «;». Если элементами вывода являются переменная, элемент массива или выражение, то вычисляются и выводятся их значения. Выводимое текстовое сообщение должно быть заключено между знаками апострофа «’». За исключением варианта 5, вывод осуществляется в текстовый буфер, где накапливается в виде строк. Для перехода к следующей строке необходимо в качестве последнего элемента вывода указать символ «точка с запятой» – вариант 6 (разделительная запятая перед ним не нужна). Содержимое буфера выводится в окне «Результаты выполнения», которое открывается одним из способов: • С помощью меню «Выполнение | Числовые результаты» • Щелчком на инструментальной кнопке «Отображение числовых результатов». Записать в файл можно только содержимое массива чисел (целых или вещественных) и строк. Возможность записи в файл результатов обработки и затем чтения их оператором ввода позволяет поэтапно решать весьма сложные задачи. Представлением выводимой информации можно управлять с помощью форматирования элементов вывода. Форматирование элементов вывода 1) Без форматирования значение элемента вывода отображается с максимальной точностью, например, выводится как 0.666666666666667. 2) Форматирование задается двумя параметрами: N – общее число позиций, отводимых для элемента вывода; D – число знаков дробной части. Элемент вывода с параметрами форматирования имеет вид Элем.вывода:N:D Если при заданных N и D недостаточно позиций для вывода целой части числа, то при выводе число займет столько позиций, сколько потребуется для его правильного представления. 3) Если перед параметром N поместить знак «минус», то будет выведено обозначение элемента вывода (как задано в операторе вывода), знак «=» и затем численное значение. 2/3
4.2.4 Пример линейного алгоритма Решение большинства задач в школьных курсах алгебры и физики может быть описано линейными алгоритмами. Рассмотрим в качестве примера одну из задач механики. ЗАДАЧА 4.1. Вычислить тормозной путь автомобиля в зависимости от скорости движения (в км/час), уклона дороги (в %) и коэффициента трения колес автомобиля о дорожное покрытие. Данную задачу можно описать следующей информационной моделью, состоящей из математической модели, вычислительной модели и модели данных (см. подраздел 1.1). Математическая модель На рис. 4.7 показаны силы, действующие на автомобиль (условно обозначенный квадратиком) при движении вверх по уклону (вариант «а») и вниз по уклону (вариант «б»).
v
v -fi
Fs
B
B
Ft
Ft A
Fs fi
C Fd
C
A Fd
Fg
Fg
а)
б)
Рис. 4.7 Ілюстрація до задачі 4.1
33
Уклон u принято считать равным отношению приращения высоты к единице пройденного пути, то есть, применительно к рис. 4.7а
u=
BC = sin fi , AB
(4.1)
где fi – угол уклона. Применительно к рис. 4.7б, где автомобиль движется вниз по уклону, высота уменьшается и угол между горизонтальной линией и дорогой является отрицательной величиной, следовательно, уклон (4.1) также имеет отрицательное значение. На автомобиль массой падения g = 9.8 м/с2
m действует сила тяжести
Fg = m ⋅ g .
Fg, соответствующая ускорению свободного (4.2)
На дороге с уклоном эта сила раскладывается на две составляющие: силу давления на дорогу Fd и силу скатывания Fs. Из рисунка нетрудно видеть, что угол между векторами сил Fg и Fd равен fi, таким образом
Fd = Fg ⋅ cos fi
Fs = −Fg ⋅ sin fi
(4.3)
Знак «минус» в правой части выражения для силы скатывания Fs указывает, что она направлена противоположно вектору v скорости движения вверх по уклону и в направлении вектора v при движении вниз по уклону (отрицательное значение sin fi в данном случае дает положительное значение Fs). Сила торможения Ft, как известно, не зависит от скорости движения, но прямо пропорциональна силе давления на дорогу Fd и коэффициенту трения k
Ft = −k ⋅ Fd .
(4.4)
Она также направлена противоположно вектору скорости v. Обозначим силу, замедляющую движение автомобиля, через F
F = Ft + Fs .
(4.5)
При движении вверх по уклону оба слагаемых в правой части выражения (4.5) отрицательны и модуль силы F принимает максимальное значение. При движении вниз по уклону первое слагаемое (Ft) остается отрицательным, а второе слагаемое (Fs) становится положительным, в результате чего модуль силы F принимает минимальное значение. То есть, сила скатывания Fs в первом случае способствует, а во втором случае препятствует торможению. Замедление движения автомобиля a связано с силой F соотношением, аналогичным (4.2)
F = m⋅a.
(4.6)
Если отсчет времени начинать с момента начала торможения, то текущее значение скорости движения определяется выражением
v = v0 + a ⋅ t ,
v
(4.7)
где v0 – скорость в момент начала торможения (заданная величина). Искомый тормозной путь автомобиля s, совершающего равнозамедленное движение, равен
s = v0 ⋅ t + a ⋅
t2 . 2
(4.8)
Вычислительная модель Для вычисления тормозного пути по формуле (4.8) необходимо предварительно вычислить замедление a и время торможения t. При разложении силы тяжести Fg на составляющие Fd и Fs целесообразно воспользоваться тригонометрическим соотношением
sin 2 x + cos 2 x = 1 , справедливым для любого аргумента x. Отсюда с учетом (4.1) следует cos fi = 1 − sin 2 fi = 1 − u 2 .
На основе соотношений (4.2)-(4.6) нетрудно получить равенства m ⋅ a = −k ⋅ Fd + Fs = −k ⋅ m ⋅ g ⋅ 1 − u 2 − m ⋅ g ⋅ u = −m ⋅ g ⋅ ( k ⋅ 1 − u 2 + u ) .
34
Разделив левую и правую часть равенства на массу (которое не зависит от массы автомобиля)
m, получаем выражение для вычисления замедления
a = −g ⋅ (k ⋅ 1 − u 2 + u ) .
(4.9)
Учитывая, что после торможения текущая скорость автомобиля v должна быть равна 0, из равенства (4.7) 0 = v0 + a⋅t определяем время торможения
t =−
v0 . a
(4.10)
Теперь тормозной путь может быть вычислен непосредственно по формуле (4.8). Модель данных Приведенные выше выкладки определяют следующий набор переменных, которые требуется описать (см. описание переменных в п.2.6.1). Входные данные: v0 – скорость движения автомобиля перед торможением; u – уклон пути; k – коэффициент трения колес о дорожное покрытие. Результат вычислений: s – тормозной путь. Промежуточные данные: g – ускорение свободного падения; a– замедление при торможении; t – время торможения. Все переменные должны иметь вещественный тип (:R) и могут иметь нулевое начальное значение, кроме переменной g, которой необходимо присвоить значение 9.8. Алгоритм решения задачи Благодаря подробно обоснованной вычислительной модели, составление алгоритма не представляет никаких трудностей. Опишем алгоритм в виде последовательности операторов, которые выполняются соответствующими блоками. Блок 1 2 3 4 5 6 7 8 9
УПБ Нач I1 O1 A1 A2 A3 A4 O2 Кон
Оператор Введення : v0, u, k Виведення: ‘Для’,v0:-7:0,’км/год,’,u:-6:0,’%,’,k:-7:2 v0:=v0*1000/3600; u:=u/100 a:=-g*(k*sqrt(1-sqr(u))+u t:=-v0/a s:=v0*t+a*sqr(t)/2 Виведення: ‘ гальмовий шлях =’,s:6:1,’м’;
Алгоритм, как всегда, начинается ТЕРМИНАТОРОМ «Начало» (блок 1) и заканчивается ТЕРМИНАТОРОМ «Конец» (блок 9). Собственно работа алгоритма начинается с блока 2 (ДАННЫЕ – ввод I1), который обеспечивает ввод входных данных: скорости движения v0 (в км/час), уклона дороги u (в %) и коэффициента трения k (реальный диапазон 0<k<0.5). Для полноты информации о решаемой задаче рекомендуется перед выводом результатов решения вывести также входные данные. Это выполняется блоком 3 (ДАННЫЕ – вывод O1). Предположим, введена скорость 50км/час, уклон 10% и коэффициент трения 0.4. Эти сведения можно представить в виде Для V0=50км/год, U=10%, K=0.40
(4.11)
Тогда оператор вывода O1 должен содержать следующие элементы вывода: - текстовый элемент ‘Для’; - поскольку скорость движения (км/час) на практике достаточно задавать не более чем трехзначным числом, то необходимо предусмотреть вывод в виде V0=XXX. Выводимая информация занимает 6 позиций плюс 1 позиция для отделения от предыдущего элемента вывода. Такой вывод реализуется элементом V0:-7:0 (см. форматирование вывода в п.4.2.3); - текстовый элемент ‘км/час,’;
35
поскольку уклон (%) на практике достаточно задавать не более чем двумя цифрами со знаком, то необходимо предусмотреть вывод в виде U=-XX. В данном случае такой вывод реализуется элементом U:6:0; - текстовый элемент ‘%’; - для коэффициента трения необходимо предусмотреть вывод в виде K=X.XX, что реализуется элементом вывода K:-7:2. Поскольку в дальнейшем в эту же строку вывода должны помещаться результаты расчета тормозного пути, то оператор O1 не нужно завершать никаким разделительным знаком. -
Блок 4 (ПРОЦЕСС – присваивание А1) выполняет два оператора присваивания, с помощью которых производится пересчет значения «км/час» в значение «м/с» и значения «%» в значение десятичной дроби. Блок 5 (ПРОЦЕСС – присваивание А2) выполняет оператор присваивания переменной а значения замедления в соответствии с формулой (4.9), где для возведения в квадрат и извлечения квадратного корня используются библиотечные функции соответственно sqr() и sqrt() (см. п.3.2.2). Блок 6 (ПРОЦЕСС – присваивание А3) выполняет оператор присваивания переменной t значения в соответствии с формулой (4.10). Блок 7 (ПРОЦЕСС – присваивание А4) вычисляет тормозной путь по формуле (4.8) и присваивает полученное значение переменной s. Блок 8 (ДАННЫЕ – вывод О2) выводит полученные результаты с помощью следующих элементов вывода: - текстовый элемент ‘ тормозной путь =’; - значение переменной S в формате S:6:1, который обеспечивает вывод с точностью до десятых долей метра; - текстовый элемент ‘м’; - признак «;» окончания строки. Информация, выводимая данным блоком, добавляется к информации, выведенной блоком 3, и отображается в окне «Результаты выполнения». Для приведенного примера строка результатов будет иметь следующий вид Для V0=50км/год, U=10%, К=0.40 гальмовий шлях = 19.8м
(4.12)
Построение и выполнение алгоритма • • •
После запуска системы АЛГОРИТМ перейдите в режим создания нового алгоритма одним из способов: С помощью меню «Файлы | Новый алгоритм» Щелчком на инструментальной кнопке «Создать новый алгоритм» Нажатием комбинации клавиш Ctrl+N.
В открывшемся окне «Разработчик» введите запрашиваемые данные (необязательно). •
Перед синтезом алгоритма опишите используемые переменные в соответствии с моделью данных (см. п.2.6.1).
•
Введите блоки алгоритма в соответствии с приведенными выше операторами, задайте связи между блоками в виде последовательного соединения, постройте соединительные линии (см. п.4.1.5).
Построенный алгоритм должен иметь вид, показанный на рис. 4.8, где он дополнен краткими комментариями.
36
Рис. 4.8 Алгоритм вирішення задачі 4.1 Перед запуском алгоритма на выполнение проверьте правильность его структуры одним из способов: • С помощью меню «Анализ | Структура алгоритма» • Щелчком на инструментальной кнопке «Диагностика структуры алгоритма» • Нажатием комбинации клавиш Shift+F7. При отсутствии ошибок будет выдано сообщение о том, что алгоритм может быть запущен на выполнение. • • •
Запустите выполнение алгоритма одним из способов: С помощью меню «Выполнение | Авторежим» Щелчком на инструментальной кнопке «Выполнение без остановов» Нажатием клавиши F9.
После ввода входных данных и выполнения расчетов откройте окно «Результаты выполнения» одним из способов: • С помощью меню «Выполнение | Числовые результаты…» • Щелчком на инструментальной кнопке «Отображение числовых результатов». Для приведенного тестового примера в окне «Результаты выполнения» должна отобразиться строка, аналогичная (4.12). 4.3 Ветвящиеся структуры Ветвящиеся структуры обеспечивают управление ходом выполнения алгоритма в зависимости от выполнения или невыполнения определенных условий. Они образуются с помощью блока РЕШЕНИЕ (см. п.4.1.2). В отличие от других блоков, имеющих единственный выход, данный блок имеет два выхода – нижний и боковой –, которые соответствуют двум возможным результатам выполнения оператора. Оператор блока РЕШЕНИЕ имеет условное обозначение D (от Decision). Он вычисляет Логічний вираз и в зависимости от полученного значения выбирает дальнейший путь выполнения алгоритма: - если логическое выражение истинно (имеет значение true), то передается управление той ветви алгоритма, которая соединена с выходом, имеющим признак Д (да); - если логическое выражение ложно (имеет значение false), то передается управление другой ветви алгоритма, которая соединена с выходом, имеющим признак Н (нет). В качестве логического выражения (см.подраздел 3.3) можно использовать: - переменную логического типа (:B); - собственно логическое выражение, состоящее из логических операндов и логических операций (например, A & B, где A и B – логические переменные); - выражение, использующее операции отношения (например, A > B, где переменные А и В могут быть числового, символьного или строкового типа). При построении ветвящегося фрагмента алгоритма рекомендуется следовать нескольким правилам: 1) Для облегчения визуального восприятия алгоритма связи между блоками должны отображаться в максимально простом виде с минимальным пересечением соединительных линий. 2) Для упрощения реализации ветвящегося фрагмента алгоритма в виде программных конструкций признак «Д» должен иметь нижний выход блока РЕШЕНИЕ, а признак «Н» – боковой выход. В этом случае выход Д обозначает также переход «далее» (параметр msb), а выход Н – переход «иначе» (параметр msb1). Такой вариант предусмотрен по умолчанию. 3) Ветвящаяся структура должна быть целостной, то есть ветви алгоритма, подключенные к разным выходам, в конечном итоге должны сходиться в одной точке. 4) Фрагмент алгоритма в каждой ветви также должен образовывать целостную структуру. Пусть, например, требуется проверить условие a = b и в случае его невыполнения присвоить b := 0. Соответствующий фрагмент алгоритма показан на рис. 4.9а, однако он не удовлетворяет рекомендации (1). На рис. 4.9б приведен более наглядный вариант, который получен переменой местами выходов (возможность такой перемены рассмотрена в п.4.1.3).
37
Н
a=b
a=b
Д
Д
a <>b
Н Д
Н
b := 0
b := 0
b := 0
а)
б)
в)
Рис. 4.9 Рекомендований варіант розгалуження – праворуч Он учитывает рекомендацию (1), но не учитывает рекомендацию (2). Замена логического выражения в блоке РЕШЕНИЕ на инверсное (замена знака «=» на знак «<>») позволяет получить вариант, который приведен на рис. 4.9в и удовлетворяет всем рекомендациям. 4.3.1 Разновидности ветвящихся структур Ветвящиеся структуры обеспечивают выбор варианта выполнения алгоритма.
ЛВ
Н Д
Фрагмент алгоритму
Весьма распространенной является разновидность, обеспечивающая двухальтернативный выбор, которая показана на рис. 4.11. В данном при выполнении условия выполняется ФрагментД, а при невыполнении – ФрагментН. Выходы обоих фрагментов соединяются в одной точке, образуя выход ветвящейся структуры.
Рис. 4.10 Вибір з однією альтернативою
ЛВ
Н Д
ФрагментД
ФрагментН
Простейшая разновидность ветвящейся структуры обеспечивает одноальтернативный выбор. Эта разновидность показана на рис. 4.10, где ЛВ – логическое выражение (проверяемое условие). В данном случае выполнение условия обеспечивает переход с нижнего выхода (Д) к выполнению фрагмента алгоритма, следующего за блоком РЕШЕНИЕ. В противном случае ветвящаяся структура ничего не выполняет, так как боковой выход (Н) соединен непосредственно с её выходом.
С помощью блока РЕШЕНИЕ можно реализовать также многоальтернативный выбор, аналогичный переключателю. Пусть, например, переменная Р к моменту выполнения ветвящейся структуры может иметь одно из трех различных значений Z1, Z2, Z3. Ветвящаяся структура в зависимости от значения переменной Р должна выполнить соответственно один из фрагментов: Фрагмент1, Фрагмент2, Фрагмент3. Пример ветвящейся структуры с много альтернативным выбором приведен на рис. 4.12. Если переменная Р может принимать только одно из значений Z1, Z2, Z3, то из неравенств P<>Z1 и P<>Z2 следует, что P=Z3. В этом случае проверка в блоке 5 уже не нужна, его можно исключить и выход Н блока 3 подсоединить к входу Фрагмента3 (показано пунктирной линией). Если же переменная Р может принимать еще какие-либо значения, кроме Z1, Z2, Z3 (не существенно, какие именно), то возможны варианты: -
либо выход Н последнего блока проверки (блока 5) подключить к выходу В ветвящейся структуры (в этом случае последняя не будет выполнять никаких действий, если значение пе
Рис. 4.11 Вибір з двома альтернативами
38
1
Н
P=Z1
5
Н
P=Z3
Д
Д
2
6 Фрагмент1
Фрагмент3
3
Н
P=Z2
Д
7 Фрагмент0
4 Фрагмент2 B
-
ременной Р не равно ни Z1, ни Z2, ни Z3);
Рис. 4.12 Багатоальтернативний вибір
либо ввести еще один Фрагмент0, выполняющий какие-либо действия в последней ситуации, и на него передать управление с выхода Н блока 5.
-
Для любой разновидности ветвящихся структур фрагменты алгоритмов в любой из ветвей могут иметь любую целостную структуру – линейную, ветвящуюся, циклическую –, что позволяет создавать весьма сложные алгоритмы. 4.3.2 Модификация алгоритма решения задачи 4.1 Рассмотренный в п.4.2.4 линейный алгоритм вычисления тормозного пути автомобиля правильно работает лишь в том случае, когда сила торможения Ft по модулю превышает силу скатывания Fs. При уменьшении коэффициента трения или увеличении уклона дороги торможение затрудняется и становится невозможным, когда |Ft| ≤ |Fs|. В этом случае при движении вниз по уклону автомобиль, несмотря на торможение, будет соскальзывать вперед, а при движении вверх по уклону остановится на мгновение, затем будет соскальзывать назад. Поскольку в соотношениях (4.1)-(4.8), которые описывают математическую модель задачи, указанное условие не предусмотрено, то алгоритм может выдавать неправильные результаты. Например, Для V=50км/год, U=-10%, K=0.05 гальмовий шлях=-195.9м. Об ошибочности результата наглядно свидетельствует отрицательное значение тормозного пути. Поэтому для удовлетворения одного из основных требований (4) к описанию решения задачи (см. п.1.2.1) в алгоритме необходимо предусмотреть соответствующую проверку. Представим выражение (4.9) для замедления движения a в виде двух слагаемых a = at + as, где
(4.13)
at = −g ⋅ k ⋅ 1 − u 2
as = − g ⋅ u
– замедление, вызванное силой – замедление или ускорение, вызванное силой скатывания Fs.
торможения
Ft,
Выполним проверку at > as .
(4.14)
Если неравенство выполняется, то вычисление тормозного пути дает правильный результат. В противном случае необходимо выдать сообщение о невозможности торможения. • •
Нетрудно внести в алгоритм, изображенный на рис. 4.8, соответствующие изменения. Добавить описания переменных at и as (типа :R). Отредактировать оператор присваивания в блоке 5 согласно (4.13) 39
at:=-g*k*sqrt(1-sqr(u)); as:=-g*u; a:=at + as •
Вставить после блока 5 блок РЕШЕНИЕ D1, реализующий проверку (4.14)
abs(at) > abs(as) •
Вставить перед терминатором «Конец» блок ДАННЫЕ О3 с оператором вывода
Виведення:’ гальмування неможливе’; •
Отредактировать связи блоков следующим образом: выход Д блока D1–A3–A4–O2–«Конец», а также выход Н блока D1–O3–«Конец».
Модифицированный алгоритм изображен на рис. 4.13. В полученном алгоритме блоки 6-10 образуют ветвящийся фрагмент, где блоки 7, 8, 9 входят в состав одной ветви, а блок 10 образует другую ветвь. То есть, ветвящийся фрагмент реализует двухальтернативный выбор. 4.3.3 Анализ корректности ветвящихся структур Корректная организация ветвящихся структур чрезвычайно важна для создания правильно работающего алгоритма. Однако связи между блоками в таких структурах усложняются и визуальное восприятие блок-схемы затрудняется. Для проверки корректности ветвящихся структур в системе АЛГОРИТМ предусмотрен специальный режим анализа, который включается одним из способов: • С помощью меню «Анализ | Ветвления» • Щелчком на инструментальной кнопке «Анализ ветвящихся структур» • Нажатием клавиши F7.
Рис. 4.13 Модифікований алгоритм вирішення задачі 4.1 На рис. 4.13 колонки описания блоков отображены в указанном режиме. В данном случае в первой строке описания комментарий не выводится, а остается только условное обозначение блока (УОБ). Для каждого блока, который принадлежит ветвящимся структурам (одной или вложенным друг в друга), в первой строке указывается последовательность номеров тех структур, куда он входит. В данном случае есть только один ветвящийся фрагмент, который начинается с блока 6. Цифра «1» в УОБ этого блока D1 является номером ветвящегося фрагмента. Эта единица отображается красным цветом в первой строке описания всех блоков 610, наглядно показывая, какие именно блоки образуют ветвящуюся структуру. В качестве дополнительного примера, иллюстрирующего полезность рассмотренного режима анализа, на рис. 4.14 приведен алгоритм, который реализует многоальтернативный выбор в соответствии с рис. 4.12. Нетрудно видеть, что 1-й ветвящийся фрагмент (началом которого является блок РЕШЕНИЕ D1) состоит из блоков 3-9. Он содержит в себе вложенный 2-й фрагмент (началом которого является блок D2), состоящий из блоков 5-9. Последний, в свою очередь, содержит в себе вложенный 3-й фрагмент (началом которого является блок D3), состоящий из блоков 7-9. Данная ветвящаяся структура является корректной, поскольку является целостной: она имеет один вход – вход блока 3 – и все её ветви сходятся в одной точке – точке входа в блок 10.
40
Рис. 4.14 Аналіз розгалужених структур При текстовом описании алгоритма совокупностью выполняемых операторов после текста оператора будем в фигурных скобках указывать метку блока, которому передается управление, если этот блок не является следующим. В блоках РЕШЕНИЕ, кроме того, будем указывать, какому выходу (Д или Н) соответствует передача управления. Для алгоритма, изображенного на рис. 4.14, такое описание имеет следующий вид: Метка УПБ 1 2 3 4 5 6 7 8 9 10
Оператор Нач I1 D1 O1 D2 O2 D3 O3 O4 Кон
Введення: p p = z1 {Д4/Н5} Виведення:’Фрагмент1’;{10} p = z2 {Д6/Н7} Виведення:’Фрагмент2’;{10} p = z3 {Д8/Н9} Виведення:’Фрагмент3’;{10} Виведення:’Фрагмент0’;
Оно не столь наглядно, как блок-схема, однако достаточно для построения алгоритма. На рис. 4.15 показан пример некорректной
ветвящейся структуры. В данном алгоритме с помощью блока РЕШЕНИЕ D1 организуется цикл для подсчета суммы целых чисел 1, 2,..., 10. Алгоритм работоспособен 41
и накапливает в переменной s значение 55, которое можно увидеть в окне «Список переменных» (см. подраздел 2.6). Некорректность данной ветвящейся структуры заключается в том, что ветви алгоритма, подсоединенные к выходам Д и Н блока 4, не сходятся в
Рис. 4.15 Приклад некоректної розгалуженої структури
одной точке. Кроме того, ветвь, подсоединенная к выходу Н, заканчиваетс входом в сам блок D1. То есть, блок D1 оказывается вложенным сам в себя. Подобную ветвящуюся структуру нельзя непосредственно реализовать программной конструкцией if…then…else…. На блок-схеме некорректность ветвящейся структуры отображается изменением цвета УОБ (D1) с черного на красный. Режим анализа выключается так же, как и включается, после чего в первых строках описания восстанавливается отображение комментариев. 4.4 Циклические структуры Слово «цикл» произошло от греческого слова «kyklos» (колесо, круг, кругооборот) и применительно к программированию имеет смысл «повторяющийся». Повторение какой-либо последовательности действий – весьма распространенное явление в окружающем нас мире. Движение маятника в механических часах, работа двигателя внутреннего сгорания в автомобиле, ходьба, звучание телефонного вызова и т.д. являются типичными примерами циклических процессов. Многие математические функции вычисляются путем разложения в ряд, то есть, путем циклического процесса вычисления членов ряда (по одной и той же формуле, в которой изменяется только номер члена ряда) и их суммирования. Очевидно, что обработка массивов, состоящих из однотипных элементов, также сводится к циклическим операциям над его элементами. Циклической структурой называется целостный фрагмент алгоритма, обеспечивающий выполнение некоторых повторяющихся действий. Циклическую структуру образуют блок НАЧАЛО ЦИКЛА, блок КОНЕЦ ЦИКЛА и один или несколько блоков, которые расположены между ними и образуют тело цикла. Для повторного выполнения тела цикла необходимо передать управление от блока КОНЕЦ ЦИКЛА к блоку НАЧАЛО ЦИКЛА. Однако на блоксхеме алгоритма такая обратная связь не отображается, но фактически она осуществляется. Собственно говоря, графические символы НАЧАЛО и КОНЕЦ ЦИКЛА (см. п. 4.1.2) введены именно для того, чтобы упростить вид блок-схем, особенно в случае вложенных циклов. Благодаря им циклические структуры выглядят подобно линейным структурам. Для правильного выполнения цикла необходимо при построении алгоритма обеспечить следующие требования. 1) Номер цикла, который указан в условных обозначениях блока (УОБ) начала и конца цикла, должен быть одинаковым. 2) Тело цикла должно иметь целостную структуру, то есть, запрещается вход в тело цикла в обход блока НАЧАЛО ЦИКЛА и выход из тела цикла в обход блока КОНЕЦ ЦИКЛА. Кроме целостности, других ограничений на структуру тела цикла нет – оно может иметь линейную, ветвящуюся и циклическую структуру. Следовательно, допускается многократное вложение циклов. Внутренний цикл должен целиком (то есть, блок НАЧАЛО ЦИКЛА, тело цикла и блок КОНЕЦ ЦИКЛА) находиться в теле внешнего цикла. При вводе алгоритма это требование выполняется автоматически, благодаря реализации принципа: цикл, который начинается первым (внешний), закрывается последним, а цикл, который начинается последним (внутренний), закрывается первым. 3) Во избежание «зацикливания» алгоритма необходимо обеспечить конечное число повторений тела цикла. 4) Типы операторов начала и конца цикла должны быть взаимно согласованными. Последнее требование обусловлено тем, что существуют несколько разновидностей циклов, которые отличаются функционированием операторов начала и конца цикла: - цикл с параметром; - цикл с предусловием; - цикл с послеусловием. Необходимо отметить, что внешний вид этих структур одинаков. 42
4.4.1
Цикл с параметром
Данная разновидность цикла обычно используется для построения циклической структуры алгоритма в случае, когда до входа в цикл известно число повторений тела цикла. Блок начала цикла изображается соответствующим графическим символом и его условное обозначение состоит из буквы F (от For) и номера цикла № (например, F4). Оператор начала цикла имеет следующую форму Поч. циклу № : параметр:=вираз_поч. ознака вираз_кін. где Нач. цикла № : – преамбула, формируемая по умолчанию (номер цикла № определяется автоматически); параметр – переменная целочисленного типа, играющая роль счетчика цикла; выраж.нач. – в общем случае арифметическое выражение, которое задает начальное значение параметра цикла; выраж.кон. – в общем случае арифметическое выражение, которое задает конечное значение параметра цикла; признак – характеризует направление изменения параметра и задается в виде: ключевое слово to (TO) – в данном случае параметр на каждом проходе цикла изменяется на +1; ключевое слово downto (DOWNTO) – в данном случае параметр на каждом проходе цикла изменяется на –1. Признак должен отделяться пробелом от начального и конечного значения параметра. Число повторений тела цикла зависит от начального значения параметра, конечного значеня параметра и признака. Обозначим начальное значение параметра как in, конечное значение параметра как ik. Тогда число повторений цикла равно: Признак
Соотношение
Число повторений
to to downto downto
in <= ik in > ik in >= ik in < ik
ik – in + 1 ни разу in – ik + 1 ни разу.
Блок конца цикла изображается соответствующим графическим символом и его условное обозначение должно состоять из буквы E (от End) и того же номера цикла №, что и в операторе начала цикла (для примера, E4). Совпадение номеров обеспечивается автоматически. Оператор конца цикла имеет следующую форму Кін. циклу № : где Кон. цикла №: – преамбула, формируемая по умолчанию. В данном случае он не выполняет никаких действий. При вставке блока «Конец цикла» в окне «Характеристики и описание блока» ничего вводить не требуется. Данный блок служит лишь для указания, где заканчивается тело цикла и куда необходимо перейти после его завершения. Предупреждение. В теле цикла нельзя изменять параметр цикла. Увеличение или уменьшение значения параметра (в зависимости от признака) выполняется автоматически. Вычисление факториала В качестве примера применения цикла с параметром рассмотрим решение следующей задачи. ЗАДАЧА 4.2. Вычислить факториал n! целого положительного числа n. Данная задача описывается весьма простой информационной моделью. Математическая модель Факториалом числа n называется число, равное
n!= 1 ⋅ 2 ⋅ 3 ⋅ ... ⋅ n .
(4.15)
В соответствии с этим выражением 1!=1, 2!=2, 3!=6, 4!=24 и т.д. В математике принято также определение 0! =1. Вычислительная модель Нетрудно видеть, что факториал числа n можно представить в виде
n!= ((n −1)!) ⋅ n .
(4.16)
Откуда для n = 1 – 1! = (0!)⋅1 = 1⋅1 = 1, 43
для n = 2 – 2! = (1!)⋅2 = 1⋅2 = 2, для n = 3 – 3! = (2!)⋅3 = 2⋅3 = 6 и т.д. В каждом из этих выражений текущее значение числа n умножается на факториал предыдущего значения (n1). Для получения 3! необходимо выполнить 3 шага таких вычислений. Обозначим fact – переменная, которая накапливает значение факториала. Тогда для каждого значения переменной i = 1, 2, …, n необходимо вычислить
fact := fact ⋅ i ,
(4.17)
где начальное значение переменной fact должно быть равно 1. Следовательно, для вычисления n! необходимо n раз повторить вычисления (4.17) и на каждом следующем шаге переменная i должна увеличиваться на 1. Для выполнения таких повторяющихся вычислений наиболее подходящим является цикл с параметром. Модель данных В рассматриваемой задаче используются три переменные целочисленного типа – n, i, fact. Алгоритм решения задачи 4.2 Ввиду простоты алгоритма ограничимся представлением его в виде описания операторов: Блок 1 2 3 4 5 6 7 8
УПБ Нач I1 A1 F1 A2 E1 O1 Кон
Оператор Введення: n fact := 1 Поч. циклу 1: i:=1 to n fact := fact*i Кін. циклу 1: Виведення: n,’!=’, fact;
Блоки 1 и 8 являются терминаторами. Блок 2 выполняет ввод числа n. Блок 3 присваивает переменной fact начальное значение 1. Блоки 4, 5, 6 образуют циклическую структуру. Блок 4 является началом цикла с параметром, который задает диапазон изменения переменной i от 1 до n в сторону увеличения (признак to). Блок 5 представляет собой тело цикла, которое реализует вычисления (4.17). Блок 6 обозначает конец циклической структуры. Блок 7 осуществляет вывод полученного значения факториала. В данном случае каждый блок передает управление следующему блоку, вследствие чего блок-схема алгоритма имеет вид, подобный линейной структуре. При вводе числа n = 0 тело цикла (оператор присваивания А2) не выполнится ни разу и будет выдан результат 0! = 1, который равен начальному значению переменной fact, присвоенному оператором А1. При вводе числа n > 0 тело цикла выполнится n раз, например, при n = 10 будет получено значение 10! = 3628800. Определение максимального элемента массива В качестве второго примера использования цикла с параметром рассмотрим часто встречающуюся задачу обработки массива. ЗАДАЧА 4.3. Найти максимальный элемент массива, состоящего из n элементов, и индекс этого элемента. Математическая модель Пусть {a1, a2,…, an} – заданный массив, состоящий из известного числа элементов n. Требуется найти значение am максимального элемента
am = max{a1 , a2 ,..., a n } и его индекс im такой, что
aim = am . Вычислительная модель Очевидно, что необходимо поочередно проанализировать все n элементов массива a. Не исключено, что максимальным является 1-й элемент, поэтому вначале следует присвоить am := a1,
im := 1.
Затем для всех остальных элементов ai с индексами i = 2, 3,…, n необходимо выполнить проверку: если ai > am, то обновить значения am := ai, im := i, в противном случае оставить прежние значения am и im. 44
Пусть массив a состоит из n = 8 чисел a = {3, 7, 2, 5, 9, 4, 6, 8}. Присвоим вначале am = 3, im = 1. Процесс анализа представим в виде i 2 3 4 5 6 7 8
ai > am ? am 7 > 3 – так 2 > 7 – ні 5 > 7 – ні 9 > 7 – так 4 > 9 – ні 6 > 9 – ні 8 > 9 – ні
im 7
2
9
5
После завершения анализа значения переменных am = 9 и im = 5 являются искомым результатом. Модель данных В рассматриваемой задаче элементами массива могут быть как целые, так и вещественные числа. Тип переменной am должен совпадать с типом элементов массива, а переменные im и i являются целочисленными. Алгоритм решения задачи 4.3 Представим его в виде описания операторов. Блок 1 2 3 4 5 6 7 8 9
УПБ Нач I1 A1 F1 D1 A2 E1 O1 Кон
Оператор Введення: n, a[‘masrand1.dat’] am:=a[1]; im:=1 Поч. циклу 1: i:=2 to n a[i] > am {Д6/Н7} am:=a[i]; im:=i Кін. циклу 1: Виведення:’am=’,am,’ im=’,im;
В блоке 2 вводится заранее сформированный массив masrand1.dat из 100 случайных целых чисел, поэтому число n не должно превышать 100. Примечание. Путем изменения в операторе D1 знака неравенства «>» на «<» алгоритм будет отыскивать минимальный, а не максимальный элемент.
4.4.2 Цикл с предусловием Данная разновидность циклической структуры используется в случае, когда число повторений тела цикла заранее неизвестно и необходимо предусмотреть ситуацию, в которой тело цикла вообще ни разу не выполнится. Блок начала цикла изображается соответствующим графическим символом и его условное обозначение состоит из буквы W (от While) и номера цикла № (например, W2). Оператор начала цикла имеет следующую форму Поч. циклу №: логічний_вираз где Нач. цикла №: – преамбула, формируемая по умолчанию (номер цикла № определяется автоматически); логическое_выражение – условие, в случае истинности которого выполняется тело цикла, в противном случае осуществляется выход из цикла. Если условие с самого начала окажется ложным, то вход в цикл не состоится и управление будет передано блоку, на который указывает блок конца цикла. Блок конца цикла изображается соответствующим символом и его условное обозначение состоит из буквы E(от End) и того же номера цикла №, что и в операторе начала цикла (для примера, Е2). Совпадение номеров обеспечивается автоматически. Оператор конца цикла с предусловием не выполняет никаких действий и полностью аналогичен оператору конца цикла с параметром: Кін. циклу №: Предупреждение. В теле цикла должно изменяться значение какой-либо переменной или єлемента массива, что в конечном счете должно привести к невіполнению условия цикла и его завершению. В противном случае цикл не сможет закончиться. Необходимо отметить, что цикл с предусловием является более общей формой организации циклической структуры, чем цикл с параметром. Другими словами, любые действия, которые можно выполнить с помощью цикла с параметром, можно выполнить и с помощью цикла с предусловием. То есть, первый можно всегда заменить вторым, например: 45
Цикл з параметром ………. F1 Поч. циклу 1: i:=1 to n Тіло циклу E1 Кін. циклу 1: ……….
Цикл з передумовою ………. A1 i:=1 W1 Поч. циклу 1: i<=n Тіло циклу A2 i:=i+1 E1 Кін. циклу 1: ……….
Вместе с тем, второй вариант требует двух дополнительных операторов прсваивания: для задания начального значения переменной i (оператор А1) и для изменения её значения в теле цикла (оператор А2). Поэтому в задачах с известным числом повторений тела цикла целесообразно использовать первый вариант. Пример цикла с предусловием Решим задачу: ЗАДАЧА 4.4. Определить минимальное число двоичных разрядов, необходимых для кодирования целого положительного числа n. Обозначим: r – число двоичных разрядов, m – максимальное целое число, которое может быть представлено r разрядами (см. подраздел 2.1)
m = 2r −1
(4.18)
Математическая модель Строго говоря, данную задачу можно решить путем округления в большую сторону логарифма по основанию 2 числа n, то есть, без привлечения циклов. Однако можно сформулировать её решение и в виде неравенств
( 2 r −1 −1) < n ≤ ( 2 r −1) .
(4.19)
Левое неравенство имеет место, когда r–1-го числа разрядов недостаточно для представления числа n, а правое неравенство – когда r разрядов достаточно. Вычислительная модель Проверку следует начинать с числа разрядов r = 1 (соответственно m = 1) и вместо системы неравенств (4.19) достаточно проверить неравенство
m< n.
(4.20)
Если оно не выполняется, необходимо увеличить число разрядов на 1, вычислить по формуле (4.18) новое значение m и повторить проверку условия (4.20). Этот процесс повторяется, пока выполняется условие (4.20), и завершается при невыполнении (4.20), то есть, когда m ≥ n. Пусть, например, n=5. Перед началом цикла r = 1, m = 1: Первый проход: 1 < 5 выполняется, обновляются значения r = 2, m = 22 – 1 = 3 ; Второй проход: 3 < 5 выполняется, обновляются значения r = 3, m = 23 – 1 = 7 ; Третий проход: 7 < 5 не выполняется, цикл завершается с результатом r = 3. Действительно, 510 = 1012, то есть, для кодирования числа 5 достаточно 3-х двоичных разрядов. Отметим, что выполнение (4.20) перед последним проходом цикла соответствует левому неравенству (4.19), а невыполнение (4.20) после этого прохода соответствует правому неравенству (4.19). Нетрудно видеть, что число проходов цикла заранее неизвестно и для их организации следует использовать цикл с предусловием. Модель данных В рассматриваемой задаче используются целочисленные переменные n, r и m. Однако необходимо учесть, что операция возведения в степень возвращает вещественный результат, поэтому для реализации выражения (4.18) следует использовать оператор присваивания
m := Round(2^ r − 1) .
(4.21)
Алгоритм решения Алгоритм решения задачи 4.4 представим в виде описания операторов. Блок 1 2 3 4 5
УПБ Нач I1 A1 W1 A2
Оператор Введення: n r:=1; m:=1 Поч. циклу 1: m < n r:=r+1; m:=Round(2^r–1) 46
6 7 8
E1 O1 Кон
Кін. циклу 1: Виведення:’Для кодування числа ‘,n,’ достатньо ‘,r,’ розрядів’;
Алгоритм достаточно наглядно реализует подробно рассмотренную вычислительную модель и в пояснении не нуждается. Заметим только, что для случаев n = 0 и n = 1 тело цикла (оператор А2) не выполнится ни разу и будет выдано значение r = 1 (присвоенное оператором А1). Для n = 500 будет выдано значение r = 9. 4.4.3 Цикл с послеусловием Как и предыдущая разновидность цикла, цикл с послеусловием применяется в случае, когда число повторений тела цикла заранее неизвестно. Однако, в отличие от предыдущей разновидности, здесь тело цикла обязательно выполняется хотя бы один раз. Блок начала цикла с послеусловием изображается соответствующим графическим символом и его условное обозначение состоит из буквы R (от Repeat) и номера цикла № (например, R3). Оператор начала цикла имеет следующую форму Поч. циклу №: где Нач. цикла №: – преамбула, формируемая по умолчанию (номер цикла № определяется автоматически). Он фактически не выполняет никаких действий и служит лишь для указания, что вслед за ним начинается тело цикла. Блок конца цикла с послеусловием изображается соответствующим графическим символом и его условное обозначение состоит из буквы U (от Until) и того же номера №, что и в блоке начала цикла (для примера, R3). Оператор конца цикла имеет следующую форму Кін. циклу №:логічний_вираз где Кон. цикла №: – преамбула, формируемая по умолчанию; логическое_выражение – условие, в случае истинности которого цикл завершается, в противном случае – продолжается. Предупреждение. В теле цикла должно изменяться значение какой-либо переменной или элемента массива, что в конечном итоге должно привести к выполнению условия и выходу из цикла. В противном случае цикл не сможет закончиться. Вычисление константы «пи» Многие математические константы являются иррациональными числами, которые можно выразить непериодической бесконечной десятичной дробью. Однако их можно вычислить с заданной точностью на основе разложения в числовой ряд. Рассмотрим решение подобной задачи. ЗАДАЧА 4.5. Вычислить число «пи» с относительной точностью eps (0 < eps < 1) на основе разложения в числовой ряд. Математическая модель Существует несколько числовых рядов для вычисления числа «пи». Используем ряд вида
1+
1 32
+
1 52
+
1 72
+ ... =
π2 . 8
(4.22)
Ряд является сходящимся, то есть, по мере увеличения номера члена ряда его значение уменьшается. Ограничимся учетом такого числа членов ряда, последний из которых будет меньше заданного числа eps. Вычислительная модель Обозначим: n – номер члена ряда (0-й член равен 1, 1-й член равен 1/9, 2-й член равен 1/25 и т. д.); sn – значение n-го члена ряда, которое в соответствии с (4.22) определяется выражением
sn =
1 (2 ⋅ n + 1) 2
;
(4.23)
sum – переменная, в которой накапливается сумма членов ряда sum := sum + sn. (4.24) Перед входом в цикл необходимо присвоить нулевые начальные значения переменным sum и sn. На каждом проходе цикла необходимо: - вычислить значение очередного члена ряда sn в соответствии с (4.23); - выполнить накопление суммы членов в соответствии с (4.24) и увеличить на 1 номер члена ряда n; - проверить в конце цикла выполнение условия sn < eps. (4.25) 47
В случае его невыполнения указанные вычисления повторяются, а в случае выполнения следует выйти из цикла. Для получения искомого значения «пи» необходимо в соответствии с (4.22) вычислить
sum := 8 ⋅ sum ,
(4.26)
после чего полученное значение sum необходимо вывести как результат решения задачи. Модель данных Переменные eps, sn, sum должны быть описаны как переменные вещественного типа, а n – как переменная целого типа. Начальные значения переменных роли не играют, их можно задать нулевыми. В вычислениях следует использовать библиотечные функции (извлечение квадратного корня).
sqr (возведение в квадрат) и
sqrt
Алгоритм решения На основе рассмотренной вычислительной модели нетрудно составить следующий алгоритм, который представим в виде описания операторов. Блок 1 2 3 4 5 6 7 8 9 10
УПБ Нач I1 A1 R1 A2 A3 U1 A4 O1 Кон
Оператор Введення: eps sum:=0; n:=0 Поч. циклу 1: sn:=1/sqr(2*n+1) sum:=sum+sn; n:=n+1 Кін. циклу 1: sn < eps sum:=sqrt(8*sum) Виведення:’пі=’,sum,’ (‘,n,’ членів ряду)’;
Оператор вывода кроме полученного значения «пи» в скобках выводит число использованных членов ряда. Алгоритм дает следующие результаты: Для eps=1e-4 пі=3.135345..(51 членів ряду) Для eps=1e-6 пі=3.140957..(501 членів ряду) Для eps=1e-8 пі=3.141529..(5001 членів ряду) Действительное значение числа «пи» с 6-ю верными десятичными знаками равно 3.141593. Приведенный пример свидетельствует о том, что получение высокоточного значения числа «пи» требует весьма большого объема вычислений. Второй пример цикла с послеусловием Рассмотренные выше особенности различных алгоритмических структур позволяют теперь вернуться к задаче 1.1 вычисления средней успеваемости учеников (см. п.1.2.2) и пояснить приведенный на рис.1.1 алгоритм её решения (см. п.1.2.3). Прежде всего необходимо учесть, что нецелесообразно запускать решение задачи для каждого ученика в отдельности. Следует обеспечить повторное её решение для некоторой группы учеников. От есть, требуется организовать цикл, на каждом проходе которого обрабатыватся данные о каком-либо одном ученике. Хотя учителю известно число учеников, однако для использования цикла с параметром это число должно быть введено отдельно. Более удобно использовать цикл с неизвестным числом повторений и в качестве признака завершения расчетов вводить пустую строку вместо фамилии ученика. Поскольку в данной задаче должен быть «обработан» минимум один ученик, то наиболее подходящим вариантом цикла является цикл с послеусловием. Организация такого циклического процесса показана на рис.4.16а.
48
1
5 Початок
2
7 Н
sn <> sp
Обчислення середнього балу учня Блоки 5-11
12
Кін. циклу 1: sn = sp
13 Кінець
а)
Поч. циклу 2 7
Введ. b
8
Н
D b <> 0
Д Ф
6 Поч. циклу 2
Введ. sn
4
n:=0 sb:=0
6
Поч. циклу 1 3
5 n:=0 sb:=0
8
9
10
11
Д
9
n:=n+1 sb:=sb+b
10 Кін. циклу 2: b=0 11 mb:= Round(sb/n)
Введ. b n:=n+f(b) sb:=sb+b Кін. циклу 2: b=0 mb:= Round(sb/n) Вивед. sn, mb
Вивед. sn, mb
в)
б) Рис. 4.16 Фрагменти алгоритму задачі 1.1
В теле цикла 1 вводится фамилия ученика в строковую переменную sn (блок 3). Затем содержимое sn сравнивается с пустой строкой, которая присвоена переменной sp на этапе описания переменных (блок 4). Если sn – непустая строка, то с выхода Д блока 4 управление передается фрагменту Ф, который вычисляет средний балл ученика, а блок конца цикла 1 (блок 12) передает управление в начало тела цикла, поскольку условие sn = sp не выполняется. Если с помощью блока 3 в переменную sn введена пустая строка, то блок 4 с выхода Н передает управление блоку 12, обеспечит завершение цикла вследствие выполнения условия sn = sp. Аналогичным образом реализуется вычисление среднего балла ученика с помощью цикла 2 с послеусловием, где признаком окончания ввода оценок служит ввод нулевой оценки. Соответствующий фрагмент алгоритма показан на рис.4.16б. Перед входом в цикл обнуляется счетчик оценок n и сумма оценок sb (блок 5). В теле цикла вводится очередная оценка в переменную b (блок 7). Затем по аналогии с циклом 1 следует проверить, отличается ли она от 0. Если отличается, то с выхода Д блока решения D управление передается блоку 8, который накапливает число оценок и их сумму. Затем блок 9 коца цикла 2 передает управление опять блоку ввода 7, поскольку условие b = 0 не выполняется. Если введенная оценка равна 0, то блок решения D с выход Н передает управление непосредственно на блок 9, который завершает цикл вследствие выполнения условия b = 0. После завершения цикла 2 производится вычисление средней оценки mb и её округление до ближайшего целого числа (блок 10). Полученные результаты выводятся блоком 11. На рис.4.16в приведен более «изящный» вариант вычислительного фрагмента алгоритма. В варианте на рис.4.16б обход блока 8 при b = 0 позволяет сохранить сумму баллов sb и число оценок n, накопленные на предыдущих проходах цикла. Но если b = 0, то сумма баллов в блоке 8 не изменится. Для того, чтобы при b = 0 осталось неизменным и число оценок, введена функция пользователя f(b)
0, если b = 0 f (b) = 1, если b ≠ 0
(4.27)
Она реализована с помощью библиотечной условной функции ifa( ) (см. п.3.2.3) и описана в окне «Список функций» (см. рис.3.2). Таким образом, эта функция позволяет «обойти» накопление числа оценок n в операторе присваивания n:=n + f(b) в случае b = 0 и тем самым исключить блок проверки D из тела цикла 2.
49
Вставляя блоки 5-11, на рис.4.16б, на место фрагмента Ф в алгоритм, приведенный на рис.4.16а, получаем окончательный алгоритм решения задачи 1.1, который показан на рис. 1.2. В структурном отношении он представляет собой циклическую структуру (блоки 2-12), тело которой, в свою очередь, содержит ветвящуюся (блоки 4-11) и циклическую (блоки 6-9) структуры. Модификация алгоритма решения задачи 1.1 Для дальнейшего использования результатов работы рассмотренного алгоритма целесообразно внести в него изменения, позволяющие записать результаты в файл vidomist.dat. • Добавьте в окне «Список переменных» (см. рис.2.2) переменную i целочисленного типа с начальным значением 0 – индекс в массиве результатов. • Опишите в окне «Список массивов» (см. п.2.6.2) одномерный массив result, содержащий 30 (или другое максимальное число учеников в классе) элементов строкового типа. • Вставьте перед блоком R1 (см. рис.1.2) блок присваивания с оператором i := 1. • Отредактируйте оператор присваивания в блоке А3: mb:=Round(sb/n); sn:=sn+’ – ‘+Strf(mb,0,0); result[i]:=sn; i:=i+1. В данном случае в строковой переменной sn к фамилии ученика добавляется разделительный знак « – » и его средний балл mb, преобразованный в строку функцией Strf(mb,0,0). Затем полученная строка записывается в массив result и увеличивается индекс i. • Отредактируйте оператор в блоке вывода О1 Виведення: sn; • Вставьте перед блоком «Конец» блок вывода Виведення: result[‘vidomist.dat’] который выполняет запись массива result в файл vidomist.dat. Полученный алгоритм изображен на рис.4.17. 4.4.4 Анализ циклических структур • • •
Анализ циклических структур производится подобно анализу ветвящихся структур одним из способов: С помощью меню «Анализ | Цикли» Щелчком на инструментальной кнопке «Анализ циклических структур» Нажатием комбинации клавиш Ctrl+F7.
В данном режиме на поле алгоритма в колонках описания операторов вместо комментариев отображаются синим цветом цифры – номера циклов, в состав которых входит блок. В случае некорректной организации цикла УОБ соответствующих блоков начала или конца цикла также изменяет свой цвет с черного на синий.
Рис. 4.17 Модифікований алгоритм задачі 1.1 у режимі аналізу циклічних
структур
На рисунке наглядно видна вложенность циклических структур и область, занимаемая каждой из них.
50
4.5 Сортировка и поиск Сортировка и поиск являются одними из наиболее распространенных процессов обработки данных. Списки учеников в журналах и телефонные справочники сортируются в алфавитном порядке. Аналогично отображаются списки переменных, массивов и функций в системе АЛГОРИТМ. Перечень поставщиков, предлагающих один и тот же товар по разной цене, обычно сортируется в порядке возрастания цены, а список футбольных и других спортивных команд – в порядке убывания набранных очков. В любом случае сортировки приходится иметь дело с данными, записанными в массиве, и требуется поменять местами элементы массива так, чтобы они размещались в определенном порядке их значений. Сортировка нужна для облегчения поиска необходимой информации. Если данные в массиве не отсортированы по заданному признаку, то приходится перебирать элементы массива один за другим, пока не будет найден интересующий, что совершенно неэффективно. Существуют различные алгоритмы сортировки, которые в конечном итоге дают одинаковый результат, но отличаются объемом вычислений. В принципиальном отношении различие между ними заключается в степени учета возможности того, что в каких-то фрагментах массива данные уже расположены в требуемом порядке. Учет указанного обстоятельства влечет за собой усложнение алгоритма. Более высокая эффективность сложных алгоритмов сортировки проявляется лишь при обработке больших массивов (тысячи и более элементов). Для относительно небольших массивов (состоящих из десятков – сотен элементов) вполне оправдано использование простейших алгоритмов сортировки. Рассмотрим два таких алгоритма. Они, в частности, интересны тем, что моделируют естественное поведение человека, который выполняет сортировку вручную. В дальнейшем полагаем, что элементы массива – числа, которые необходимо разместить по возрастанию. Таким образом, требуется решить следующую задачу. ЗАДАЧА 4.6. Задан массив а, состоящий из n чисел, размещенных в произвольной последовательности. Требуется упорядочить элементы массива в порядке возрастания. Математическая модель В результате сортировки элементы массива a должны удовлетворять неравенству ai ≤ ai+1 для всех i = 1, 2,…, n – 1. Вычислительная модель решения задачи зависит от метода сортировки. 4.5.1 Линейная сортировка Данный метод является наиболее простым для понимания и реализации. Среди всех n элементов массива a найдем минимальный (см. п.4.4.1 задача 4.3) и поменяем местами 1й элемент и найденный. Для этого запомним значение минимального элемента в переменной am, а его индекс – в переменной jm. Затем запишем значение 1-го элемента a[1] на место найденного элемента с индексом jm a[jm] := a[1],
(4.28а)
а на место 1-го элемента запишем найденное минимальное значение a[1] := am.
(4.28б)
Повторим поиск минимального значения среди элементов с индексами 2, 3,…, n и найденное минимальное значение запишем на место a[2]. Аналогично поступим с оставшейся частью массива. Обозначим: i – индекс элемента, куда записывается очередное найденное минимальное значение; j – индекс анализируемых элементов массива, среди которых отыскивается минимальный. Из рассмотренного выше принципа линейной сортировки следует, что для его реализации необходимо организовать два цикла: один цикл с параметром i является внешним, а второй цикл с параметром j является внутренним. Для каждого значения индекса i = 1, 2,…, n поиск минимального элемента производится в диапазоне индексов j = i, i+1,…, n. При этом не исключено, что i-й элемент является минимальным. Поэтому в теле цикла по i в самом начале следует выполнить присваивания am := a[i]; jm := i.
(4.29)
Тогда в цикле с параметром j анализ элементов массива можно начинать с индекса j = i+1 и заканчивать индексом n. При этом если i = n, то j = n+1 и внутренний цикл по j с начальным значением n+1 и конечным значением n просто не выполнится (см. в п.4.4.1 свойства цикла с параметром для признака to).
51
Проиллюстрируем операции сортировки на примере массива a = {3, 7, 2, 5, 9, 4, 6, 8}. Последовательность выполнения операций для 1-го и 2-го проходов внешнего цикла по i приведена на рис. 4.18. Нетрудно рассмотренную последовательность операций реализовать в виде следующего алгоритма, i=
1
3 j=
7
2
5
9
4
6
8
2
3
4
5
6
7
8
1-й прохід циклу i=1: am:=a[i] (am=3), jm:=i (jm=1) j=2: a[2]<am (7<3) j=3: a[3]<am (2<3) j=4: a[4]<am (5<2) ..................................... j=8: a[8]<am (8<2) -
ні так: am:=a[j] (am=2), jm:=j (jm=3) ні ні
Обмін елементів масиву: a[jm]:=a[i] (a[3]=3), a[i]:=am (a[1]=2) i=
2
2
7
j=
3
5
9
4
6
8
3
4
5
6
7
8
2-й прохід циклу i=2: am:=a[i] (am=7), jm:=i (jm=2) j=3: a[3]<am (3<7) j=4: a[4]<am (5<3) ..................................... j=8: a[8]<am (8<3) -
так: am:=a[j] (am=3), jm:=j (jm=3) ні ні
Обмін елементів масиву: a[jm]:=a[i] (a[3]=7), a[i]:=am (a[2]=3) i=
3
2
3
7
j=
5
9
4
6
8
4
5
6
7
8
Рис. 4.18 Послідовність лінійного сортування который представим в виде описания операторов. Блок УПБ 1 Нач 2 I1 3 A1 4 F1 5 A2 6 F2 7 D1 8 A3 9 E2 10 A4 11 O1 12 E1 13 Кон
Оператор Введення: n, a[‘masrand1.dat’] a0:=a Поч. циклу 1: i:=1 to n am:=a[i]; jm:=i Поч. циклу 2: j:=i+1 to n a[j] < am {Д8/Н9} am:=a[j]; jm:=j Кін. циклу 2: a[jm]:=a[i]; a[i]:=am Виведення: i:4,a0[i]:5,a[i]:5; Кін. циклу 1:
Алгоритм оперирует с целочисленными переменными i, j, n, jm. Данные, которые вводятся блоком 2 из файла masrand1.dat, также являются целыми числами, поэтому массивы a, a0 (размером 100 элементов) и переменная am – целого типа. Однако алгоритм может сортировать и вещественные числа при соответствующих изменениях типа a, a0, am и типа входных данных. С помощью блока 3 данные дублируются в массиве a0, что позволяет сопоставить с ними результаты сортировки. Собственно сортировка выполняется блоками 4–12, которые образуют внешний цикл с параметром i. В блоке 5 присваиваются начальные значения переменных am и jm в соответствии с выражениями (4.29). Блоки 6–9 образуют внутренний цикл с параметром j, который определяет минимальный элемент и его индекс 52
в не отсортированной части массива (с индексами j = i+1,…, n). После окончания этого цикла блок 10 меняет местами минимальный элемент и текущий i-й элемент в соответствии с операциями, подобными (4.28). С помощью блока 11 результаты сортировки выводятся в виде трех столбцов: 1-й столбец – номер элемента массива, 2-й столбец – значение массива до сортировки, 3-й столбец – полученный отсортированный массив. Нетрудно видеть, что число повторений тела внешнего и внутреннего циклов зависит только от числа элементов массива n и не зависит от первоначального размещения данных. Общее число повторений тела внутреннего цикла можно определить как сумму членов арифметической прогрессии: Параметр зовнішнього циклу i :1 Число повторень внутріш. циклу: Эта сумма равна
2 n–1
3 n–2
… n–3
n …
0.
n +1 1 ⋅ (n − 1 + 0) = ⋅ (n 2 − 1) . 2 2 n +1 1 ⋅ (n − 1 + 0) = ⋅ (n 2 − 1) . 2 2
То есть, объем вычислений возрастает со скоростью, стремящейся к n2, что подтверждается экспериментально. При последовательном запуске алгоритма для разных значений n в окне «Результаты выполнения алгоритма» в скобках получены следующие числа, которые характеризуют общий объем вычислений в виде количества обращений к блокам алгоритма Обсяг масиву n=10 n=20 n=40 n=80
Обсяг обчислень 96 310 1023 3687
Нетрудно видеть, что при увеличении объема массива в два раза объем вычислений увеличивается в число раз, которое стремится к 22. Приведенный алгоритм записан в файле LinSort.alg. Демонстрационная версия алгоритма, записанная в файле DemLinSort.alg (для n=10), выводит результаты сортировки в виде, подобном рис.4.18. 4.5.2 Сортировка вставками Как и ранее полагаем, что входной массив a состоит из n элементов. Данный метод обеспечивает следующее свойство: при анализе j-го элемента массива расположенный слева от него участок массива с индексами 1,…, j – 1 уже отсортирован по возрастанию. Поэтому если a[j – 1] ≤ a[ j],
(4.30)
то j-й элемент остается на своем месте, отсортированный участок расширяется за счет его включения и алгоритм переходит к анализу следующего элемента. Если же неравенство (4.30) не выполняется, то анализируемый элемент необходимо вставить в отсортированный участок на такое место i, чтобы слева от него находился элемент a[i – 1] ≤ a[j], а справа – элемент a[i + 1] > a[j]. Для этого все элементы, значение которых больше, чем a[j], необходимо сдвинуть на одну позицию вправо, чтобы освободить место для вставляемого элемента. Указанные операции выполняются для j = 2,…, n. Этот цикл является внешним, а сдвиг элементов в отсортированной части массива (для освобождения места вставляемому элементу) выполняется внутренним циклом. Рис. 4.19 иллюстрирует метод вставки на примере ранее рассмотренного массива a = {3, 7, 2, 5, 9, 4, 6, 8}. Первой операцией в теле внешнего цикла является сохранение значения (7) анализируемого элемента a[j] (то есть, a[2]) в переменной at (рис.4.19а). Поскольку это значение удовлетворяет неравенству (4.30) (3 < 7), то значение переменной at = 7 записывается на прежнее место (рис.4.19б). На данном проходе внешнего цикла внутренний цикл не выполняется ни разу, так как никакие элементы отсортированного участка (состоящие только из одного элемента a[1]=3) сдвигать вправо не требуется. Перед вторым проходом внешнего цикла (для j=3) отсортированный участок состоит уже из двух элементов: a[1]=3 и a[2]=7. На данном проходе анализируемый элемент a[3]=2 не удовлетворяет неравенству (4.30) и запомненное в переменной at значение 2 необходимо вставить перед элементом a[1]=3 (рис.4.19в). Для этого с помощью внутреннего цикла необходимо выполнить сдвиг элементов: a[3]:=a[2] (рис.4.19г) и a[2]:=a[1] (рис.4.19д). На освободившееся место 1-го элемента записывается значение переменной at=2 53
(рис.4.19е). Таким образом, к началу третьего прохода (для j=4) отсортированную часть массива образуют уже 1-й, 2-й и 3-й элементы. Далее обработка выполняется аналогичным способом.
4
5
6
7
8
3
7
2
5
9
4
6
8
7
at
1
2
3
4
5
6
7
8
3
7
2
5
9
4
6
8
7
at
1
2
3
4
5
6
7
8
3
7
2
5
9
4
6
8
2
at
1
2
3
4
5
6
7
8
3
7
7
5
9
4
6
8
1
2
3
4
5
6
7
8
3
3
7
5
9
4
6
8
1
2
3
4
5
6
7
8
2
3
7
5
9
4
6
8
2
at
а)
зовнішній цикл j=2
3
б)
в)
г)
д)
зовнішній цикл j=3
2
внутрішній цикл
1
е)
Рис. 4.19 Ілюстрація методу уставки Рассмотренный пример по существу описывает вычислительную модель алгоритма, которую необходимо дополнить следующими уточнениями. 1) Число повторений внешнего цикла известно, поэтому следует использовать цикл с параметром j = 2,…, n. 2) Число повторений внутреннего цикла неизвестно, причем возможны ситуации, когда он не должен выполниться ни разу, поэтому следует использовать цикл с предусловием. 3) Перепись элементов в отсортированной части массива с помощью внутреннего цикла следует производить «от конца к началу», то есть, уменьшая на 1 индекс элементов i на каждом проходе. 4) Условием для продолжения внутреннего цикла должно быть выполнение неравенства i > 0 (во избежание выхода за допустимый диапазон значений индекса) и выполнение неравенства a[i] > at. Поясним последнее условие. Выполнение данного неравенства свидетельствует о том, что анализируемый элемент a[j] (значение которого равно at) не должен находиться после i-го элемента, так как он меньше по величине и требование сортировки нарушается. Поэтому тело внутреннего цикла должно быть выполнено, в результате чего индекс i уменьшится на 1. Тогда на следующем проходе внутреннего цикла со значением at будет сравниваться значение элемента, более близкого к началу отсортированной части, значит меньшего по 54
величине. Цикл должен повторяться до тех пор, пока неравенство a[i] > at станет ложным, то есть будет справедливым неравенство a[i] ≤ at. Запись значения at на i+1-ую позицию обеспечивает соблюдение требований сортировки. В соответствии с вышесказанным алгоритм сортировки вставками можно описать следующими операторами Блок УПБ 1 Нач 2 I1 3 A1 4 F1 5 A2 6 W2 7 A3 8 E2 9 A4 10 E1 11 F3 12 O1 13 E3 14 Кон
Оператор Введення: n, a[‘masrand1.dat’] a0:=a Поч. циклу 1: j:=2 to n at:=a[j]; i:=j-1 Поч. циклу 2: (i>0) & (a[i]>at) a[i+1]:=a[i]; i:=i-1 Кін. циклу 2: a[i+1]:=at Кін. циклу 1: Поч. циклу 3: j:=1 to n Виведення: j:4, a0[j]:5, a[j]:5; Кін. циклу 3:
Блоки 1–3 аналогичны алгоритму линейной сортировки. Собственно сортировка выполняется внешним циклом, образованным блоками 4–10, в который вложен внутренний цикл, образованный блоками 6–9. Как видно из приведенного описания, для реализации данного алгоритма достаточно всего 7 блоков, тогда как алгоритм линейной сортировки состоит из 8 блоков. Однако общее число блоков (14) в данном варианте на один больше за счет того, что для вывода полученных результатов требуется организовать отдельный цикл (блоки 11–13). Следует также отметить, что задача сортировки в данном случае решена без использования ветвлений и структура алгоритма внешне выглядит как линейная. Приведенный алгоритм записан в файле InsSort.alg, а его демонстрационная версия (для n=10) – в файле DemInsSort.alg. При последовательном запуске алгоритма для разных значений n получены следующие оценки объема вычислений. Обсяг масиву Обсяг обчислень n = 10 57 n = 20 155 n = 40 461 n = 80 1580 Сравнение полученных результатов с результатами линейной сортировки свидетельствует о почти вдвое меньшем объеме вычислений. Таким образом, с помощью более компактного алгоритма достигнута существенно лучшая эффективность, что подтверждает важность тщательной алгоритмической проработки решаемой задачи. Применительно к задаче сортировки возможно дальнейшее улучшение эффективности, однако за счет усложнения принципов построения алгоритма, в частности, за счет использования рекурсии (см. ?). 4.5.3 Бинарный поиск Как отмечалось выше, сортировка и поиск являются взаимосвязанными операциями. Если массив данных не упорядочен, то для отыскания конкретных данных массив необходимо просматривать элемент за элементом с самого начала, пока не будет найден интересующий элемент. Такой поиск называют линейным. Если массив состоит из n элементов, то число операций при линейном поиске в среднем пропорционально n/2. Для упорядоченного массива число операций поиска интересующего элемента можно значительно сократить, используя так называемый бинарный или двоичный поиск. Таким образом, существует дилемма: - либо не затрачивать машинное время на сортировку массива, но при этом на обработку каждого запроса на поиск будет расходоваться значительное время; - либо затратить необходимое время на сортировку массива, но сократить время поиска. При создании службы поиска информации в больших массивах, которая ориентирована на массовое поступление запросов, предпочтение отдается второму варианту. ЗАДАЧА 4.7. Задан массив a, состоящий из n элементов, упорядоченных по возрастанию. Найти индекс элемента, имеющего значение tst. Если такого элемента нет, присвоить индексу значение 0. Обеспечить в среднем сокращение числа операций по сравнению с линейным поиском. 55
Математическая модель Обозначим: A – множество значений элементов массива a; tst – заданное значение; ind – искомый индекс элемента.
такой, что aind = tst если tst ∈ A ind = 0 если tst ∉ A
(4.31)
В этом выражении знак «∈» обозначает «принадлежит множеству», а знак «∉» – «не принадлежит множеству». Вычислительная модель Принцип бинарного поиска иллюстрируется рис.4.20 на примере массива a, состоящего из n=8 элементов a={1, 4, 6, 7, 9, 12, 17, 18}. Обозначим imin, imax –соответственно минимальный и максимальный индексы, в пределах которых выполняется поиск. Пусть значение, индекс которого необходимо найти, равно tst=9. Вначале областью поиска является весь массив, то есть imin=1, imax=n=8. Вычислим проверяемый индекс в центре области поиска как ind = (imin + imax):2, (4.32) где «:» – знак целочисленного деления. В данном случае ind=4 (рис.4.20а).
tst=9
1
4
6
7
9 12
17
18
1
2
3
4
5
6
7
8
5
6
7
8
5
tst=5
1
2
3
1
2
3
4
5
6
7
8
3 2
3
imin
imax
ind
a[ind]<>tst
1
8
(1+8):2=4
7<9
а)
5
8
(5+8):2=6
12>9
б)
5
5
(5+5):2=5
9=9
в)
1
8
(1+8):2=4
7>5
г)
1
3
(1+3):2=2
4<5
д)
3
3
(3+3):2=3
6>5
е)
3
2
imin>imax
не знайдено
ж)
Рис. 4.20 Ілюстрація бінарного пошуку Соответствующий элемент массива a[4]=7 меньше заданного значения tst=9. Поэтому диапазон индексов 1..4 можно исключить из дальнейшего поиска и минимальный индекс принять равным imin = ind + 1, то есть, imin = 4 + 1 =5. Для вычисления нижней границы поиска imin целесообразно описать и использовать функцию
ind + 1 для a[ind ] < tst fmin(ind ) = для a[ind ] ≥ tst imin
(4.33а)
В системе АЛГОРИТМ ее описание имеет следующий вид fmin(ind) := ifa(a[ind] < tst, ind + 1, imin)
(4.33б)
Переменные imin, ind и сама функция имеют целочисленный тип, переменная tst имеет такой же тип, как и элементы массива a. В соответствии с выражением (4.32), новое значение проверяемого индекса равно ind=(5 + 8):2 = 6 (рис.4.20б). Значение элемента массива a[6]=12 больше заданного значения tst=9. Поэтому диапазон индексов 6..8 можно исключить из дальнейшего поиска и максимальный индекс принять равным imax = ind – 1, то есть, imax = 6 – 1 = 5. Для вычисления верхней границы поиска imax целесообразно описать и использовать функцию
ind −1 для a[ind ] > tst fmax(ind ) = imax для a[ind ] ≤ tst
(4.34а)
В системе АЛГОРИТМ ее описание имеет следующий вид fmax(ind) := ifa(a[ind] > tst, ind - 1, imax).
(4.34б) 56
В данном случае новый проверяемый индекс равен ind=(5 + 5):2 = 5 и соответствующий элемент a[5] = 9 = tst. Следовательно, поиск закончен и искомый индекс элемента равен ind= 5 (рис.4.20в). Рассмотрим теперь поиск значения tst=5, которого нет в массиве. На первом шаге поиска аналогично предыдущему варианту выбран индекс ind=4 (рис.4.20г). Поскольку для 4-го элемента имеет место неравенство a[4] = 7 > tst, то вычисляется новая верхняя граница imax = ind – 1 = 4 – 1 = 3. В соответствии с (4.32) новое значение индекса равно ind=(1 + 3):2 = 2 (рис.4.20д). Однако a[2] = 4 < tst и должна быть изменена нижняя граница imin = ind + 1 = 2 + 1 = 3. Таким образом, нижняя и верхняя границы совпали (рис.4.20е). Вместе с тем, элемент a[3] = 6 > tst и должна быть изменена верхняя граница imax = ind – 1 = 3 – 1 = 2. Но при этом оказалось imin > imax, что может служить признаком прекращения дальнейшего поиска (рис.4.20ж). Нетрудно видеть, что область поиска на каждом шаге сокращается в среднем в два раза. Следовательно, общее число шагов можно оценить как log 2 n (в рассмотренном примере log 2 8 =3). При отсутствии в массиве элемента поиска добавляется еще один шаг. На практике, число шагов может быть даже меньше, если оказалось, что индекс, вычисляемый по формуле (4.32), как раз соответствует искомому элементу (например, для tst=7 искомый индекс ind = 4 определяется за один шаг). Поскольку при циклическом повторении описанных операций поиска они должны выполниться хотя бы один раз, то целесообразно использовать цикл с послеусловием. Рассмотренный принцип бинарного поиска реализован алгоритмом, который представлен на рис.4.21.
Рис. 4.21 Алгоритм бінарного пошуку Алгоритм оперирует с переменными imax, imin, ind, n, tst (целого типа) и переменной found (логического типа), которая используется как признак успешного завершения поиска. Исходные данные записываются в массив a из 100 элементов целого типа. Кроме того, алгоритм оперирует с пользовательскими функциями fmin(ind), fmax(ind), которые описаны выражениями (4.33б), (4.34б), и пользовательской функцией find(found):= ifa(found, ind, 0), которая возвращает значение найденного индекса (для found = true) или 0 (для found = false). Блоки 2 и 3 обеспечивают ввод числа элементов массива (n ≤ 20) и чтение данных из файла в массив (в данном случае файл Sorted20.dat содержит 100-элементный массив целого типа, у которого первые 20 элементов упорядочены по возрастанию). Ввод (блок 3) выполняется только при первом запуске алгоритма. Блок 4 вводит заданное значение tst. Блок 5 выполняет начальные присваивания перед циклом поиска, состоящим из блоков 6–11. Блок 7 вычисляет текущее значение индекса ind в соответствии с выражением (4.32). Блоки 8, 9 проверяют равенство a[ind] = tst и в случае его выполнения присваивают признаку found значение true и обеспечивают переход в конец цикла. В случае невыполнения указанного равенства управление передается блоку 10, который изменяет границы поиска в соответствии с выражениями (4.33), (4.34). Блок 11 конца цикла обеспечивает выход из процесса поиска либо при истинности признака found (элемент найден), либо при выполнении неравенства imin > imax (элемент не найден). Блок 12 выводит результаты поиска. Приведенный алгоритм записан в файл BinSearch.alg. 4.6 Иерархические структуры
57
4.6.1
Основные определения Иерархические структуры широко используются при построении сложных алгоритмов.
В соответствии с общепринятой практикой программирования, целесообразно выделить из сложного алгоритма отдельные фрагменты, каждый из которых решает какую-либо частную задачу. Это особенно полезно, когда приходится многократно решать одну и ту же частную задачу при различных исходных данных. Однако разбиение алгоритма на относительно самостоятельные фрагменты целесообразно даже в том случае, когда они выполняются однократно. Отдельные фрагменты легче отлаживать и проще изменять их функциональность, что в конечном итоге повышает эффективность процесса создания алгоритма. Подобное структурирование алгоритма основано на понятии процедуры и блока вызова процедуры, которые не следует отождествлять друг с другом. Понятие процедуры относится к выделенной взаимосвязанной группе блоков, которые решают некоторую частную задачу. Процедуре присваивается собственное имя и она оформляется аналогично любому алгоритму, то есть, имеет один вход (Начало) и один выход (Конец). Таким образом, процедура представляет собой целостную структуру. Обращение к алгоритму процедуры из вызывающего алгоритма осуществляется с помощью блока, который изображается графическим символом ПРОЦЕДУРА (см. п.4.1.2). Условное обозначение блока (УОБ) состоит из буквы Р и 1-2-х цифр или букв, задаваемых пользователем по своему усмотрению. Оператор блока ПРОЦЕДУРА имеет вид Ім‘я_процедури(необов‘язковий список параметрів) Блок ПРОЦЕДУРА обеспечивает передачу управления блоку Начало той процедуры, имя которой указано в операторе. После ее выполнения управление возвращается блоку ПРОЦЕДУРА вызывающего алгоритма. Далее управление передается по адресу, который указан в параметре msb. Вызывающий алгоритм и вызываемая процедура образуют иерархическую структуру, пример которой схематически показан на рис. 4.22. На рис. 4.22а изображена схема передачи управления. В основном алгоритме два блока ПРОЦЕДУРА, обозначенные как Р1 (с метками g и h) вызывают одну и ту же процедуру с именем Proced1. Несмотря на то, что они оба передают управление блоку Начало процедуры, блок Конец возвращает управление именно тому блоку Основного алгоритма, который вызвал процедуру Proced1: в первом случае – блоку g, во втором случае – блоку h. Поскольку параметры msb этих блоков равны их меткам, увеличенным на 1, то управление передается следующим за ними блокам. Процедура Proced1 в данном примере выполняет функцию подпрограммы. В свою очередь, она с помощью блока Р2 (метка m) и блока Р3 (метка n) вызывает соответственно процедуры с именем Proced2 и Proced3. 0-й рівень
1-й рівень
2-й рівень
Основний
Proced1
Proced2
Нач
Нач
Нач
g
Основний
0-й рівень
Proced1
1-й рівень
m
Р1
Р2 g+1
h
Кон m+1
Proced3
n
Р1 h+1
Кон
Нач
Р3
Proced2
Proced3
2-й рівень
n+1
Кон
Кон
б)
а) Рис. 4.22 Приклад ієрархічної структури Иерархия процедур определяется тем, как они вложены друг в друга. Основной алгоритм всегда образует 0-й уровень иерархии. В примере, изображенном на рис. 4.22, в основной алгоритм вложена одна процедура Proced1, которая образует 1-й уровень иерархии. В свою очередь, в Proced1 вложены две процедуры Proced2 и Proced3, которые образуют 2-й уровень иерархии. Номера уровней характеризуют «глубину» вложения, поэтому 0-й уровень является самым высоким. В данном случае, иерархическая связь процедур показана на рис. 4.22б. Она определяет взаимную доступность процедур для вызова, которая обозначена простыми стрелками, и доступность данных, которая показана контурными стрелками.
58
Для вызывающего алгоритма доступны все непосредственно вложенные в него процедуры. В рассмотренном примере Основной алгоритм может вызывать процедуру Proced1, но для него недоступны процедуры Proced2, Proced3. Последние доступны только из процедуры Proced1. Допускается (но не рекомендуется) обращение друг к другу процедур одного и того же уровня иерархии, если они вложены в один и тот же алгоритм предыдущего уровня. То есть, в приведенном примере Proced2 может вызывать Proced3 и наоборот. Особый случай представляет вызов процедурой самой себя, который называется рекурсией. Он отдельно рассматривается в п.?. В каждой процедуре при необходимости могут быть использованы свои внутренние данные, которые называются локальными. Они должны быть описаны аналогично глобальным данным Основного алгоритма (см. п.2.6.1 и п.2.6.2). Доступность данных определяется следующими правилами. 1.
Для вызывающего алгоритма недоступны локальные данные вызываемых процедур.
2.
Для каждой процедуры доступны не только ее локальные данные, но и данные вызывающих процедур по всей иерархической цепочке вверх, вплоть до Основного алгоритма, то есть, включая глобальные данные.
Эти правила называются локализацией имен и необходимо их учитывать во избежание ошибок, которые проявляются при выполнении алгоритма, но не обнаруживаются при диагностировании его логической структуры. Благодаря указанным правилам можно использовать для локальных данных имена, которые уже использованы на более высоких уровнях иерархии. При определении текущего значения данных вначале просматривается список локальных данных. Если найдено интересующее имя, то значение выбирается из этого списка и дальнейший поиск прекращается. Если в списке локальных данных нет интересующего имени, то просматривается список данных вызывающего алгоритма и т.д., вплоть до Основного алгоритма. Пусть, например, в Основном алгоритме и вызываемой процедуре Р1 используются циклы с параметром i. Основний УПБ Оператор Нач ……………………. F1 Поч. циклу 1: i:=1 to n ……………………. P1 P1 ……………………. Е1 Кін. циклу 1: …………………… Кон
Процедура Р1 УПБ Оператор Нач ……………………. F1 Поч. циклу 1: i:=1 to m ……………………. E1 Кін. Циклу 1: ……………………. Кон
При обращении к процедуре Р1 значение глобальной переменной i в Основном алгоритме равно, например, 2. В процедуре локальная переменная i изменяется в диапазоне от 1 до m. Поскольку она описана в списке локальных переменных, то ее значение выбирается из этого списка и до поиска в списке глобальных переменных дело не доходит. Поэтому ее значение как глобальной переменной (2) не изменяется. Вместе с тем, после возвращения из процедуры Р1 в Основной алгоритм локальные данные процедуры Р1 становятся недоступными и поиск переменной i выполняется только в глобальном списке. Таким образом, циклы в Основном алгоритме и в процедуре Р1 выполняются независимо и не мешают друг другу. 4.6.2 Создание процедур Иерархическая структура алгоритма формируется при объявлении процедур. Объявление вложенной процедуры выполняется следующим способом. •
Щелкните на закладке алгоритма, для которого необходимо создать вложенные процедуры. Тем самым активизируется вызывающий алгоритм. Первоначально существует только Основной алгоритм и имеется только одна закладка «Основной».
•
Выполните команду меню «Описание | Объявление процедур…». Откроется диалоговое окно «Объявление процедур, вложенных…». Вид этого окна показан на рис.4.23.
•
В строку «Имя вложенной процедуры:» введите имя процедуры, которая должна быть вложена в вызывающий алгоритм. Нажмите кнопку «Добавить в список». Введенное имя появится в окне списка. Повторите последние две операции для формирования остальных вложенных процедур, если их несколько. После составления списка всех вложенных процедур нажмите кнопку «Создать закладки». Закройте окно кнопкой «Закрыть».
• • • •
59
Рис. 4.23 Вікно для оголошення процедур
В результате этих операций над полем алгоритма появляются закладки объявленных процедур. Аналогично объявляются более «глубокие» вложения. Для каждой из объявленных процедур необходимо ввести описания и создать соответствующий алгоритм. •
Щелкните на закладке формируемой процедуры.
•
Для ввода комментария к процедуре откройте окно «Объявление процедур…» с помощью меню «Описание | Объявление процедур…». В строку «Комментарий к процедуре…» введите текст комментария. Нажмите кнопку «Изменить комментарий» и закройте окно нажатием кнопки «Закрыть».
•
Если в процедуре предусматриваются локальные переменные, опишите их аналогично описанию глобальных переменных в Основном алгоритме (см. п.2.6.1).
•
Если в процедуре предусматриваются локальные массивы, опишите их аналогично описанию глобальных массивов в Основном алгоритме (см. п.2.6.2).
•
Постройте алгоритм процедуры аналогично синтезу Основного алгоритма (см. п.4.1.5).
Единственная особенность алгоритмов процедур по сравнению с Основным алгоритмом проявляется в блоке Начало, который видоизменяется, если процедура использует параметры. Рассмотрим разновидности процедур. 4.6.3 Процедура без параметров Простейшим вариантом организации иерархической структуры является вариант использования процедур без параметров и без локальных данных. В этом случае в процедуру фактически переносится фрагмент Основного алгоритма и все переменные и массивы являются глобальными. В операторе блока вызова процедуры указывается лишь имя вызываемой процедуры. В блоке Начало процедуры ничего не вводится. Таким образом, блок-схема процедуры, в принципе, ничем не отличается от блок-схемы Основного алгоритма. Все исходные данные для выполнения процедуры и полученные результаты содержатся в глобальных переменных и массивах. Очевидный недостаток такого варианта заключается в том, что промежуточные данные, которые используются в процедуре для решения частной задачи и фактически не нужны Основному алгоритму, «засоряют» список глобальных данных. Поэтому их целесообразно исключить из списка глобальных данных и описать в процедуре как локальные данные. Однако такое изменение не устраняет более существенный недостаток процедуры без параметров. Он заключается в том, что все входные данные и результаты попрежнему остаются глобальными и, тем самым, «привязывают» процедуру к Основному алгоритму, не позволяя использовать ее для решения аналогичных задач в других алгоритмах. 60
Тем не менее, создание процедуры без параметров является вполне оправданным приемом структурирования сложных алгоритмов. Рассмотрим простой пример иерархической структуры, использующей процедуры без параметров. ЗАДАЧА 4.8. На плоскости заданы 4 точки с координатами (x1,y1), (x2,y2), (x3,y3), (x4,y4). Определить, к какой из точек ближе точка с координатами (x,y). Как известно, расстояние r между двумя точками (xa,ya) и (xb,yb) определяется как r = ( xa − xb) 2 + ( ya − yb) 2 .
Однако для решения задачи важно не столько конкретное значение расстояния, сколько сопоставление «больше–меньше». Поскольку при увеличении подкоренного выражения расстояние также монотонно увеличивается, то в интересах упрощения вычислений можно оценивать меру удаленности точек друг от друга просто суммой квадратов разности их координат
r = ( xa − xb) 2 + ( ya − yb) 2 .
(4.35)
Поставленную задачу можно решить различными способами. Рассмотрим вычислительную модель, которая весьма просто реализуется с помощью вложенных процедур. 1-й шаг. Вычислим в соответствии с выражением (4.35) расстояния r1, r2 между точкой (x,y) и точками (x1,y1), (x2,y2). Присвоим переменной min минимальное из расстояний, а точке (xr,yr) – координаты соответствующей ближайшей точки. 2-й шаг. Проверим, не является ли более близкой точка (x3,y3), вычислив для нее расстояние r3. Если r3<min, то присвоим переменной min значение r3 и координатам (xr,yr) – значения координат (x3,y3). 3-й шаг. Проверим, не является ли более близкой точка (x4,y4), вычислив для нее расстояние r4. Если r4<min, то присвоим координатам (xr,yr) – значения координат (x4,y4). В результате последовательного выполнения этих шагов переменные (xr,yr) принимают значения координат ближайшей точки. Если рассматривать выполнение шагов как решение как решение отдельных частных задач, то можно оформить их в виде трех процедур соответственно с именами Р1, Р2, Р3. С точки зрения организации иерархической структуры возможны два варианта. А) Все три процедуры создаются как вложенные в Основной алгоритм, который вначале вызывает Р1, затем Р2 и, наконец, Р3. Основний Этот вариант показан на рис. 4.24а. Б) Последовательность выполнения процедур задается их иерархией. Очевидно, что для выполнения 3-го шага Основний Р3 предварительно должен быть выполнен 2-й шаг, а для выполнения 2-го шага – 1-й шаг. Поэтому логично в основной алгоритм вложить процедуру Р3, в нее – процедуру Р2, а в последнюю – процедуру Р1. Этот вариант показан на рис. 4.24б. Причем в теле Р2 Р1 Р2 Р3 процедуры Р3 необходимо прежде всего вызвать процедуру Р2. Аналогично в теле процедуры Р2 необходимо прежде всего вызвать процедуру Р1.Таким образом обеспечивается требуемая Р1 последовательность шагов вычислений. Поскольку вычисление расстояния по формуле (4.35) б) а) должно выполняться многократно, то целесообразно описать пользовательскую функцию Рис. 4.24 Варіанти ієрархії
f ( xt , yt ) := sqr ( xt − x ) + sqr ( yt − y ) , в которую в качестве аргументов подставляются координаты 1-ой,…,4-ой точек. Данные, с которыми оперирует алгоритм, целесообразно распределить следующим образом: - описать в Основном алгоритме в качестве глобальных переменных исходные данные x, y, x1, y1, x2, y2, x3, y3, x4, y4 и результаты решения задачи xr, yr; - описать в процедуре Р3 в качестве локальных переменных min, r (они будут доступны и для вложенных процедур Р2 и Р1); - описать в процедуре Р1 в качестве локальных переменных r1, r2. Все переменные в данной задаче должны иметь вещественный тип (:R). Координаты фиксированных точек, в принципе, могут быть любыми, однако для определенности их можно задать равными: x1=0, y1=0, x2=0, y2=1, x3=1, y3=0, x4=1, y4=1. 61
Алгоритм решения рассмотренной задачи изображен на рис. 4.25 в виде отдельных блок-схем для Основного алгоритма и всех процедур.
Рис. 4.25 Алгоритм вирішення задачі 4.8
Каждая блок-схема достаточно проста и не нуждается в дополнительных пояснениях. Алгоритм сохранен в файле NearPoint.alg. При вводе координат анализируемой точки (0.4, 0.7) он выдает результат Найближчою до (0.40, 0.70) є точка (0, 1) В заключение отметим, что подобная задача встречается при кодировании и декодировании сигналов. Кроме того, она представляет и академический интерес, так как в режиме трассировки позволяет наглядно продемонстрировать процесс вызова процедур и локализацию имен. Данный пример также полезен для уяснения сущности рекурсивного вызова процедур.
62
4.6.4 Процедуры с параметрами В общем случае процедура может иметь один или несколько параметров, которые образуют список параметров. Параметры отделяются друг от друга запятой, а весь список заключается в скобки. Параметры, которые указываются в блоке ПРОЦЕДУРА вызывающего алгоритма (см. п.4.6.1), называются фактическими. В системе АЛГОРИТМ допускается использовать не более 8 параметров. В блоке НАЧАЛО вызываемой процедуры (то есть, процедуры с тем именем, которое указано в операторе ПРОЦЕДУРА вызывающего алгоритма), в качестве оператора также необходимо ввести в скобках (Список параметрів) где элементы списка разделяются запятыми. Параметры, которые перечисляются в блоке НАЧАЛО вызываемой процедуры, называются формальными. Список формальных параметров по числу и типу параметров должен полностью соответствовать списку фактических параметров. В роли формальных параметров могут выступать переменные и массивы. Они должны быть также описаны в списках локальных переменных и массивов данной процедуры. -
По своему назначению параметры разделяются на две группы: параметры-значения; параметры-переменные.
Параметры-значения Такие параметры служат входными данными для процедуры. Независимо от того, изменяются или нет значения формальных параметров в ходе выполнения процедуры, значения фактических параметров, которые были в момент обращения к процедуре, остаются неизменными после ее выполнения. Если формальным параметром является переменная, то соответствующим фактическим параметром может быть: - константа; - переменная; - элемент массива; - выражение, которое возвращает значение требуемого типа. Если формальным параметром является массив, то и фактический параметр должен быть массивом той же размерности и типа. Нетрудно видеть, что параметры-значения нельзя использовать для передачи в вызывающий алгоритм результатов выполнения процедуры. Для этого, как и в случае процедуры без параметров, приходится использовать глобальные переменные и массивы. Параметры-переменные С помощью таких параметров можно не только передать в процедуру входные данные, но и возвратить в вызывающий алгоритм полученные результаты. Вследствие этого фактическими параметрами-переменными могут быть только переменные и массивы. В списке формальных параметров элемент списка, соответствующий параметру-переменной, должен иметь форму var ім‘я_параметра где признак var и имя параметра должны разделяться пробелом. Если параметром является переменная, то при выполнении блока КОНЕЦ процедуры полученное значение переменной (формального параметра) присваивается соответствующей переменной вызывающего алгоритма (фактическому параметру). Если параметром является массив, то при вызове процедуры в нее передается не сам массив как таковой, а его имя, которое подставляется вместо имени локального массива – формального параметра. Таким образом, после завершения процедуры массив, переданный как фактический параметр, уже имеет значения элементов, которые изменены в результате выполнения процедуры. В этом случае для локального описания массива в процедуре достаточно ввести его имя и размерность, равную 1 (одномерный массив из одного элемента), независимо от структуры массива – фактического параметра. 63
Применение параметров позволяет сделать процедуру переносимой, то есть использовать ее в различных алгоритмах, где требуется решать соответствующие частные подзадачи. Она будет работать независимо от того, какие имена имеют фактические параметры вызывающего алгоритма. То есть, в данном случае процедура оперирует только с локальными данными и не нуждается ни в каких других данных. В качестве примера рассмотрим алгоритм линейной сортировки (см. п.4.5.1), в котором собственно операции сортировки выделены в процедуру Sort. В данном случае Основной алгоритм описывается следующей последовательностью операторов: Основний Блок УПБ Оператор 1 Нач 2 I1 Введення: n 3 F1 Поч. циклу 1: i:=1 to n 4 A1 m0[i]:=10*random 5 E1 Кін. циклу 1: 6 A2 m:=m0 7 P Sort(n,m) 8 F2 Поч. циклу 2: i:=1 to n 9 O1 Виведення: i:4:0, m0[i]:7:3, m[i]:7:3; 10 E2 Кін. циклу 2: 11 Кон Змінні: i, n тип :I, початкове значення 0 Масиви: m, m0 тип :R, вимірність 1..100 Блок 2 вводит число n элементов массива. Блоки 3 – 5 формируют массив m0 из n элементов, которые представляют собой случайные числа в диапазоне 0..10. Блок 6 копирует m0 в рабочий массив m. Блок 7 осуществляет вызов процедуры Sort, параметрами которой являются число элементов массива n и сам массив m. Блоки 8 – 10 выводят номер элемента, значение элемента несортированного массива и значение элемента отсортированного массива. Процедура сортировки описывается следующей последовательностью операторов: Процедура Sort Блок УПБ Оператор 1 Нач (n,var a) 2 F1 Поч. циклу 1: j:=1 to n-1 3 F2 Поч. циклу 2: i:=j+1 to n 4 D1 a[i] < a[j] {Д5/Н6} 5 A1 at:=a[i]; a[i]:=a[j]; a[j]:=at 6 E2 Кін. циклу 2: 7 E1 Кін. циклу 1: 8 Кон Змінні: i, j, n тип :I, at тип :R, початкові значення 0 Масив: a тип :R, вимірність 1..1. Первым параметром процедуры Sort является параметр-значение n, определяющий число элементов массива. Второй параметр является параметром-переменной, который передает в процедуру несортированный массив и возвращает в Основной алгоритм этот же массив с элементами, упорядоченными по возрастанию. Нетрудно видеть, что процедура обрабатывает массив с числом элементов, равным n, хотя формально массив a описан как массив из одного элемента. Поэтому процедура Sort пригодна для использования с любым другим вызывающим алгоритмом, в котором формируется массив из элементов вещественного типа. Приведенный алгоритм записан в файле PrcdSort.alg. 4.6.5 Рекурсия Вернемся к организации иерархической структуры для решения задачи 4.8 поиска одной из 4-х точек, которая находится ближе других к заданной точке (см. п.4.6.3). Если посмотреть на алгоритмы процедур, изображенные на рис. 4.25, то нетрудно увидеть почти полное сходство процедуры Р3 (1-й уровень иерархии) с процедурой Р2 (2-й уровень иерархии). В данном случае процедура Р3 вызывает процедуру Р2, которая выполняет такие же действия, как и вызывающая процедура Р3, но над другими данными. Если оформить Р3 и Р2 как процедуры с параметрами, то можно сделать их совершенно одинаковыми. В таком случае возникает вопрос, можно ли вообще обойтись без процедуры Р2 и вместо нее из процедуры Р3 вызывать опять процедуру Р3? При определенных условиях такой рекурсивный вызов допускается.
64
Рекурсия – это такой способ организации алгоритма, при котором вызов процедуры (или функции) осуществляется из тела этой же самой процедуры (или функции). В принципе, коль скоро допускается взаимный вызов процедур на одном и том же уровне иерархии, то процедура может обращаться сама к себе. Однако требуется выполнение следующего важного условия. Во избежание зацикливания такого рекурсивного процесса необходимо обеспечить, чтобы при достижении некоторого тривиального результата можно было выйти из процедуры (или функции) без обращения к ней. Традиционным примером рекурсивного выполнения служит вычисление факториала n!=1⋅2⋅…⋅(n-1)⋅n, когда при i-ом обращении к процедуре возвращается результат, равный i⋅(i–1)!. Для получения этого результата осуществляется вызов процедуры вычисления факториала числа (i–1). Процесс продолжается до тех пор, пока не окажется i–1=0. Для получения результата 0!=1 уже не требуется в теле процедуры опять обращаться к ней. Нетрудно реализовать рекурсивное вычисление факториала с помощью пользовательской функции. На страничке «Пользовательская» диалогового окна «Список функций» (см. подраздел 3.5) опишите функции: _f1(n):=1 _f2(n):=n*fact(n-1) fact(n):=iff(n=0,_f1(n),_f2(n)) Этого описания достаточно для вычисления факториала при нажатии кнопки «Вычислить». Нетрудно видеть, что при n>0 функция fact(n) обращается к функции _f2(n), которая, в свою очередь, обращается к функции fact(n–1). При n=0 функция fact(n) обращается к функции _f1(n), которая возвращает 1, то есть, рекурсивное обращение прекращается. На основе указанной функции можно составить простейший алгоритм вычисления факториала: Блок УПБ Оператор 1 Нач 2 I1 Введення: n 3 A1 f:=fact(n) 4 O1 Виведення: ‘n!=’,f; 5 Кон Опис змінних: n:R=0.0, f:R=0.0 По сравнения с примером вычисления факториала путем организации цикла (см. задачу 4.2 в п.4.4.1) использование рекурсии позволило получить более изящный и компактный алгоритм. Он записан в файле FactFnc.alg. В данном случае циклическое повторение вычислений обеспечивается самим рекурсивным процессом. В процессе рекурсии, перед тем как передавать в процедуру значения фактических параметров, значения ее локальных переменных запоминается в стеке, а при выходе из процедуры восстанавливается из стека. Стек представляет собой структуру данных типа массив, обращение к которому организовано по принципу «последним записал, первым прочитал». Перед записью адрес стека увеличивается на 1, а после чтения – уменьшается на 1. В системе АЛГОРИТМ объем стека допускает до 15 повторных обращений к процедуре и сохранение до 16 локальных переменных при каждом обращении. Что касается рекурсивного использования процедуры (а не функции, рассмотренной выше) для вычисления факториала, то такой подход нецелесообразен, поскольку не приводит к упрощению алгоритма. Однако существует много задач, которые эффективно решаются именно с помощью рекурсии. К числу таких задач относится быстрая сортировка, алгоритм которой записан в файле Qsort.alg. При большом объеме массива данных она обеспечивает выигрыш в общем количестве операций по сравнению с алгоритмом линейной сортировки, рассмотренном в п. 4.6.4 и записанном в файле PrcdSort.alg. В частности, для сортировки массива из 100 элементов алгоритму Qsort.alg требуется 3660 операций, тогда как алгоритму PrcdSort.alg – 7799 операций.
65