Построение цикла с помощью инварианта
Правильное использование конструкции цикла всегда представляет некоторую трудность. Применение элементарной теории помогает избежать ошибок и облегчает написание сложных программ.
Основная идея состоит в следующем. В процессе выполнения цикла изменяются значения набора переменных. Надо найти соотношение между меняющимися переменными, которое остается постоянным. Это соотношение называется инвариантом цикла. Сознательное построение цикла "пока" всегда связано с явной формулировкой и использованием инварианта цикла.
Явная формулировка инварианта помогает выписать инициализацию переменных, выполняемую до начала цикла, и тело цикла. Инициализация должна обеспечить выполнение инварианта до начала работы цикла. Тело цикла должно быть сконструировано таким образом, чтобы обеспечить сохранение инварианта. (Более точно, из того, что инвариант выполняется до начала исполнения тела цикла, должно следовать выполнение инварианта после окончания тела цикла. В процессе исполнения тела цикла инвариант может нарушаться.)
Завершение цикла, как правило, связано с ограниченной величиной, которая монотонно возрастает или монотонно убывает при каждом выполнении тела цикла. Цикл "пока" завершается, когда условие после слова "пока" в заголовке цикла становится ложным. Следовательно, это условие должно прямо или косвенно зависеть от величины, монотонно убывающей или возрастающей в процессе выполнения цикла. По достижению ее определенного значения условие должно становиться ложным. Условием завершения цикла называют отрицание условия, стоящего после слова "пока" в заголовке цикла.
Выполнение инварианта цикла и одновременно условия завершения должно обеспечивать решение требуемой задачи.
Общая схема
Обозначим через X множество всевозможных наборов значений всех переменных, меняющихся в ходе выполнения цикла. Множество X иногда называют фазовым, или конфигурационным, пространством задачи. Инвариант - это некоторое условие I(x), зависящее от точки x из множества X и принимающее значение "истина" или "ложь". (Математики называют такие условия предикатами.) В процессе инициализации точке x присваивается такое значение x0, что условие I(x0) истинно.
Обозначим условие завершения цикла через Q(x). Условия I(x) и Q(x) должны быть подобраны таким образом, чтобы одновременная истинность I(x) и Q(x) обеспечивала решение требуемой задачи: нахождение точки x с требуемыми свойствами.
Тело цикла можно трактовать как отображение точки x в новую точку T(x) из того же множества X:
T:X -> X
Условие I(x) является инвариантом для отображения T: если I(x) истинно, то I(T(x)) также истинно.
Общая схема построения цикла с помощью инварианта выглядит следующим образом:
x := x0; // x0 выбирается так, чтобы условие // I(x0) было истинным утверждение: I(x); цикл пока не Q(x) | инвариант: I(x); | x := T(x); // точка x преобразуется в T(x) конец цикла утверждение: Q(x) и I(x); ответ := x;
Конечно, эта схема не имеет никакой ценности без умения применять ее на практике. Рассмотрим несколько важных примеров ее использования.