Though the word Computus can technically describe any sort of computation {{ "bede725" | cite }} or else a set of medieval tables for calculating various astrological events {{ "dictcomputus" | cite }}, it is also one of the most common historical names for the calculation of the Christian holiday of Easter every year. Nominally, Easter happens the Sunday after the first full moon after the spring equinox (roughly March 21st). This particular full moon is known by a number of names, such as the Pink (Strawberry) Moon, Hunter's Moon, or the Snow Moon, along with several others. The most common name for it is the paschal full moon, which translates to "Passover" in Greek and signifies an important Jewish festival.
For the first few centuries, the date of Easter each year was dictated by the Pope; however, after the church grew, it was no longer straightforward to communicate this date to all of Christendom. As such, the church did what it could to algorithmically generate tables for clergy to determine the date of Easter each year. To this day, the calculation of Easter still poses a problem, with western and eastern (orthodox) churches celebrating on different dates approximately 50% of the time.
I'll be honest, there is a lot of good, Christian drama surrounding the calculation of this event and it's remarkably interesting to read about {{ "bien2004" | cite }}. Suffice it to say that the date of Easter bamboozled many historical scholars, with at least one algorithm appearing in the early archives of the now famous scientific journal of Nature {{ "computus1876" | cite }}. The calculation was so complicated that even Frederick Gauss had to try his hand at it (and failed before being corrected by one of his students).
Essentially, the date of Easter depends on both the lunar and solar cycles The date of the paschal full moon, for example, is static in the lunar calendar, but it is not in the solar calendar. In this way, computus is the act of mapping a lunar cycle onto the Gregorian (solar) calendar everyone knows and loves. Because many different calendar systems have existed throughout history, there was a natural question as to which calendar system would be used to calculate the precise date of Easter. The western churches chose the Gregorian calendar and the eastern churches chosethe Julian one, and this is one reason why western and eastern churches sometimes celebrate on different dates. That said, the Gregorian calendar more accurately represents the true date of the paschal full moon, so the western church's approach ended up being more precise.
Though there are many methods to calculate Easter, for now, we will focus only on Gauss's algorithm; however, we mayl certainly come back (in subsequent years) to incorporate other Easter algorithms if there is demand. These algorithms are some of my favorite gems in the history of algorithm design because of all the drama surrounding the calculation of something that seems trivial! After all, how hard could it be to calculate Easter?
Gauss is known for a lot of things: Gaussian elimination, the Cooley-Tukey method before Cooley or Tukey even existed, Gauss's Law for electromagnetism, etc. One thing he is not particularly well known for is an algorithm he devised in 1800, which was later corrected by his student Peter Paul Tittle in 1816. In fact, there were a series of publications from Gauss in this era all relating to the precise date of Easter. The legend goes that Gauss actually did not know his real birthday in the Gregorian calendar and used this same algorithm to determine it. Apparently, his mother only told him that he was born on a Wednesday 8 days before Ascension Day in 1777, which corresponds to April 30th {{ "bien2004" | cite }}.
Honestly, Gauss's Easter algorithm was the 19th century equivalent of undocumented code. I could imagine Gauss grumpily "patching" his method when users complained that it did not work on dates past 4200 or even certain dates within his own era! When some of his compatriots (such as Johann Lambert and Jean Joseph Delambre) expressed their concern over the method's performance, Gauss replied by saying,
The investigation by which the formula [...] is found is based on higher arithmetic, for which I presumably cannot refer to any publication.
Which was the 19th century equivalent of saying, "you are too dumb to understand my genius." I have definitely met a few fledgling programmers who feel the same, but none of them were anywhere near as prolific as Gauss.
One of the most important fans of Gauss's work was Servois, who created a calendar based on Gauss's 1800 publication, shown below:
This calendar shows the date the paschal full moon, indicating that Easter will be the following Sunday {{ "servois" | cite }}.
In this table, a value greater than 22 indicates the full moon will be on the presented number (date) in March and a value less than 22 indicates the full moon will be on that date in April.
The
The task for this chapter will be to explain (to the best of my abilities) how one would go about using Gauss's Easter algorithm to calculate the date of Easter for any given year (within the limitations of the algorithm).
Because Easter is the Sunday following the paschal full moon, which is the first full moon of spring, Gauss's algorithm is tasked at finding a way to map the lunar calendar to the Gregorian (solar) calendar. For this reason, before discussing the algorithm, itself, we must first introduce both calendar systems. The Gregorian (solar) calendar has been created to mark Earth's full revolution around the Sun, which is approximately 365.2425 days. Unfortunately, days are based on the Earth's rotation about its axis, not its revolution around the Sun, so the number of days in a year is not an integer number (such as 365). This discrepancy has actually lead to a large number of calendar systems, including one invented by Gauss, himself {{ "standish2004" | cite }}. Before the Gregorian calendar, there was another correction made from an old Roman calendar to set the days in a year to be 365.25 days. This was called the Julian calendar. From there, the Julian calendar was further corrected to the Gregorian calendar with 365.2425 days. Though there is only a small change necessary to use Gauss's Easter algorithm for the Julian calendar, this will not be covered here; however, if you want to see this, we can add it in upon request.
To account for the non-integer nature of the Gregorian year, a leap day is celebrated on February 29th every 4 years, with exception of when the year is a multiple of 100, where no leap-day is observed; if the year is divisible by 400, however, a leap day is still observed. This means that every 400 years, there are 97 leap days. This is why a leap day was celebrated in 2020 and 2000, but was not in 1900. If at this point, you feel like your favorite calendar system is held together by duct tape and string, you would be right.
In addition to the solar year, Gauss's Easter algorithm also needs to keep the lunar year into account. A lunar month corresponds to the time it takes the Moon to complete one full revolution around the Earth. In most cases, this is approximately 27.5 days {{ "lunar_month_wiki" | cite }}. That said, space is complicated and the Moon is not the only revolving body. Lunar phases are related to the time it takes for the Moon to return to its location in relation to the line connecting the Sun and Earth, as shown below:
This is called the synodic month and will be the approximation used for this chapter. Below, we also show a snapshot of this simulation after 6 synodic months:
Here, we show an outline of the Earth and Moon in an arbitrary initial position, each with an angle of
Here, we see the Sun at the center, with the Earth and Moon starting the year at an angle of
Because the synodic month and the solar year are not synchronized, the phase of the Moon will be different on the same day of the Gregorian year. That said, the lunar and solar calendars will re-synchronize roughly every 19 years. For example, if there is a new moon on January 1st, 2020, there will not be a new moon on January 1st, 2021; however, there will be a new moon on January 1st, 2039. This 19-year cycle where the Moon and Sun are waiting to re-synchronize is known as the Metonic cycle and has been studied for centuries.
This cycle allows us to somewhat easily transition between solar and lunar calendars. If we imagine any Gregorian date (let's say January 1st again for clarity), the moon could be in one of 19 different phases, as shown below:
Here, we show each possible phase of the moon as an outline, but the actual phase as a grey circle. Essentially, by knowing what year we are on in the Metonic cycle, we can single out which phase of the moon we will see on any given date. This is powerful and will allow us to find the next full moon by looking ahead a few days.
As a final note, there is a small offset in the Metonic cycle of 1 hour and 45 minutes every 19 years, so in 2500 years, it will be 8 days off, but that's a problem for people in 2500. For now, we should be able to start discussing the algorithm, itself.
As alluded to in Gauss's quote above, the Easter algorithm is closer to a set of formulas than a method used to compute anything on a modern computer. This is partially because of bad software engineering by Gauss and partially because computers did not really exist at that point. Considering this method was literally called Computus, there probably was not much to compute at all at the time. Nowadays, you could more easily find the date of Easter with loops and conditions, but this is the Arcane Algorithm Archive, and this is definitely an arcane algorithm, so let's go!
For this section, we will be following similar notation to Gauss's original 1800 work, which is a bit terse and hard to follow; however, each term is significantly meaningful. If you are reading this and think you have a better way to present anything, please let us know (with an issue or pull request on github) and we can correct the text!
This method can be split into 2 parts:
- Calculating the days from March 21st to the next full moon
- Calculating the days from the full moon to the next Sunday
In the following sections, we will discuss both individually.
To start, we will be calculating
This expression represents the fact that the Metonic cycle will be 8 days off every 2500 years and adds an additional offset of 13 to ensure the Metonic cycle aligns with empirical observation.
At this point, we know what year we are at on the Metonic calendar and have calculated an offset accordingly; however, we have yet to take into account leap years.
As stated above, there are 97 leap days every 400 years, and the calculation of
This means that
where 15 is an offset indicating that the full moon on year 0 is 15 days from March 21st,
With all this information, we can finally calculate the number of days from March 21st until the first full moon, as
Again, the
Every 12 lunar months is roughly 354 days, which is 11 days shorter than 365.
This means that every year in the Metonic cycle, the lunar phase will be 11 days behind.
It just so happens that
Regardless, we now have
Which shows that the date of the paschal full moon for 2020 is April 9th. Now we can move on to finding the precise date of Easter, which should be the following Sunday
This calculation will take a few variables from the previous section, namely
From here, things get a little tricky.
There are 52 weeks in a year, but
January 1st | Day of the week | Special considerations |
---|---|---|
2017 | Sunday | None |
2018 | Monday | None |
2019 | Tuesday | None |
2020 | Wednesday | Leap Year |
2021 | Friday | None |
Simply put, every year we should subtract one day of the week, but on leap years, we should subtract 2. To keep tabs on this, we need two separate counts,
$$
b = \text{year}~%4,
$$
and
$$
c = \text{year}%~7,
$$
where
With all these terms put together, we can finally calculate the offset from the full moon to Easter Sunday as
Here, all terms are described as above and the multiplicative factor of 6 to
At this point, we can calculate the days from March 21st to Easter Sunday to be
Remember that March 22nd would be the first possible day to celebrate Easter because March 21st would be the first possible full moon of spring. All said, there are a few exceptions that are somewhat tricky to understand, namely:
These conditionals are placed on the output of
Many say that these conditionals are placed on the output for historical reasons, but between you and me, I feel there is a more mathematical reason that I do not fully understand.
After all, why is the correction for
As mentioned, this particular algorithm does not make use of any standard computational techniques. There are no loops, conditionals, stacks, or queues. However, there can be no doubt that Gauss was a master of his craft. The sheer complexity of this calculation both baffles and astounds me -- especially because this was done hundreds of years before computational thinking became common-place.
Sure, this can be done straightforwardly with a calculator, but it is no doubt an algorithm worth discussing and celebrating for its ingenuity at the time of creation.
Here is a video describing key elements of Gauss's Easter Algorithm:
Unlike many other chapters in the Algorithm Archive, this particular method can be described almost entirely by mathematical expressions.
As such, it should be relatively straightforward to implement in a number of different languages, and I heartily encourage you to do so!
For now, we have the code outputting a tuple of
{% method %} {% sample lang="jl" %} import, lang:"julia" {% sample lang="hs" %} import, lang:"haskell" {% sample lang="py" %} import, lang:"python" {% sample lang="crystal" %} import, lang:"crystal" {% sample lang="rust" %} import, lang:"rust" {% sample lang="ps1" %} import, lang:"powershell" {% sample lang="c" %} import, lang:"c" {% sample lang="cpp" %} import, lang:"cpp" {% sample lang="lisp" %} import, lang:"lisp" {% sample lang="nim" %} import, lang:"nim" {% sample lang="scala" %} import, lang:"scala" {% sample lang="dart" %} import, lang:"dart" {% sample lang="javascript" %} import, lang:"javascript" {% sample lang="typescript" %} import, lang:"typescript" {% endmethod %}
{% references %} {% endreferences %}
<script> MathJax.Hub.Queue(["Typeset",MathJax.Hub]); </script>The code examples are licensed under the MIT license (found in LICENSE.md).
The text of this chapter was written by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "Servois 1800 Colored Table" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "Relative Orbits" was created by Xadisten and was provided during a discussion on Twitch. It is licensed under the Creative Commons Attribution 4.0 International License.
- The image "Synodic Half Year" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "Metonic shadows" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "Full Year Orbit" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "Servois 2000 Colored Table" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.