尋找給定年份的復活節星期日的日期
1. 概述
在本教程中,我們將了解為什麼復活節日期計算起來很複雜。然後,我們將實作三種演算法來用 Java 計算它:Gauss、Butcher-Meeus 和 Conway 演算法。
2. 天主教復活節主日的歷史
復活節是慶祝耶穌基督從死裡復活的節日。復活節的時間最初與猶太教的逾越節有關,因為耶穌與門徒的最後晚餐是逾越節的晚餐。然而,在最初的幾個世紀裡,每個基督教社區都可以選擇一個日期來慶祝這個節日,這導致了一些爭議。 325年尼西亞會議最終標準化了復活節的定義:復活節星期日是春分後滿月後的第一個星期日。
計算復活節的日期具有挑戰性,因為它取決於陰曆和陽曆,而陰曆週期與陽曆週期不符。因此,數學演算法在確定復活節日期方面派上了用場。
3. 演算法
讓我們指出,所有演算法都專注於計算天主教復活節日期,該日期使用教宗格里高利十三世在 16 世紀末引入的公曆。另一方面,一些教堂,如俄羅斯東正教教堂,仍然使用儒略曆來確定復活節的日期。
現在,讓我們來看看每個演算法。
3.1.高斯演算法
德國著名數學家高斯在19世紀初第一個解決了這個問題。 他的演算法首先追蹤大致的月球軌道,然後確定精確的偏移量以獲得滿月後的周日。
我們來看看:
LocalDate computeEasterDateWithGaussAlgorithm(int year) {
int a = year % 19;
int b = year % 4;
int c = year % 7;
int k = year / 100;
int p = (13 + 8*k) / 25;
int q = k / 4;
int M = (15 - p + k - q) % 30;
int N = (4 + k - q) % 7;
int d = (19*a + M) % 30;
int e = (2*b + 4*c + 6*d + N) % 7;
if (d==29 && e == 6) {
return LocalDate.of(year, 4, 19);
} else if (d==28 && e==6 && ((11*M + 11)%30 < 10)) {
return LocalDate.of(year, 4, 18);
}
int H = 22 + d + e;
if (H <= 31) {
return LocalDate.of(year, 3, H);
}
return LocalDate.of(year, 4, H-31);
}
在此程式碼中:
a
代表黃金數字,表示年份在默通週期中的位置b
與閏年相關,確保根據二月的長度進行準確調整c
追蹤日曆,每個世紀沒有一次閏年p
和q
是中間變量,計算它們可以確定M
和N
,它們代表對 epact(農曆年和太陽年之間的差異)的調整d
是 3 月 21 日到復活節滿月的天數e
是逾越節滿月後的第一天和復活節星期日之間的天數
最後,有兩個例外,因為復活節滿月永遠不可能發生在 4 月 19 日,並且在極少數情況下,默冬週期的前一個滿月會在同一天出現,這是不允許的。
我們的方法傳回LocalDate
,因為它是表示沒有時區的日期的自然選擇。此外,我們可以對我們的方法進行單元測試:例如,以 2024 年為例,假設我們將類別命名為EasterDateCalculator
:
@Test
void givenEasterInMarch_whenComputeEasterDateWithGaussAlgorithm_thenCorrectResult() {
assertEquals(LocalDate.of(2024, 3, 31), new EasterDateCalculator().computeEasterDateWithGaussAlgorithm(2024));
}
3.2. Butcher-Meeus 演算法
這個演算法的起源令人驚訝:1876年,一份名為Nature
的英文報紙收到一封來自紐約的信,其中包含復活節日期計算的方法。一年後,米斯主教布徹證明這是對的。 Meeus 於 1991 年在他的Astronomical Algorithms
一書中普及了它。
如今,它在日曆相關的軟體和應用程式中使用最廣泛。
Butcher-Meeus 演算法透過結合基於改進的天文數據和計算技術的改進,增強了高斯的原始方法。
讓我們來實現它:
LocalDate computeEasterDateWithButcherMeeusAlgorithm(int year) {
int a = year % 19;
int b = year / 100;
int c = year % 100;
int d = b / 4;
int e = b % 4;
int f = (b + 8) / 25;
int g = (b - f + 1) / 3;
int h = (19*a + b - d - g + 15) % 30;
int i = c / 4;
int k = c % 4;
int l = (2*e + 2*i - h - k + 32) % 7;
int m = (a + 11*h + 22*l) / 451;
int t = h + l - 7*m + 114;
int n = t / 31;
int o = t % 31;
return LocalDate.of(year, n, o+1);
}
在此實作中:
-
a
是黃金數 -
b
是世俗年份 -
c
是年份 -
d
和e
處理閏世紀調整 -
f
和g
與 proemptosis 有關,這是對月球方程式的調整,以解決默冬週期的缺陷 -
h
代表 epact -
i
和k
協助進一步的閏年調整 -
l
代表一月的第一個星期日 -
m
是最終的修正項,使我們能夠計算復活節的月份和日期
3.3.康威演算法
在20世紀下半葉,英國數學家約翰·康威提出了一種新的計算復活節日期的獨創方法。為此,他引入了關鍵日的概念,即一系列始終發生在同一工作日的每月日期,作為計算重大事件的基本參考點。
我們來編碼一下:
LocalDate computeEasterDateWithConwayAlgorithm(int year) {
int s = year / 100;
int t = year % 100;
int a = t / 4;
int p = s % 4;
int x = (9 - 2*p) % 7;
int y = (x + t + a) % 7;
int g = year % 19;
int G = g + 1;
int b = s / 4;
int r = 8 * (s + 11) / 25;
int C = -s + b + r;
int d = (11*G + C) % 30;
d = (d + 30) % 30;
int h = (551 - 19*d + G) / 544;
int e = (50 - d - h) % 7;
int f = (e + y) % 7;
int R = 57 - d - f - h;
if (R <= 31) {
return LocalDate.of(year, 3, R);
}
return LocalDate.of(year, 4, R-31);
}
更準確地說,在這段程式碼中:
-
s
是世俗年份,t
年份 -
a
有助於確定一個世紀內的閏年 -
x
是長期關鍵日 -
y
是當年的關鍵日 -
G
表示黃金數 -
b
與輪迴有關,這是對月球方程式的修正,以防止復活節日期晚一天到來 -
r
是先發制 -
C
是長期校正 -
d
確定逾越節滿月日 -
h
考慮與 epact 相關的例外情況 -
e
測量逾越節滿月和關鍵日之間的偏差 - 最後,
f
代表復活節滿月的星期幾,使我們能夠最終計算出復活節的日期
4。
在本文中,我們了解了為什麼需要演算法來計算復活節日期並實現了三個最著名的演算法。高斯和 Butcher-Meeus 演算法的簡化用於計算儒略曆中的復活節日期。然而,對於仍使用儒略曆的教會來說,這並不是故事的結束。幾乎所有國家都使用公曆,所以最後,他們必須將日期轉換為這個日曆。
與往常一樣,程式碼可以在 GitHub 上取得。