Раздел 2 — Алгоритм и его запись

От условия задачи к алгоритму, который можно проверить до того, как написан код

Прогресс курса Раздел 2 из 28

Что вы освоите в этом разделе

2 академических часа теории, практической работы в разделе нет. Раздел заканчивается тестом на портале; следующий раздел открывается после теста. Алгоритмы этого раздела записываются без языка программирования — в разделах 4–6 они станут программами на C.
01

Этапы решения задачи

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

ЭтапЧто делаетсяРезультат
1. Постановкачто дано, что найти, какие ограничения; непонятное уточняется у заказчикаточное условие
2. Модельвеличины, их типы и допустимые значения, формулы, связывающие ихматематическая модель
3. Алгоритмпоследовательность действий, которая по входным данным получает результатблок-схема или псевдокод
4. Программазапись алгоритма на языке программирования, сборкаисполняемый файл
5. Тестированиезапуск на примерах с заранее известным ответомтаблица тестов, найденные ошибки

Пример. Условие: «На экскурсию едут N учеников. В автобусе c мест. Сколько автобусов заказать?»

Тестовые примеры придумываются до программы, вместе с моделью. Пока программы нет, ответ считается по смыслу задачи, а не «как получилось»: так тест проверяет программу, а не повторяет её ошибку.
02

Жизненный цикл программы и программный продукт

Решение учебной задачи заканчивается тестами. Программа, которой пользуются люди, живёт дальше. Жизненный цикл — всё время от идеи программы до отказа от неё:

  требования ──► проектирование ──► реализация ──► тестирование ──► внедрение ──► сопровождение ──► вывод из эксплуатации
       ▲                                                                                │
       └──────────────────── новые требования, найденные ошибки ◄───────────────────────┘

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

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

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

Характеристика качестваВопрос, который она задаёт
Функциональная пригодностьделает ли программа то, что нужно, и правильно ли
Надёжностьчто будет при неверном вводе, сбое сети, переполнении диска
Производительностьсколько времени и памяти нужно на реальных объёмах данных
Удобство использованияможет ли пользователь разобраться без автора
Сопровождаемостьможет ли другой программист найти и исправить ошибку
Переносимостьсоберётся ли и заработает ли в другой ОС
Защищённостьможно ли через программу получить чужие данные или управление

Эти характеристики перечислены в стандарте ГОСТ Р ИСО/МЭК 25010. В курсе каждая из них превращается в конкретное требование к практической работе: проверка ввода — надёжность, оценка числа действий — производительность, имена по смыслу — сопровождаемость, сборка без предупреждений по стандарту C11 — переносимость.

03

Алгоритм и его свойства

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

СвойствоЧто значитНарушение
Дискретностьалгоритм состоит из отдельных шагов, выполняемых по очереди«найдите автобусы»
Определённостькаждый шаг понимается однозначно«поделите и округлите» — вверх или вниз?
Конечностьалгоритм заканчивается за конечное число шагов«начиная с 0, прибавлять 2, пока число не станет равно 7» — никогда не станет
Результативностьна любых допустимых данных — результат или сообщение об ошибкенет ответа на N = 0 или c = 0
Массовостьрешает класс задач, а не один случайработает только для N = 50, c = 45

Алгоритм Евклида — наибольший общий делитель (НОД) двух натуральных чисел. Он основан на свойстве: НОД(a, b) = НОД(b, a mod b), где a mod b — остаток от деления a на b; и НОД(a, 0) = a. Словесная запись:

1. Ввести натуральные a и b.
2. Пока b не равно 0, повторять:
   2.1. r = остаток от деления a на b.
   2.2. a = b.
   2.3. b = r.
3. Вывести a.

Конечность здесь надо доказать, а не предположить: остаток всегда меньше делителя, поэтому b на каждом шаге уменьшается хотя бы на 1, оставаясь неотрицательным, — значит, рано или поздно станет 0. Такое рассуждение — почему повторение закончится — требуется в курсе для каждого цикла.

04

Блок-схемы по ГОСТ 19.701-90

Символы блок-схем в России установлены стандартом ГОСТ 19.701-90 «Схемы алгоритмов, программ, данных и систем». Для алгоритмов курса нужно семь символов. На страницах курса они нарисованы текстом упрощённо; соответствие — в последнем столбце.

СимволФормаНазначениеНа странице
Терминаторпрямоугольник со скруглёнными концаминачало и конец( Начало )
Данныепараллелограммввод и вывод/ Ввод a, b /
Процесспрямоугольниквычисление, присваивание[ r = a mod b ]
Решениеромб, один вход и два выхода «да» / «нет»проверка условия< b ≠ 0 ? >
Подготовкашестиугольникзаголовок цикла со счётчиком<< i = 1..n >>
Предопределённый процесспрямоугольник с двойными боковыми сторонамивызов подпрограммы (функции)[| НОД(a, b) |]
Соединительокружность с меткойразрыв линии, продолжение на другой странице(A)

Правила: поток идёт сверху вниз и слева направо — в этих направлениях стрелки можно не рисовать, в остальных стрелка обязательна. У схемы один вход и один выход. У ромба ровно два выхода, оба подписаны. Текст в символе — короткая формула или условие, а не абзац. Схема алгоритма Евклида:

               ( Начало )
                   │
              / Ввод a, b /
                   │
     ┌──────►< b ≠ 0 ? >─── нет ───┐
     │             │ да            │
     │      [ r = a mod b ]        │
     │             │               │
     │         [ a = b ]           │
     │             │               │
     │         [ b = r ]           │
     └─────────────┘               │
                            / Вывод a /
                                   │
                              ( Конец )
Частые ошибки в блок-схемах: у ромба один выход или три; ввод-вывод в прямоугольнике вместо параллелограмма; ветка ошибки обрывается или ведёт ко второму «Концу»; стрелка цикла возвращается не к проверке условия, а в середину тела; в символе написан код на C вместо действия.
05

Три базовые структуры и псевдокод

Любой алгоритм можно составить из трёх базовых структур, вкладывая их друг в друга (теорема Бёма — Якопини, 1966). Схемы и программы, построенные только из них, называют структурными: у каждой структуры один вход и один выход, и её можно читать как один большой шаг.

  Следование           Ветвление                     Цикл с предусловием     Цикл с постусловием

  [ действие 1 ]       < условие? >── нет ──┐        ┌──►< условие? >─ нет ─┐   ┌──►[ тело ]
        │                   │ да            │        │        │ да           │   │       │
  [ действие 2 ]       [ действие 1 ] [ действие 2 ] │     [ тело ]          │   └─ да ─< условие? >
        │                   │               │        └────────┘              │           │ нет
                            ├◄──────────────┘                     ┌◄─────────┘           │
                            │                                     │

Цикл с предусловием проверяет условие до тела — тело может не выполниться ни разу. Цикл с постусловием проверяет после — тело выполняется хотя бы раз. В C это циклы while и do while (раздел 6).

Псевдокод — запись алгоритма ключевыми словами на естественном языке. Строгого стандарта у него нет; в курсе используется такая форма:

ВВОД a, b
ЕСЛИ a ≤ 0 ИЛИ b ≤ 0 ТО
    ВЫВОД «нужны натуральные числа»
ИНАЧЕ
    ПОКА b ≠ 0 ВЫПОЛНЯТЬ
        r ← a mod b
        a ← b
        b ← r
    КОНЕЦ ПОКА
    ВЫВОД a
КОНЕЦ ЕСЛИ

Стрелка ← — присваивание: вычислить правую часть и записать результат в переменную слева. a ← b — не равенство, а действие: старое значение a пропадает. Поэтому порядок строк в теле цикла важен: если поменять местами a ← b и r ← a mod b, остаток будет считаться уже от нового a.

ПсевдокодБлок-схемаC (раздел)
x ← выражениепроцессx = выражение; (4)
ВВОД, ВЫВОДданныеscanf, printf (4)
ЕСЛИ … ТО … ИНАЧЕ … КОНЕЦ ЕСЛИрешениеif … else (5)
ПОКА … ВЫПОЛНЯТЬ … КОНЕЦ ПОКАрешение + стрелка назадwhile (6)
ПОВТОРЯТЬ … ПОКА …тело + решениеdo … while (6)
ДЛЯ i ОТ 1 ДО n … КОНЕЦ ДЛЯподготовкаfor (6)
06

Трассировка

Трассировка — выполнение алгоритма вручную с записью значений переменных после каждого шага. Она показывает, что алгоритм делает на самом деле, а не что задумано. Алгоритм Евклида для a = 48, b = 18:

Шагb ≠ 0 ?rab
ввод—4818
1да121812
2да6126
3да060
4нетвыход из цикла, вывод a = 6

НОД(48, 18) = 6 — верно: 48 = 6 · 8, 18 = 6 · 3, и у 8 и 3 общих делителей нет. А если ввести a = 18, b = 48 — меньшее первым? Первый шаг даст r = 18 mod 48 = 18, a = 48, b = 18: числа просто поменялись местами, дальше — та же таблица. Алгоритм работает и так, это видно из трассировки, а не из догадки.

По трассировке составляются тестовые примеры — пары «входные данные → ожидаемый результат». Хороший набор тестов проверяет:

07

Сколько действий выполнит алгоритм

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

Сумма 1 + 2 + … + n. Алгоритм с циклом:

S ← 0
ДЛЯ i ОТ 1 ДО n
    S ← S + i
КОНЕЦ ДЛЯ

Сложений — n. Для n = 1 000 000 это миллион сложений. А по формуле суммы арифметической прогрессии S = n · (n + 1) / 2 — одно умножение, одно сложение и одно деление при любом n. Чем больше n, тем сильнее разница.

Поиск в списке. Есть ли фамилия в списке из n фамилий? Проверять подряд с первой: в лучшем случае — 1 сравнение (фамилия первая), в худшем — n (последняя или её нет). Если список упорядочен по алфавиту, можно открыть середину, сравнить и отбросить половину, в которой фамилии быть не может; затем — середину оставшейся половины, и так далее. Каждое сравнение уменьшает список вдвое:

nПодряд, худший случайДелением пополам, худший случай
10104
1 0001 00010
1 000 0001 000 00020
1 000 000 0001 000 000 00030

Число делений пополам до одного элемента — это log2 n с округлением вверх: 210 = 1024 ≥ 1000, 220 ≈ миллион. Цена второго способа — список должен быть упорядочен заранее; сколько стоит упорядочить, разбирается в разделе 8 вместе с записью таких оценок в виде O(n), O(log n).

Оценивайте число действий для наибольших данных, которые допускает задача. 10 сравнений вместо 4 не заметит никто. Миллиард сравнений вместо 30 — это секунды вместо микросекунд на каждый запрос к сайту.

Ключевые выводы раздела

Запомните главное

  • Этапы решения задачи: постановка, модель, алгоритм, программа, тестирование; тесты придумываются до программы
  • Жизненный цикл продолжается после выпуска программы; самый долгий этап — сопровождение
  • Программный продукт — программа плюс установка, документация, обработка ошибок и поддержка
  • Свойства алгоритма: дискретность, определённость, конечность, результативность, массовость; конечность каждого цикла доказывается
  • Блок-схема по ГОСТ 19.701-90: ромб — два подписанных выхода, ввод-вывод — параллелограмм, один вход и один выход у схемы
  • Любой алгоритм составляется из следования, ветвления и цикла
  • Трассировка показывает, что алгоритм делает на самом деле; по ней составляются тесты
  • Алгоритмы сравнивают по числу действий на наибольших данных: n сравнений против log2 n
Связи раздела. Опирается на раздел 1 — программа выполняется компьютером буквально, шаг за шагом. Нужен для: разделов 4–6 — следование, ветвление и циклы становятся инструкциями C, трассировка — способом проверки; раздела 7 — предопределённый процесс становится функцией; раздела 8 — поиск делением пополам и запись O(n), O(log n).
Тест по разделу 2 — на учебном портале. Курс на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Раздел 1: Языки и инструменты Все разделы Раздел 3: Типы данных и память