Реализации алгоритмов/Алгоритм пасхалии

Материал из Викиучебника — открытых книг для открытого мира

Пасха (англ. Easter, кит. 复活节) — христианский праздник воскресения Иисуса Христа, отмечающийся в первое воскресенье после первого полнолуния, наступающего не ранее весеннего равноденствия; таким образом он не привязан к конкретной дате («переходящий праздник»). Под полнолунием и равноденствием понимаются не фактические астрономические явления, а их расчётные даты: полнолуние вычисляется по метонову циклу, за равноденствие берётся 21 марта (календарное равноденствие для северного полушария).

Пасхалия (англ. = лат. Computus — исчисление, хин. ईस्टर) — методика расчёта даты празднования Пасхи в том или ином году. В настоящее время большинство православных церквей используют александрийскую пасхалию, опирающуюся на юлианский календарь («старый стиль»), тогда как католическая, а также некоторые православные и протестантские церкви придерживаются григорианской, основанной на общепринятом григорианском календаре. Из-за расхождения между этими календарями (13 дней на сегодня) разница между датами празднования «католической» и «православной» Пасхи в разные годы может составлять 0, 1, 4 или 5 недель.

Описание[править]

Существуют различные алгоритмы пасхалии. Здесь использован алгоритм григорианской пасхалии неизвестного автора (журнал New York correspondent, 1876) и его адаптация бельгийским математиком-астрономом Жаном Меёсом (франц. Jean Meeus) для александрийской пасхалии (Jean Meeus - Astronomical Algorithms, 1991).

Дано: год

Найти: месяц, день

Вычисляется фактор. ЦЕЛ(x) означает целую часть выражения, ОСТ(x / y) — остаток от деления.

Александрийская пасхалия:

d = ОСТ((ОСТ(год / 19) × 19 + 15) / 30)
factor = d + ОСТ(((ОСТ(год / 4) × 2) + (ОСТ(год / 7) × 4) + 34 − d) / 7) + 114

Григорианская пасхалия:

a = ОСТ(год / 19)
b = ЦЕЛ(год / 100)
c = ОСТ(год / 100)
h = ОСТ((19 × a + b − ЦЕЛ(b / 4) − ЦЕЛ((b − ЦЕЛ((b + 8) / 25) + 1) / 3) + 15) / 30)
l = ОСТ((32 + (ОСТ(b / 4) + ЦЕЛ(c / 4)) × 2 − h − ОСТ(c / 4)) / 7)
factor = h + l − ЦЕЛ((11 × (h + 2 × l) + a) / 451) × 7 + 114

Месяц и день Пасхи вычисляются из фактора:

месяц = ЦЕЛ(factor / 31)
день = ОСТ(factor / 31) + 1

Для григорианской пасхи найденные значения определяют григорианскую дату, а для александрийской — юлианскую.

Реализации[править]

BASIC[править]

PowerBASIC, QBASIC, QuickBasic версий 4.X, Visual Basic версий ≤ 6.0, Visual Basic для приложений[править]

' computus.bas

' ComputusGetEaster вычисляет по данному фактору месяц и день Пасхи (приватная функция).
Sub ComputusGetEaster(factor As Integer, month As Integer, day As Integer)
    month = factor \ 31
    day = factor Mod 31 + 1
End Sub

' ComputusGetAlexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
Sub ComputusGetAlexandrian(year As Integer, month As Integer, day As Integer)
    Dim d As Integer
    d = (year Mod 19 * 19 + 15) Mod 30
    ComputusGetEaster d + (year Mod 4 * 2 + year Mod 7 * 4 + 34 - d) Mod 7 + 114, month, day
End Sub

' ComputusGetGregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
Sub ComputusGetGregorian(year As Integer, month As Integer, day As Integer)
    Dim a As Integer, b As Integer, c As Integer, h As Integer, l As Integer
    a = year Mod 19
    b = year \ 100
    c = year Mod 100
    h = (19 * a + b - b \ 4 - (b - (b + 8) \ 25 + 1) \ 3 + 15) Mod 30
    l = (32 + (b Mod 4 + c \ 4) * 2 - h - c Mod 4) Mod 7
    ComputusGetEaster h + l - (11 * (h + l + l) + a) \ 451 * 7 + 114, month, day
End Sub

Пример использования:

' comptest.bas

' PrintDate выводит дату в формате:
' К: ГГГГ-ММ-ДД
' где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
Sub PrintDate(calendar As String, year As Integer, month As Integer, day As Integer)
    Print calendar; ": "; Str$(year); "-";
    If month < 10 Then Print "0";
    Print Str$(month); "-";
    If day < 10 Then Print "0";
    Print Str$(day)
End Sub

' Для Visual Basic добавить здесь Sub Main

Dim year As Integer

Input "Year", year

Dim month As Integer, day As Integer

ComputusGetAlexandrian year, month, day
PrintDate "Alexandrian", year, month, day

ComputusGetGregorian year, month, day
PrintDate "Gregorian", year, month, day

' Для Visual Basic добавить здесь End Sub

C[править]

/* computus.h */

#ifndef _COMPUTUS_H_
#define _COMPUTUS_H_


typedef unsigned short Year;
typedef unsigned char Month;
typedef unsigned char Day;

/*
computus_get_alexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
*/
void computus_get_alexandrian(Year year, Month *month, Day *day);

/*
computus_get_gregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
*/
void computus_get_gregorian(Year year, Month *month, Day *day);


#endif


/* computus.c */

#include "computus.h"


/*
_computus_get_easter вычисляет по данному фактору месяц и день Пасхи (приватная функция).
*/
void _computus_get_easter(Year factor, Month *month, Day *day) {
    *month = Month(factor / 31);
    *day = Day(factor % 31 + 1);
}

void computus_get_alexandrian(Year year, Month *month, Day *day) {
    Year d = (year % 19 * 19 + 15) % 30;
    _computus_get_easter(d + (year % 4 * 2 + year % 7 * 4 + 34 - d) % 7 + 114, month, day);
}

void computus_get_gregorian(Year year, Month *month, Day *day) {
    Year
        a = year % 19,
        b = year / 100,
        c = year % 100,
        h = (19 * a + b - b / 4 - (b - (b + 8) / 25 + 1) / 3 + 15) % 30,
        l = (32 + (b % 4 + c / 4) * 2 - h - c % 4) % 7;
    _computus_get_easter(h + l - (11 * (h + l + l) + a) / 451 * 7 + 114, month, day);
}

Пример использования:

/* computus_test.c */

#include <stdio.h>

#include "computus.h"


/*
print_date выводит дату в формате:
К: ГГГГ-ММ-ДД
где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
*/
void print_date(const char *calendar, Year year, Month month, Day day) {
    printf("%s: %hu-%.2hu-%.2hu\n", calendar, year, month, day);
}

int main() {
    Year year;
    printf("Year? ");
    scanf("%hu", &year);

    Month month;
    Day day;

    computus_get_alexandrian(year, &month, &day);
    print_date("Alexandrian", year, month, day);

    computus_get_gregorian(year, &month, &day);
    print_date("Gregorian", year, month, day);
}

C++[править]

// computus.hpp

#pragma once


namespace computus {

    typedef unsigned short Year;
    typedef unsigned char Month;
    typedef unsigned char Day;

    // getAlexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
    void getAlexandrian(Year year, Month &month, Day &day);

    // getGregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
    void getGregorian(Year year, Month &month, Day &day);

}


// computus.cpp

#include "computus.hpp"


namespace computus {

    // _getEaster вычисляет по данному фактору месяц и день Пасхи (приватная функция).
    void _getEaster(Year factor, Month &month, Day &day) {
        month = Month(factor / 31);
        day = Day(factor % 31 + 1);
    }

    void getAlexandrian(Year year, Month &month, Day &day) {
        Year d = (year % 19 * 19 + 15) % 30;
        _getEaster(d + (year % 4 * 2 + year % 7 * 4 + 34 - d) % 7 + 114, month, day);
    }
    
    void getGregorian(Year year, Month &month, Day &day) {
        Year
            a = year % 19,
            b = year / 100,
            c = year % 100,
            h = (19 * a + b - b / 4 - (b - (b + 8) / 25 + 1) / 3 + 15) % 30,
            l = (32 + (b % 4 + c / 4) * 2 - h - c % 4) % 7;
        _getEaster(h + l - (11 * (h + l + l) + a) / 451 * 7 + 114, month, day);
    }

}

Пример использования:

// computus_test.cpp

#include <iostream>
#include <iomanip>

#include "computus.hpp"


// printDate выводит дату в формате:
// К: ГГГГ-ММ-ДД
// где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
void printDate(const char *calendar, computus::Year year, computus::Month month, computus::Day day) {
    std::cout
        << calendar << ':' << ' ' << year
        << '-' << std::setw(2) << std::setfill('0') << int(month)
        << '-' << std::setw(2) << std::setfill('0') << int(day)
        << '\n';
}

int main() {
    computus::Year year;
    std::cout << "Year? ";
    std::cin >> year;

    computus::Month month;
    computus::Day day;

    computus::getAlexandrian(year, month, day);
    printDate("Alexandrian", year, month, day);

    computus::getGregorian(year, month, day);
    printDate("Gregorian", year, month, day);
}

Excel[править]

=DOLLAR(("4/"&A1)/7+MOD(19*MOD(A1,19)-7,30)*14%,)*7-6
Предполагается, что ячейка A1 содержит год.[1] Формула вычисляет дату католической Пасхи и даёт верный результат для 1900…2203 годов.

Go[править]

// computus.go

package computus

type Year = uint16
type Month = uint8
type Day = uint8

// easter вычисляет по данному фактору месяц и день Пасхи (приватная функция).
func easter(factor Year) (Month, Day) {
	return Month(factor / 31), Day(factor % 31 + 1)
}

// Alexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
func Alexandrian(year Year) (Month, Day) {
	d := (year % 19 * 19 + 15) % 30
	return easter(d + (year % 4 * 2 + year % 7 * 4 + 34 - d) % 7 + 114)
}

// Gregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
func Gregorian(year Year) (Month, Day) {
	a := year % 19
	b := year / 100
	c := year % 100
	h := (19 * a + b - b / 4 - (b - (b + 8) / 25 + 1) / 3 + 15) % 30
	l := (32 + (b % 4 + c / 4) * 2 - h - c % 4) % 7
	return easter(h + l - (11 * (h + l + l) + a) / 451 * 7 + 114)
}

Пример использования:

// computus_test.go

package main

import (
	"computus"
	"fmt"
)

// printDate выводит дату в формате:
// К: ГГГГ-ММ-ДД
// где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
func printDate(calendar string, year Year, month Month, day Day) {
	fmt.Printf("%s: %d-%02d-%02d\n", calendar, year, month, day)
}

func main() {
	var year computus.Year
	
	print("Year? ")
	fmt.Scanln(&year)

	var month computus.Month
	var day computus.Day

	month, day = computus.Alexandrian(year)
	printDate("Alexandrian", year, month, day)

	month, day = computus.Gregorian(year)
	printDate("Gregorian", year, month, day)
}

Pascal, Delphi[править]

{ computus.pas }

UNIT Computus;

INTERFACE

    TYPE
        TYear = Word;
        TMonth = 1..12;
        TDay = 1..31;

    {
    GetAlexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
    }
    procedure GetAlexandrian(year: TYear; var month: TMonth; var day: TDay);

    {
    GetGregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
    }
    procedure GetGregorian(year: TYear; var month: TMonth; var day: TDay);

IMPLEMENTATION

    {
    _Easter вычисляет по данному фактору месяц и день Пасхи (приватная функция).
    }
    procedure _GetEaster(factor: TYear; var month: TMonth; var day: TDay);
    begin
        month := factor div 31;
        day := factor mod 31 + 1
    end;

    procedure GetAlexandrian(year: TYear; var month: TMonth; var day: TDay);
    var d: TYear;
    begin
        d := (year mod 19 * 19 + 15) mod 30;
        _GetEaster(d + (year mod 4 * 2 + year mod 7 * 4 + 34 - d) mod 7 + 114, month, day)
    end;

    procedure GetGregorian(year: TYear; var month: TMonth; var day: TDay);
    var a, b, c, h, l: TYear;
    begin
        a := year mod 19;
        b := year div 100;
        c := year mod 100;
        h := (19 * a + b - b div 4 - (b - (b + 8) div 25 + 1) div 3 + 15) mod 30;
        l := (32 + (b mod 4 + c div 4) * 2 - h - c mod 4) mod 7;
        _GetEaster(h + l - (11 * (h + l + l) + a) div 451 * 7 + 114, month, day)
    end;

END.

Пример использования:

{ computus_test.pas }

PROGRAM ComputusTest;

USES Computus;

VAR year: Computus.TYear;
    month: Computus.TMonth;
    day: Computus.TDay;

{
PrintDate выводит дату в формате:
К: ГГГГ-ММ-ДД
где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
}
procedure PrintDate(calendar: String; year: Computus.TYear; month: Computus.TMonth; day: Computus.TDay);
begin
    Write(calendar, ': ', year, '-');
    if month < 10 then
        Write('0');
    Write(month, '-');
    if day < 10 then
        Write('0');
    WriteLn(day)
end;

BEGIN
    Write('Year? ');
    ReadLn(year);

    Computus.GetAlexandrian(year, month, day);
    PrintDate('Alexandrian', year, month, day);

    Computus.GetGregorian(year, month, day);
    PrintDate('Gregorian', year, month, day)
END.

Perl[править]

Rich Bowen’s модуль Date::Easter доступен на сайте CPAN .[2]

Python[править]

# computus.py

def _easter(factor):
    """
    _easter вычисляет по данному фактору месяц и день Пасхи (приватная функция).
    """
    return factor // 31, factor % 31 + 1

def alexandrian(year):
    """
    alexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
    """
    d = (year % 19 * 19 + 15) % 30
    return _easter(d + (year % 4 * 2 + year % 7 * 4 + 34 - d) % 7 + 114)

def gregorian(year):
    """
    gregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
    """
    a = year % 19
    b = year // 100
    c = year % 100
    h = (19 * a + b - b // 4 - (b - (b + 8) // 25 + 1) // 3 + 15) % 30
    l = (32 + (b % 4 + c // 4) * 2 - h - c % 4) % 7
    return _easter(h + l - (11 * (h + l + l) + a) // 451 * 7 + 114)

Пример использования:

# computus_test.py

import computus


def print_date(calendar, year, month, day):
    """
    print_date выводит дату в формате:
    К: ГГГГ-ММ-ДД
    где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
    """
    print("%s: %d-%02d-%02d" % (calendar, year, month, day))

year = int(input("Year? "))

month, day = computus.alexandrian(year)
print_date("Alexandrian", year, month, day)

month, day = computus.gregorian(year)
print_date("Gregorian", year, month, day)

Rust[править]

// computus.rs

type Year = u16;
type Month = u8;
type Day = u8;

// easter вычисляет по данному фактору месяц и день Пасхи (приватная функция).
fn _easter(factor: Year) -> (Month, Day) {
    ((factor / 31) as Month, (factor % 31 + 1) as Day)
}

// Alexandrian вычисляет для данного года месяц и день православной Пасхи (юлианская дата).
pub fn alexandrian(year: Year) -> (Month, Day) {
    let d = (year % 19 * 19 + 15) % 30;
    _easter(d + (year % 4 * 2 + year % 7 * 4 + 34 - d) % 7 + 114)
}

// Gregorian вычисляет для данного года месяц и день католической Пасхи (григорианская дата).
pub fn gregorian(year: Year) -> (Month, Day) {
    let a = year % 19;
    let b = year / 100;
    let c = year % 100;
    let h = (19 * a + b - b / 4 - (b - (b + 8) / 25 + 1) / 3 + 15) % 30;
    let l = (32 + (b % 4 + c / 4) * 2 - h - c % 4) % 7;
    _easter(h + l - (11 * (h + l + l) + a) / 451 * 7 + 114)
}

Пример использования:

// computus_test.rs

mod computus;

// read_var считывает данные из стандартного потока ввода.
fn read_var<Type: std::str::FromStr>(var: &mut Type) -> bool {
    let mut input_text = String::new();
    std::io::stdin()
        .read_line(&mut input_text)
        .expect("Unable to read from standard input.");
    match input_text.trim().parse::<Type>() {
        Ok(value) => {
            *var = value;
            true
        }
        Err(..) => false,
    }
}

// print_date выводит дату в формате:
// К: ГГГГ-ММ-ДД
// где К — название календаря, ГГГГ — год, ММ — двузначный номер месяца, ДД — двузначное число.
fn print_date(calendar: &str, year: computus::Year, (month, day): (computus::Month, computus::Day)) {
    println!("{}: {}-{:02}-{:02}", calendar, year, month, day);
}

fn main() {
    let mut year: computus::Year;

    print!("Year? ");
    if read_var(&mut year) {
        print_date("Alexandrian", year, alexandrian(year));
        print_date("Gregorian", year, gregorian(year));
    }
}

Программируемые микрокалькуляторы «Электроника»[править]

МК-61 / 52, МС-1104[править]

Александрийская пасхалия

Использование: <год> → регистр X, В/О, С/П (число в регистре X, номер месяца в регистре Y).

R — любой адресуемый регистр (0…9, a…e), r — любой другой адресуемый регистр.

00. X→П R	01. 7		02. ПП		03. 54		04. В↑		05. +		06. X→П r	07. П→X R	08. 4		09. ПП
10. 54		11. П→X r	12. +		13. В↑		14. +		15. 3		16. 4		17. +		18. X→П r	19. П→X R
20. 1		21. 9		22. ПП		23. 54		24. 1		25. 9		26. ×		27. 1		28. 5		29. +
30. 3		31. 0		32. ПП		33. 54		34. П→X r	35. ↔		36. X→П r	37. −		38. 7		39. ПП
40. 54		41. П→X r	42. +		43. 1		44. 1		45. 4		46. +		47. 3		48. 1		49. ПП
50. 54		51. 1		52. +		53. С/П		54. F ⥁		55. В↑		56. F Вx	57. ÷		58. F Вx	59. ↔
60. К[x]	61. В↑		62. F ⥁		63. ×		64. −		64. В/О

Православная пасхалия, григорианский календарь

Использование: <год> В/О, С/П; результат: РX = номер месяца, РY = число месяца.

П2		1		9		ПП		86		П3		ИП2		4		ПП		86
П4		ИП2		7		ПП		86		П5		1		9		ИП3		*
1		5		+		3		0		ПП		86		П6		2		ИП4
*		4		ИП5		*		+		6		ИП6		*		+		6
+		7		ПП		86		ИП6		+		П1		3		П4		ИП2
1		-		2		10^x	/		[x]		^		^		4		/
[x]		-		2		0		+		ИП1		+		П3		3		1
-		x>=0	76		П3		КИП4	ИП3		3		0		-		x>=0
83		П3		КИП4	ИП3		ИП4		С/П		П0		<->		П1		<->
/		[x]		ИП0		*		ИП1		-		/-/		В/О

Григорианская пасхалия

Б3-34, МК-54 / 56[править]

Отсутствующая команда выделения целой части К [x] заменяется последовательностью команд:

1			.			8			F 1/x		−			7			F 10ˣ		+			F Вx		−

Предыдущие и следующие команды остаются такими же.

Примечания[править]