Die Osterrechnung von Donald Knuth

Karl-Heinz Lewin

Ich verwende hier die Formeln für den Julianischen und den Gregorianischen Kalender aus dem Artikel The Calculation of Easter... von Donald Knuth:


Julianischer Kalender  Bedeutung
golden_number = year mod 19 + 1 Goldene Zahl
epact = ( 11 * golden_number - 4 ) mod 30 + 1 Alter des Kalendermonds am Jahresanfang
extra_days = 5 * year div 4 mod 7 bestimmt, wann im März Sonntag ist
om = 44 - epact, if om < 21 then om += 30 Tag des Ostermonds, des. ersten Frühlingsvollmonds, gezählt ab 1. März
os = om + 7 - ( om + extra_days ) mod 7 Tag des Ostersonntags, gezählt ab 1. März
Gregorianischer Kalender  Bedeutung
golden_number = year mod 19 + 1 Goldene Zahl
century = year div 100 + 1 Jahrhundert
gregorian_correction = ( 3 * century ) div 4 - 12 Anzahl der vergangenenen Jahre wie 1700, 1800, 1900, die keine Schaltjahre waren
clavian_correction = ( century - 16 - ( century - 18 ) div 25 ) div 3
epact = ( 11 * golden_number + 20 + clavian_correction - gregorian_correction ) mod 30
if ( epact <= 0 ) then epact += 30
if ( epact == 25 and golden_number > 11 ) or epact == 24 ) then epact += 1
Alter des Kalendermonds am Jahresanfang
extra_days = ( 5 * year div 4 - gregorian_correction - 10 ) mod 7 bestimmt, wann im März Sonntag ist
om = 44 - epact, if om < 21 then om += 30 Tag des Ostermonds, des. ersten Frühlingsvollmonds, gezählt ab 1. März
os = om + 7 - ( om + extra_days ) mod 7 Tag des Ostersonntags, gezählt ab 1. März

Hinweis: Die fettgedruckten Operationen "mod 7" in den Berechnungen der "extra_days" fehlen in der Veröffentlichung der CACM. Dieses Fehlen wird zwar in der Berechnung des Ostersonntags in der letzten Zeile wieder ausgeglichen, würde aber die Darstellung der Zwischenergebnisse unverständlich machen. Z.B. würde für 2024 (Julianisch) extra_days = 2530 werden, mod 7 dagegen extra_days = 3.

Implementierung in einem Tabellenkalkulatiosnprogramm

(Hier Microsoft Excel 2007, deutschsprachige Lizenz)

Ich zeige hier nur die Lösung für den Julianischen Kalender; die Lösung für den Gregorianischen Kalender wäre sehr viel aufwändiger, weshalb ich sie mir erspare.

Feld

Inhalt

Bedeutung

A1

'Jahr

Jahreszahl

B1

'GoldenN

Goldene Zahl

C1

'Epact

Epakte

D1

'ED

Extratage

E1

'EM

Tag des Ostermonds

F1

'EMcorr

korrigierter Tag des Ostermonds

G1

'ES

Tag des Ostersonntags, gezählt ab 1. März

H1

'ES

Tagesdatum des Ostersonntags

I1

'Month

Monat des Ostersonntags

A2

532

Startjahr

B2

=REST(A2;19)

C2

=REST(11*B2-4;30)+1

D2

=REST(GANZZAHL((5*A2)/4);7)

E2

=44-C2

F2

=WENN(E2<21;E2+30;E2)

G2

=21+E2)

H2

=WENN(G2>31;G2-31;G2)

I2

=WENN(G2>31;4;3)

A3

=A2+1

B3:I3

Kopie von B2:I2

A4:I96

Kopie von A3:I3

Ausgabe

  Year    GoldenN Epact   ED    EM  EMcorr  ES  ESdate  Month
   532        1      8     0    36    36    42    11      4  
   533        2     19     1    25    25    27    27      3  
   534        3     30     2    14    44    47    16      4  
   535        4     11     3    33    33    39     8      4  
   536        5     22     5    22    22    23    23      3  
   537        6      3     6    41    41    43    12      4  
   538        7      4     0    30    30    35     4      4  
   539        8     25     1    19    49    55    24      4  
   540        9      6     3    38    38    39     8      4  
   541       10     17     4    27    27    31    31      3  
   542       11     28     5    16    46    51    20      4  
   543       12      9     6    35    35    36     5      4  
   544       13     20     1    24    24    27    27      3  
   545       14      1     2    43    43    47    16      4  
   546       15     12     3    32    32    39     8      4  
   547       16     23     4    21    21    24    24      3  
   548       17      4     6    40    40    43    12      4  
   549       18     15     0    29    29    35     4      4  
   550       19     26     1    18    48    55    24      4  
   ...                                                       


Implementierung in JavaScript

	function DaynumberToDayAndMonth( daynumber ) {
		// assert( daynumber > 0 && daynumber < 62 );
		if ( daynumber <= 31 ) {
			this.dd = daynumber;
			this.mm = 3;
		} else {
			this.dd = daynumber - 31;
			this.mm = 4;
		}
		return this;
	}
	
	function floor( a ) {
		return Math.floor( a );
	}

	function div( a, b ) { // integer division
		return floor( a / b );
	}

	function DonKnuthGregorianEasterTableLine( year ) {
		var golden_number, century, gregorian_correction, clavian_correction, extra_days, epact;
		// assert( year >= 0 && year <= 4999 );
		golden_number = this.k_gn;
		century = div( year , 100 ) + 1;
		gregorian_correction = div( 3 * century, 4 ) - 12;
		clavian_correction = div( century - 16 - div( century - 18, 25 ), 3 );
		extra_days = ( div( 5 * year, 4 ) - gregorian_correction - 10 ) % 7 ;
		epact = ( 11 * golden_number + 20 + clavian_correction  - gregorian_correction ) % 30;
		if ( epact <= 0 ) {
			epact += 30;
		}
		if ( epact == 25 && golden_number > 11 || epact == 24 ) {
			epact += 1;
		} 
		this.k_ep = epact;
		this.k_ed = extra_days;
		return this;
	}

	function DonKnuthJulianEasterTableLine( year ) {
		var golden_number, extra_days, epact;
		// assert( year >= 0 && year <= 4999 );
		golden_number = this.k_gn;
		extra_days = ( div( 5 * year, 4 ) ) % 7 ;
		epact = ( 11 * golden_number - 4 ) % 30 + 1;
		this.k_ep = epact;
		this.k_ed = extra_days;
		return this;
	}

	function DonKnuthEasterTableLine( year, cal ) {
		var golden_number, day, epact, extra_days, ee;
		// assert( year >= 0 && year <= 4999 );
		golden_number = year % 19 + 1;
		this.k_yr = year;
		this.k_gn = golden_number;
		switch ( cal ) {
			case 'J': // Julian calendar
			    ee = DonKnuthJulianEasterTableLine( year );
				break;
			case 'G': // Gregorian calendar
			    ee = DonKnuthGregorianEasterTableLine( year );
				break;
			case 'A': // Occidental calendar ("Abendländischer Kalender")
			    ee = ( year <= 1582 ) 
				   ? DonKnuthJulianEasterTableLine( year )
				   : DonKnuthGregorianEasterTableLine( year );
				break;
		}
		epact = this.ep;
		extra_days = this.k_ed;
		day = 44 - epact;
		if ( day < 21 ) {
			day += 30;
		}
		this.k_om = day;
		day += 7 - ( day + extra_days ) % 7;
		this.k_os = day;
		return this;
	}

	function DonKnuthEasterTable( annus, times, cal, outputformatter ) {
		// assert( annus >= 0 && annus <= 4996 );
		// assert( times >= 4 && times <= 532 );
		// assert( annus + times <= 5000 );
		for ( let y = annus; y < annus + times; y++ ) {
			var line = DonKnuthEasterTableLine( y, cal );
			outputformatter( line );
		}
	}


Die formelle Überprüfung des Algorithmus

Wenn Sie auf den unten folgenden Knopf klicken, dann erscheint zum Vergleich mit dem weiter oben gezeigten ersten 19-jährigen Abschnitt der Ausgabe des Tabellenkalkulationsprogramms eine nach dem soeben erschlossenen Algorithmus mit den gezeigten JavaScript-Funktionen erzeugte Berechnung der Ostermond- und Ostersonntagsdaten mit allen Zwischenergebnissen..
Einen Osterrechner mit diesem Algorithmus, bei dem Sie das Startjahr, die Anzahl der Jahre und die Art des Kalenders (Julianisch, Gregorianisch, Abendländisch (d.h. Julianisch bis 1582, Gregorianisch ab 1583)) wählen können, finden Sie unter Die Osterrechnung von Donald Knuth als Tabellenrechner.



Literatur

Donald Knuth (1962): The calculation of Easter...; Communications of the ACM (CACM), Volume 5, Issue 4; April 1962; S. 209-210; https://doi.org/10.1145/366920.366980


Der Autor ist Mathematiker und arbeitete als Software-Entwickler.

Karl-Heinz Lewin, Haar: karl-heinz.lewin@t-online.de

Copyright © Karl-Heinz Lewin, 2024