"Информатика (базовый уровень)", Гейн А.Г., Гейн А.А.
Инвариант цикла — свойство переменных, которое сохраняется при каждом повторении тела цикла. Он помогает доказать правильность алгоритма: сначала проверяют свойство до первой итерации, затем его сохранение, а при остановке объединяют с условием выхода. Однако одного инварианта недостаточно: отдельно нужно показать, что цикл действительно завершится, например из-за изменения счётчика.
Текст подготовлен нейронной сетью по теме параграфа и не заменяет учебник — важные факты и формулировки сверяйте с ним.
Цикл — фрагмент алгоритма, в котором одна и та же последовательность команд многократно выполняется по условию. Он нужен, чтобы не записывать одинаковые действия много раз, например сложение элементов массива.
Итерация — одно выполнение тела цикла. Подсчёт итераций помогает понять, сколько раз обработаны данные и в какой момент меняются управляющие переменные.
Инвариант цикла — утверждение о значениях переменных, истинное перед началом цикла и сохраняющее истинность после каждой итерации. С его помощью доказывают, что алгоритм не теряет нужное свойство во время промежуточных вычислений.
Условие завершения — условие, при наступлении которого цикл прекращает выполнение. Его рассматривают вместе с инвариантом, чтобы перейти от промежуточного свойства к окончательному ответу.
Утверждение-инвариант выбирают так, чтобы оно описывало состояние уже обработанной части данных перед каждой итерацией. Например, в цикле суммирования оно может связывать счётчик с суммой всех чисел, которые уже пройдены. Такое утверждение не должно сразу повторять конечный ответ, иначе его нельзя будет использовать для описания промежуточных шагов.
Истинность проверяют в трёх ключевых точках: до первой итерации, после выполнения тела и при выходе. Начальная проверка показывает, что алгоритм стартует с нужным свойством, а проверка после шага — что свойство не исчезает во время работы. При завершении к инварианту добавляется факт, что условие продолжения уже ложно, и это сужает возможные значения переменных.
Сохранение доказывают условным рассуждением: предполагают, что инвариант верен перед очередной итерацией, затем прослеживают изменение переменных в теле цикла. Нужно показать, что новые значения снова удовлетворяют тому же утверждению. Поскольку рассуждение относится к любой итерации, оно доказывает сохранение свойства на всём протяжении цикла, а не только на одном примере.
После выхода из цикла используют одновременно инвариант и условие завершения. Например, инвариант может говорить, что обработаны первые k элементов, а условие выхода — что k равно размеру массива; значит, обработаны все элементы. Но это ещё не гарантирует выхода: нужно отдельно убедиться, что счётчик или другая управляющая величина меняется в правильном направлении и цикл не может выполняться бесконечно.
Таблицу удобно читать сверху вниз: сначала проверяют начало, затем один типичный шаг, после этого — смысл выхода и возможность остановки. Если пропущен хотя бы один этап, доказательство может быть неполным.
| Этап | Что проверить | Что это доказывает |
|---|---|---|
| Инициализация | Инвариант истинен до первой итерации | Алгоритм начинает работу с нужным свойством |
| Сохранение | Из истинности инварианта до итерации следует его истинность после итерации | Свойство сохраняется на всём протяжении цикла |
| Завершение | При выходе из цикла инвариант объединяется с условием завершения | Получается требуемый результат алгоритма |
| Остановка | Управляющая величина изменяется так, что цикл не может выполняться бесконечно | Алгоритм завершает работу |
Условие. Цикл должен вычислить сумму 1 + 2 + ... + n. Пусть k — число уже обработанных слагаемых, а s — накопленная сумма.
Ответ. Алгоритм корректен: после остановки переменная s содержит сумму первых n натуральных чисел.
Условие. Требуется найти наибольший элемент массива, просматривая его слева направо. Переменная max хранит текущий максимум, а i указывает границу обработанного фрагмента.
Ответ. Значение max после выхода из цикла является максимальным элементом массива.
Условие. Для натуральных чисел a и b алгоритм заменяет пару (a, b) на (b, a по модулю b), пока b не станет равным нулю. Нужно найти НОД исходных чисел.
Ответ. После завершения алгоритма первое число содержит НОД исходной пары.
Инвариант формулируют как конечный результат алгоритма, хотя он должен описывать промежуточное состояние на каждой итерации. Так происходит, когда ученик не учитывает уже обработанную часть данных. Проверка: можно ли применить утверждение до завершения цикла и связать его с текущим счётчиком?
Проверяют только начало цикла, но не доказывают сохранение после тела. Начальная истинность показывает лишь правильный старт и ничего не говорит о последующих изменениях. Нужно проследить один общий шаг, предполагая инвариант истинным до него.
Считают, что наличие инварианта автоматически доказывает завершение. Свойство может сохраняться в бесконечном цикле, если счётчик не меняется или меняется в неверную сторону. Проверьте границу, направление изменения и достижимость условия выхода.
Не используют условие завершения вместе с инвариантом и не получают точный ответ. Сам инвариант обычно говорит только о части данных или промежуточном значении. После выхода надо явно подставить факт завершения и показать, почему обработана вся нужная область.
До изучения инварианта нужно понимать устройство циклов, условия их выполнения и изменение переменных. Полезна математическая индукция: проверка начала и шага похожа на проверку инициализации и сохранения. Инварианты применяются в сортировке, поиске, обработке массивов и численных вычислениях, поэтому тема связывает доказательства с практическим программированием.
Должен ли инвариант быть истинным после каждой команды тела цикла?
Не обязательно после каждой отдельной команды. Достаточно доказать, что если инвариант был верен перед итерацией, то после выполнения всего тела цикла он снова истинен.
Чем инвариант цикла отличается от условия завершения?
Инвариант описывает свойство переменных, сохраняющееся во время работы цикла. Условие завершения определяет момент выхода из цикла; вместе с инвариантом оно позволяет получить итоговый результат.
Как придумать подходящий инвариант для конкретной задачи?
Нужно описать, что уже обработано к началу очередной итерации, и связать это с текущими значениями переменных. Хороший инвариант легко проверить в начале, сохранить после шага и использовать после выхода.
Можно ли доказать правильность цикла, не доказывая его остановку?
Нет, полное доказательство правильности требует показать и корректность результата, и завершение работы. Инвариант может сохраняться в бесконечном цикле, который так и не выдаст ответ.
Почему одного примера работы программы недостаточно для доказательства правильности?
Один пример проверяет алгоритм только на одном наборе данных и не обнаруживает ошибки в других случаях. Доказательство через инвариант объясняет, почему свойство сохраняется для всех допустимых входных данных.
§ 50. Инвариант цикла