Сайт Льва Волкова
  
Преувеличивая чужие добродетели, мы отдаем дань не столько им, сколько нашим собственным чувствам мы ищем похвал себе, делая вид, что хвалим других. Ф.Ларошфуко
 
      На главную  
 Личное
  Статьи
  Задачи 
 Ссылки
 АТ-531
www.levvol.ru    
 

Несколько календарных задач

Високосный год

Продолжительность тропического года (время между двумя весенними равноденствиями) составляет 365 суток 5 часов 48 минут 46 секунд. Различие в продолжительности тропического года и среднего юлианского календарного года (365,25 суток) составляет 11 минут 14 секунд. Из этих 11 минут и 14 секунд приблизительно за 128 лет складываются одни сутки.

По истечении столетий было замечено смещение дня весеннего равноденствия, с которым связаны церковные праздники. К XVI веку весеннее равноденствие наступало примерно на 10 суток раньше 21 марта, используемого для определения дня Пасхи.

Чтобы компенсировать накопившуюся ошибку и избежать подобного смещения в будущем, в 1582 году римский папа Григорий XIII провёл реформу календаря. Чтобы средний календарный год лучше соответствовал солнечному, было решено изменить правило високосных лет. По-прежнему високосным оставался год, номер которого кратен четырём, но исключение делалось для тех, которые были кратны 100. Отныне такие годы были високосными только тогда, когда делились ещё и на 400.

Иными словами, год является високосным, если он кратен 4 и при этом не кратен 100, либо кратен 400. Год не является високосным, если он не кратен 4, либо кратен 4, но при этом кратен 100 и не кратен 40 [1].

Отсюда функция опредляющая является ли год високосным или обычным:



Function LeapYear(y:integer):boolean;
Begin
if y mod 400 =0 then
if y mod 100=0 then LeapYear:=true
else LeapYear:=false
else
if y mod 4 =0 then LeapYear:=true
else LeapYear:=false
End;


Количество дней от 1 января 1 года

Хотя григорианский календарь был введен 1582 г., а в некторых странах гораздо позднее, например в СССР в 1918 г., условно будем считать, что он действует с 1 января 1 года. Тогда число дней прошедших с этого дня до данной даты (D.M.Y) определяется так: меняем параметр i от 1 до Y-1, если i соответсвует обычному году, то в переменную s прибавляем 365, если - високосному, то в s прибавляем 366. Далее меняем параметр i от 1 до M-1, в s прибавляем число дней в месяцах (с учетом является ли Y високосным или обычным годом). Наконец в s прибавляем число D.


Program TaskCalendar;
Uses CRT;
Type date=record
day,month,year:integer;
end;
Const
dmm:array [1..12] of integer
=(31,28,31,30,31,30,31,31,30,31,30,31);
Function NumberOfDays(yy:date):longint;
Var y,i:integer;
s:longint;
Begin
s:=0;
for y:=1 to yy.year-1 do
if (LeapYear(y)) then s:=s+366
else s:=s+365;
for i:=1 to yy.month-1 do
if (LeapYear(yy.year)) and (i=2) then s:=s+29
else s:=s+dmm[i];
s:=s+yy.day; NumberOfDays:=s
End;
Олимпиадная задача

В сборнике олимпиадных задач, опубликованных А.С. Пономаренко в приложении к журналу «Информатика в школе», представлена следующая задача [2]:

Составить программу, которая определит все годы до 100 - летнего юбелея, когда вы будете праздновать день своего рождения в тот же день недели, когда родились.

Если бы не было вискосных лет, то календарь повторялся бы каждые 7 лет, так как остаток от деления 365 на 7 равен 1. Если в этом году некая дата приходится на среду, то в следующем году она придется на четверг, на среду огна придется через 7 лет. Наличие високосных лет приводит к тому, что календарь полностью повторяется через каждые 7*4=28 лет.

Используем тот факт, что 01.01.01 было воскресеньем. Для кадого дня 100 - летнего периода будем определять число дней прошедших с 01.01.01, далее будем определять остаток от деления прошедших числа дней на 7, если этот остаток 1- то данный день - воскресенье, если 2, то понедельник и т.д., если остаток 0 - то данный день - суббота. Применим введенные нами функции.


Program TaskCalendar1;
Uses CRT;
Type date=record
day,month,year:integer;
end;
dw=array[0..6] of string[3];
Var d,d1:date;
i,k,j:integer;
Const
dww:array [0..6] of string[3]=
('Sat','Sun','Mon','Tue','Wen','Thu','Fri');
dmm:array [1..12] of integer=
(31,28,31,30,31,30,31,31,30,31,30,31);


	  
 Function LeapYear(y:integer):boolean;
  Begin
  if y mod 400 =0 then
      if y mod 100=0 then LeapYear:=true
                     else LeapYear:=false
                  else
      if y mod 4 =0  then LeapYear:=true
                     else LeapYear:=false
  End;
  


	
  Function NumberOfDays(yy:date):longint;
  Var  y,i:integer;
       s:longint;
     Begin
  s:=0;
  for y:=1 to yy.year-1 do
    if (LeapYear(y)) then s:=s+366
                     else s:=s+365;
  for i:=1 to yy.month-1 do
    if (LeapYear(yy.year)) and (i=2) then s:=s+29
                                     else s:=s+dmm[i];
      s:=s+yy.day;   NumberOfDays:=s
  End;


Begin
ClrScr;
write('day ='); readln(d.day);
write('month='); readln(d.month);
write('year ='); readln(d.year);
k:=NumberOfDays(d) mod 7;
yy:=d.year; d1:=d;
for i:=d.year to d.year+100 do begin
d1.year:=i;
j:=NumberOfDays(d1) mod 7;
if k=j then writeln(i:6,' ',dww[j]);
end;
readln;
End.

Сохранить эту программу

Определение даты по числу дней от 1 января 1 года

Задачу можно представить как введение функции обратной NumberOfDays. Однако, мы введем процедуру, которая по количесву дней с 1 января 1 г. вычисляет дату.

Сначала из числа дней y будем вычитать 365/366 дней и считать число лет d.year пока не получим отрицательное число, прибавим к нему последнее вычитаемое и число лет уменьшим на 1. Если оставшееся число дней y будет меньше 31, то сразу выводим month=1 и y. Если это не так, то начинаем счет месяцев, вычитая из y число дней в месяце. Если в процессе счета полцчим month=13, то присвоим month значение 1. Счет месяцев будем вести до тех пор пока y остается больше 0. Скорректируем y на число дней в последнем месяце, а число месяцев уменьшим на 1. Если при этом month примет значение 0, то назначим ему значение 12.


Procedure DateN(y:longint; var d:date);
Var
dy:integer;
Begin
with d do begin
year:=1;
{счет числа лет}
repeat
if (LeapYear(year)) then dy:=366
else dy:=365;
y:=y-dy;
year:=year+1
until (y<=0);
{корректировка числа лет}
y:=y+dy; year:=year-1;
{счет числа месяцев}
month:=1;
if y>31 then begin
repeat
if (LeapYear(year)) and (month=2) then dy:=29
else dy:=dmm[month];
y:=y-dy; month:=month+1;
if month>12 then month:=1;
until (y<=0);
{корректировка числа месяцев}
y:=y+dy; month:=month-1;
if month=0 then month:=12;
end;
d.day:=y
end;
End;

Пятницы 13

Для данного года определим пятницы 13. Применим введенные нами функции.

Сохранить эту программу

Пасхалии

По заданному номеру года определить дату Пасхи по григорианскому календарю.

Пасхалия, сборник правил, на основании которых определяется день празднования пасхи. Поскольку эти правила требуют выполнения определённых условий, связанных с движением Земли вокруг Солнца (пасха должна быть вскоре после весеннего равноденствия), обращением Луны вокруг Земли (связь пасхи с полнолунием), с семидневной неделей (воскресенье), даты празднования пасхи непостоянны и перемещаются от года к году. Вследствие неидентичности правил П. разных религий, дни празднования пасхи у них не совпадают. Математическую теорию П. разработал К. Гаусс [3,4].


Program TaskCalendar2;
Uses CRT;
Type date=record
day,month,year:integer;
end;
dw=array[0..6] of string[3];

Var d:date;
yb,ye,i:longint;
k:integer;
........................
Begin
ClrScr;
with d do begin
write('year ='); readln(year);
day:=1; month:=1;
yb:=NumberOfDays(d);
if (LeapYear(year)) then ye:=yb+366
else ye:=yb+365;
for i:=yb to ye do begin
DateN(i,d);
{день недели }
k:=i mod 7; {если этот остаток =6, то это Fri}
if (k=6) and (day=13) then writeln(13,' ',month,' Fri');
end;
end;
readln;
End.

Сохранить эту программу

Назад

Источники:

1. Википедия. Високосный год
2. Информатика в школе: Приложение к журналу «Информатика и образование. №1 - 2006. -M.: Образование и информатика, 2006. - 112 с.
3. БСЭ. Пасхалии.
4. Малаховскй В.С. Введение в математику. - Калининград: Янтарный сказ, 2006. - 439 с.

Назад