Реализации алгоритмов/Вечный календарь
Описание
[править]Существует довольно простой алгоритм вычисления дня недели для любой даты григорианского календаря, начиная с первого дня его действия — 15 октября 1582 года. (Предыдущим днём было 4 октября, числа с 5 по 14 октября включительно были пропущены для устранения 11‑дневного отставания от фактической даты, накопившегося за время использования юлианского календаря).
Положим, дата задана так: год — год (от 1582), месяц — номер месяца (1…12), число — число месяца (1…31, согласно числу дней в соответствующем месяце), тогда:
a = (14 − месяц) / 12 год = год − a месяц = месяц + 12 * a − 2 ДеньНедели = (число + (31 * месяц) / 12 + год + год / 4 − год / 100 + год / 400) ОСТАТОК 7
либо, что почти то же самое:
если месяц = 1 или месяц = 2: // январь или февраль год = год − 1 месяц = месяц + 10 иначе: месяц = месяц − 2 всё ДеньНедели = (число + (31 * месяц) / 12 + год + год / 4 − год / 100 + год / 400) ОСТАТОК 7
Деление производится нацело (с отбрасыванием остатка).
Результат: 0 — воскресенье, 1 — понедельник и т. д.
Реализации
[править]//==========================================================================================
//Глобальные переменные
//==========================================================================================
byte day = 7; //число
byte month = 7; //месяц
unsigned int year = 2017; //год
//===============================================================================================
// Функция деления без остатка f_div
// Я извиняюсь, но кому в здравом уме могла прийти идея написать отдельную функцию
// целочисленного деления в C или C++?
// Всем нормальным людям известно, что простое деление двух целых с помощью '/'
// в C или в C++ - есть целочисленное деление!!!!!!! Т.е. деление без остатка, если кто не понял.
//===============================================================================================
unsigned int f_div(unsigned int x, unsigned int y){
unsigned int result;
result = (x - (x % y)) / y;
return result;
}
//==========================================================================================
//Функция вычисления дня недели
//==========================================================================================
void weekday(){
// И вы с какой целью пытаетесь запихнуть результат функции типа unsigned int, который
// может принимать значения в диапазоне 0...65535 в переменную типа byte, которая по сути
// является unsigned char, т.е. 0...255. На выхлопе можно такое получить, что юлианский
// с григорианским календарем на пару кашлять будут разными битами во все стороны ))))
byte a = f_div((14 - month), 12);
// Ну и, конечно, надо понимать, что результат здесь для a всегда будет ноль (0), кроме
// января(1) и февраля(1)
unsigned int y = year - a;
byte m = month + 12 * a - 2;
unsigned int y4 = f_div(y, 4);
byte y100 = f_div(y, 100);
byte y400 = f_div(y, 400);
byte x = f_div(31 * m, 12);
byte wd = (day + y + y4 - y100 + y400 + x) % 7; //результат в переменной "wd"
}
//набирал с клавиатуры и убил вечер на тестирование - reodos
Классический
[править]10 PRINT "Year";: INPUT Y
20 PRINT "Month";: INPUT M
30 PRINT "Day";: INPUT D
40 IF M < 3 THEN LET Y = Y - 1: LET M = M + 12
50 LET M = M - 2
60 LET W = (D + INT(31 * M / 12) + Y + INT(Y / 4) - INT(Y / 100) + INT(Y / 400))
70 LET W = W - INT(W / 7) * 7
80 PRINT "Weekday: "; W
90 END
Примечание: При выполнении условия M < 3
в строке 40 значение M увеличивается на 12, а в строке 50 уменьшается на 2, т. е. в итоге увеличивается на 10. При невыполнении же условия значение M просто уменьшается на 2. Таким образом удаётся избежать использования оператора GOTO
.
10 INPUT "Year", Y%: INPUT "Month", M%: INPUT "Day", D%
20 IF M% < 3 THEN Y% = Y% - 1: M% = M% + 10: ELSE M% = M% - 2
30 PRINT "Weekday: "; (D% + 31 * M% \ 12 + Y% + Y% \ 4 - Y% \ 100 + Y% \ 400) MOD 7
QuickBasic версий < 4.0, Turbo Basic
[править]DEF FNWD%(Y%, M%, D%)
IF M% < 3 THEN
Y% = Y% - 1
M% = M% + 10
ELSE
M% = M% - 2
END IF
FNWD% = (D% + 31 * M% \ 12 + Y% + Y% \ 4 - Y% \ 100 + Y% \ 400) MOD 7
END DEF
PowerBASIC, QBASIC, QuickBasic версий 4.X, Visual Basic
[править]Function Weekday(year As Integer, month As Integer, day As Integer) As Integer
Dim y As Integer, m As Integer
If month < 3 Then
y = year - 1
m = month + 10
Else
y = year
m = month - 2
End If
Weekday = (day + 31 * m \ 12 + y + y \ 4 - y \ 100 + y \ 400) Mod 7
End Function
Примечание: Параметры в Sub
и Function
передаются по ссылке, и изменение значений year
и month
внутри функции могло бы иметь побочные эффекты в вызывающей программе. Для предотвращения этого использованы локальные переменные y
и m
.
Function Weekday(ByVal year As UShort, ByVal month As Byte, ByVal day As Byte) As Byte
If month < 3 Then
year -= 1
month += 10
Else
month -= 2
End If
Return (day + 31 * month \ 12 + year + year \ 4 - year \ 100 + year \ 400) Mod 7
End Function
typedef unsigned short Year;
typedef unsigned char Month;
typedef unsigned char Day;
typedef unsigned char Weekday;
Weekday weekday(Year year, Month month, Day day) {
if (month < 3u) {
--year;
month += 10u;
} else
month -= 2u;
return (Weekday)((day + 31u * month / 12u + year + year / 4u - year / 100u + year / 400u) % 7u);
}
static byte Weekday(ushort year, byte month, byte day) {
if (month < 3u) {
--year;
month += 10u;
} else
month -= 2u;
return (byte)(((ushort)day + 31u * (ushort)month / 12u + year + year / 4u - year / 100u + year / 400u) % 7u);
}
type TYear = Word;
type TMonth = 1..12;
type TDay = 1..31;
type TWeekday = 0..6;
function Weekday(year: TYear; month: TMonth; day: TDay): TWeekday;
begin
if month < 3 then
begin
year := year - 1;
month := month + 10
end
else
month := month - 2;
Weekday := (day + 31 * month div 12 + year + year div 4 - year div 100 + year div 400) mod 7
end;
type Year = uint16
type Month = uint8
type Day = uint8
type Weekday = uint8
func weekday(year Year, month Month, day Day) Weekday {
if month < 3 {
year--
month += 10
} else {
month -= 2
}
return Weekday((Year(day) + 31 * Year(month) / 12 + year + year / 4 - year / 100 + year / 400) % 7)
}
static byte weekday(short year, byte month, byte day) {
if (month < 3) {
--year;
month += 10;
} else
month -= 2;
return (byte)(((short)day + 31 * (short)month / 12 + year + year / 4 - year / 100 + year / 400) % 7);
}
function weekday(year, month, day) {
year = parseInt(year, 10);
month = parseInt(month, 10);
day = parseInt(day, 10);
if (month < 3) {
--year;
month += 10;
} else
month -= 2;
return (day + 31 * month / 12 + year + year / 4 - year / 100 + year / 400) % 7;
}
А2 — ячейка, содержащая дату.
(English): =WEEKDAY(A2;2)
(Русский): =ДЕНЬНЕД(A2;2)
ЛИБО:
=ОКРВНИЗ(ОСТАТ((ДЕНЬ(A2)+(ГОД(A2)-ОКРВНИЗ((14-МЕСЯЦ(A2))/12;1))+ОКРВНИЗ((ГОД(A2)-ОКРВНИЗ((14-МЕСЯЦ(A2))/12;1))/4;1)-ОКРВНИЗ((ГОД(A2)-ОКРВНИЗ((14-МЕСЯЦ(A2))/12;1))/100;1)+ОКРВНИЗ((ГОД(A2)-ОКРВНИЗ((14-МЕСЯЦ(A2))/12;1))/400;1)+ОКРВНИЗ(31*(МЕСЯЦ(A2)+12*ОКРВНИЗ((14-МЕСЯЦ(A2))/12;1)-2)/12;1));7);1)
CREATE FUNCTION [dbo].[getDay](
@date datetime
)
RETURNS int
AS
BEGIN
declare @a int
declare @y int
declare @m int
set @a = (14 — MONTH(@date)) / 12
set @y = YEAR(@date) — @a
set @m = MONTH(@date) + 12*@a-2
return (DAY(@date) + (31 * @m) / 12 + @y + @y / 4 — @y / 100 + @y / 400) % 7
END
function weekday(int $year, int $month, int $day) {
if ($month < 3) {
--$year;
$month += 10;
} else
$month -= 2;
return ($day + 31 * $month / 12 + $year + $year / 4 - $year / 100 + $year / 400) % 7;
}
def weekday(year: int, month: int, day: int) -> int:
if month < 3:
year -= 1
month += 10
else:
month -= 2
return (day + 31 * month // 12 + year + year // 4 - year // 100 + year // 400) % 7
def weekday(year, month, day)
if month < 3
year -= 1
month += 10
else
month -= 2
end
return (day + 31 * month / 12 + year + year / 4 - year / 100 + year / 400) % 7
end
type Year = u16;
type Month = u8;
type Day = u8;
type Weekday = u8;
fn weekday (year: Year, month: Month, day: Day) -> Weekday {
// Чтобы не делать параметры функции изменяемыми (mut),
// вычисления производятся во вложенной функции:
fn _weekday (y: Year, m: Month, d: Day) -> Weekday {
((d as Year + 31 * m as Year / 12 + y + y / 4 - y / 100 + y / 400) % 7) as Weekday
}
if month < 3 {
_weekday(year - 1, month + 10, day)
} else {
_weekday(year, month - 2, day)
}
}
func weekday (year: Int, month: Int, day: Int) -> Int {
var year = year
var month = month
if month < 3 {
year -= 1
month += 10
} else {
month -= 2
}
return (day + 31 * month / 12 + year + year / 4 - year / 100 + year / 400) % 7
}
@echo off
set /a год = %date:~6,4%
set /a месяц = %date:~3,2%
set /a число = %date:~0,2%
set /a a = ((14 - %месяц%) / 12)
set /a y = (%год% - %a%)
set /a m = (%месяц% + (12 * %a%) - 2)
set /a ДеньНедели = (((%число% + %y% + (%y% / 4) - (%y% / 100) + (%y% / 400) + ((31 * %m%) / 12))) %% 7)
echo %ДеньНедели%
ЗАДАЧА ДеньНедели (год, месяц, день: УЗКЦЕЛ): УЗКЦЕЛ;
УКАЗ
ВЫБРАТЬ месяц ИЗ
1, 2: (* январь или февраль *)
год := год - 1;
месяц := месяц + 10
ИНАЧЕ
месяц := месяц - 2
КОН;
ВОЗВРАТ (день + 31 * месяц ДЕЛИТЬ 12 + год + год ДЕЛИТЬ 4 - год ДЕЛИТЬ 100 + год ДЕЛИТЬ 400) ОСТАТОК 7
КОН ДеньНедели;
Программируемые микрокалькуляторы «Электроника»
[править]МК-61 / 52 / 161 / 163 / 152 / 1152
[править]В вычислениях участвуют только регистры стека.
00. ↔ 01. 3 02. − 03. 1 04. ↔ 05. Fx<0 06. 15 07. F🔃 08. ↔ 09. F🔃
10. − 11. 1 12. 3 13. ↔ 14. F🔃 15. + 16. 3 17. 1 18. × 19. 1
20. 2 21. ÷ 22. К[x] 23. + 24. + 25. ↔ 26. 4 27. ÷ 28. К[x] 29. +
30. ↔ 31. 2 32. F10ˣ 33. ÷ 34. ↔ 35. FВx 36. К[x] 37. − 38. ↔ 39. 4
40. ÷ 41. К[x] 42. + 43. В↑ 44. В↑ 45. 7 46. ÷ 47. К[x] 48. 7 49. ×
50. − 51. С/П
Использование: <год> → регистр Z, <месяц> → регистр Y, <число> → регистр X, В/О, С/П (номер дня недели на индикаторе).