На практике часто встречаются задачи, в которых требуется заставить какую-то переменную бегать по кругу. То есть это означает, что некий счетчик 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.
Андрей КОЛЕСНИКОВ
Горячие темы