Как заставить счетчик бегать по кругу, не используя условных конструкций

На практике часто встречаются задачи, в которых требуется заставить какую-то переменную бегать по кругу. То есть это означает, что некий счетчик i, к которому на каждом шаге цикла прибавляется 1, должен увеличиваться на единицу, пока не достигнет предельного значения n (или n-1, если счет ведется от 0). Затем после прибавления очередной единицы он должен вновь становиться равным своему исходному значению (единице или нулю). Пусть, например, i - переменная-счетчик месяцев в какой-то программе "Календарь". Она должна пробегать от 1 до 12, а затем после прибавления очередной единицы вновь принимать исходное значение - один. Конечно, можно написать несколько строк программного кода с использованием оператора if. Но можно поступить иначе. А именно, применить лаконичную арифметическую формулу

i=i+1-(i div n)*n (1)

Последний элемент этой формулы (i div n)*n "оживает" лишь тогда, когда i становится равным n. Во всех прочих случаях он "спит" в конце формулы как безобидный ноль. Пока i<n, формула (1) фактически эквивалентна выражению i="i+1." При i="n" формула превращается в выражение i="i+1-n=1." В тех случаях, когда счет ведется не от единицы, а от нуля, просто нужно разбить формулу (1) на две

i=i+1

i=i-(i div n)*n.

То есть, сначала отдельно прибавлять к i единицу, а затем отнимать или не отнимать n. Такие формулы можно использовать, например, при написании некой программы "таймер", где счетчик минут пробегает 60 значений от 0 до 59, а счетчик часов пробегает 24 значения от 0 до 23.

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

Program NextDatе;
Type
 date=1..31;
 mon=1..12;
 year=1990..2000;
Const
 nday:array [mon] of date=(31,29,31,30,31,30, 31,31,30,31,30,31);
Var
 d: date;
 m: mon;
 n,y: integer;
begin
 ReadLn (d,m,y);
 y:=y+(m div 12)*(d div 31);
 nday[2]:=Trunc(nday[2]-(y mod 4)/3);
 n:=nday[m];
 m:=m-(d div n)*(m div 12)*12+(d div n);
 d:=d-(d div n)*n+1;
 WriteLn (d:3,m:3,y:5);
end.

В данной программе исходная дата вводится в виде трех чисел. Первое обозначает число, второе - месяц, третье - год. Следующая за оператором ввода формула определяет год завтрашней даты. К счетчику лет y прибавляется 1, если месяц (m) введенной даты равен 12, а число месяца исходной даты равно 31. Напомним, что функция Trunc в Паскале возвращает целую часть числового выражения в скобках. Количество дней в различных месяцах года меняется не строго периодически. Поэтому их длительности занесены в отдельный массив. Вторым элементом этого массива является длительность февраля. Предварительно этот второй элемент равен 29. В теле программы длительность февраля пересчитывается как

Целое(Количество_дней_в_феврале - (Год mod 4)/3).

Таким образом, если остаток от деления года на 4 не равен нулю, то Количество_дней_в_феврале в итоге уменьшится на 1. Если же год високосный, т.е. переменная "Год" делится на 4 без остатка, то количество дней в феврале останется равным 29. Сходные арифметические выражения используются и для пересчета значений месяца и числа. К достоинствам программы можно отнести ее способность давать правильный ответ даже тогда, когда исходная дата введена неправильно. Например, введя изобретенное Мюнхгаузеном 32 мая, к примеру 32.05.99, получим правильный ответ 2.06.99.

Андрей КОЛЕСНИКОВ

Версия для печатиВерсия для печати

Номер: 

17 за 1999 год

Рубрика: 

Азбука программирования
Заметили ошибку? Выделите ее мышкой и нажмите Ctrl+Enter!