Correctness of Sakamoto's algorithm to find the day of week
Asked Answered
M

4

64

I am using Sakamoto's algorithm to find out the day of week from a given date. Can anybody tell me the correctness of this algorithm? I just want this from 2000 to 2099.

The algorithm from Wikipedia is given for reference.

int dow(int y, int m, int d)
{
   static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
   y -= m < 3;
   return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
Marathi answered 17/6, 2011 at 11:38 Comment(0)
S
132

Well, you can tell just by looking at it that it is correct... Assuming that the t[] array is correct, which you can verify with just 12 spot checks (one for each month using any day/year).

The y -= m < 3 is a nice trick. It creates a "virtual year" that starts on March 1 and ends on February 28 (or 29), putting the extra day (if any) at the end of the year; or rather, at the end of the previous year. So for example, virtual year 2011 began on Mar 1 and will end on February 29, while virtual year 2012 will begin on March 1 and end on the following February 28.

By putting the added day for leap years at the end of the virtual year, the rest of the expression is massively simplified.

Let's look at the sum:

(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7

There are 365 days in a normal year. That is 52 weeks plus 1 day. So the day of the week shifts by one day per year, in general. That is what the y term is contributing; it adds one to the day for each year.

But every four years is a leap year. Those contribute an extra day every four years. Thanks to the use of virtual years, we can just add y/4 to the sum to count how many leap days happen in y years. (Note that this formula assumes integer division rounds down.)

But that is not quite right, because every 100 years is not a leap year. So we have to subtract off y/100.

Except that every 400 years is a leap year again. So we have to add y/400.

Finally we just add the day of the month d and an offset from a table that depends on the month (because the month boundaries within the year are fairly arbitrary).

Then take the whole thing mod 7 since that is how long a week is.

(If weeks were eight days, for example, what would change in this formula? Well, it would be mod 8, obviously. Also the y would need to be 5*y, because 365 % 8 == 5. Also the month table t[] would need adjusting. That's it.)

Incidentally, Wikipedia's statement that the calendar is "good until 9999" is totally arbitrary. This formula is good for however long we stick with the Gregorian calendar, whether that is 10 years, 100 years, 1000 years, or 1 million years.

[edit]

The above argument is essentially a proof by induction. That is, assuming that the formula works for a particular (y,m,d), you prove that it works for (y+1,m,d) and (y,m,d+1). (Where y is a "virtual year" starting March 1.) So the key question is, does the sum change by the correct amount as you move from one year to the next? With knowledge of the leap year rules, and with the "virtual year" having the extra day at year end, it trivially does.

Snaggletooth answered 17/6, 2011 at 12:46 Comment(7)
Can you please explain the t array. I am not able to work it out.Marquismarquisate
@user1677597 For info on t array plz visit - quora.com/How-does-Tomohiko-Sakamotos-Algorithm-workWellthoughtof
It's worth noting that the Gregorian (aka western) calendar STARTS at March 1st. If you want to read more about this, read Naggum's excellent The Long Painful History of Time: naggum.no/lugm-time.htmlGift
I just want to add this : c++ int t[] = {11,12,1,2,3,4,5,6,7,8,9,10}; Note the formula : (2.6*m - 0.2) mod 7, ofcourse this is (int). this gives that array : 0 3 2 5 0 3 5 1 4 6 2 4Henriques
I didn't get the part where we are subtracting 1 from all the months except Jan and feb. Could someone explain please?Afreet
@Sumit: We are not subtracting anything from the month. We are subtracting one from the year if the month is January or February.Snaggletooth
Actually I was asking about the array t[ ]= {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; The original array was this t[ ] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; .Why did we subtracted one from 3rd element till end?Afreet
S
4

Recently I wrote blog post about this algorithm here.

The basic idea behind algorithm is to for February and January to count day of week from 31 Dec of the previous year. For all other months we will be counting day of week from current year 31 Dec. We do this in two steps first we compute day of week of last day of month preceding current month m then we just add d modulo seven.

31 Dec 1 BC is Sunday that is encoded as 0, Monday is 1 etc. So we have: 0 + y + y/4 - y/100 + y/400 this with y -= m < 3 computes day of week of 31 Dec of current year or previous year (depending on month). Note: 365 % 7 == 1 this explains why we wrote y instead of 365*y. The last component d is obvious since we start counting day of week from previous month last day.

The last part that need to be explained are values in array, for first two values these are number of days since last year 31 Dec to start of the month % 7. For rest of the months they are negated modulo seven number of days from end of prev month to 31 Dec of the current year. In other words we are subtracting days by addition modulo 7 e.g. (a-b)%7 = (a+(7-b%7))%7.

More explanation you may find in my blog post.

Sinkage answered 18/6, 2016 at 17:38 Comment(0)
H
1

This might not be a complete answer like some mentioned above, But just would like to add one thing regarding this array : 0 3 2 5 0 3 5 1 4 6 2 4

Consider months beginning from March and ending at February just like others said:

  1. March
  2. April
  3. May
  4. June
  5. July
  6. August
  7. September
  8. October
  9. November
  10. December
  11. January
  12. February

Writing January to December from above numbering style :

So consider this as an array : int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};

Now for all elements in array just do : (2.6*m - 0.2) mod 7 parse the result as integer and you will get this: 0 3 2 5 0 3 5 1 4 6 2 4

int dayOfWeek(int d, int m, int y){
  // Months Array
  int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};

  // Convert months array
  for (int i = 0; i < 12; i++){
    int ans = t[i] * 2.6 - 0.2;
    t[i] = ans % 7;
  }

  // Continue Algo
  if(m<3)
    y -= 1;

  int day = (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
  return day;
}

this : + y/4 - y/100 + y/400 is related to leap year. The algo to check for leap year is :

  1. Perfectly Divisible by 400 -> true
  2. IF perfectly divisible by 100 but not by 400 -> False
  3. Divisible by 4 -> True

perform checks on above order. Maybe that is why they subtracted y/100 and added y/4 & y/400. Yeah silly logic 😅

I know this might not be the answer, But this might help to those who find it difficult to remember/understand stuff, yeah! not all of us have high IQ levels of understanding stuff and sadly some of us can't remember stuff too, lol.

Henriques answered 8/9, 2019 at 4:30 Comment(0)
S
1

For Gregorian Calendar

int dayToWeekG(int d,int m,int y){
    int i;
    int t[12]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
            //{0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
    y-=m<3;
    i=(y+y/4-y/100+y/400 +t[m-1]+d)%7;
    return i;
}

Explanation:

  • See the commented array for
 t[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};

and compare it with a calendar of a whole year (run cal 2to generate calendar in terminal in linux/unix) notice the starting day of the week of the day for each month.

  • Every normal year shifting one day of the week and leap year shifting two days of the week. as (365%7)=1 and (366%7)=2
 i= y+y/4-y/100+y/400
  • But we are should not calculate the extra day if y is a leap year for month 0 and 1
y-=m<3
  • but by this way we are also removing the extra day from non-leap years too. so we will fill up the gap by subtracting 1 day for every month after February.

    int t[12]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

Stevestevedore answered 9/9, 2019 at 8:11 Comment(1)
I didn't get the part where we are subtracting 1 from all the months except Jan and feb. Could you explain please?Afreet

© 2022 - 2024 — McMap. All rights reserved.