坂本算法查找星期几的正确性

发布于 2024-11-15 19:32:06 字数 380 浏览 1 评论 0原文

我正在使用坂本算法来找出给定日期的星期几。 谁能告诉我这个算法的正确性吗?我只想从 2000 年到 2099 年。

来自 Wikipedia 的算法仅供参考。

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;
}

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

各空 2024-11-22 19:32:06

好吧,您只需查看它就可以知道它是正确的...假设 t[] 数组是正确的,您只需进行 12 次抽查即可验证这一点(每月一次,使用任何天/年)。

y -= m 3 是一个很好的技巧。它创建了一个从 3 月 1 日开始到 2 月 28 日(或 29 日)结束的“虚拟年”,将额外的一天(如果有)放在该年的年底;或者更确切地说,在上一年年底。例如,虚拟年份 2011 于 3 月 1 日开始,将于 2 月 29 日结束,而虚拟年份 2012 将于 3 月 1 日开始,于次年 2 月 28 日结束。

通过将闰年的添加日放在虚拟年份的末尾,表达式的其余部分被大大简化。

我们看一下总和:

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

正常的一年有365天。即 52 周加 1 天。因此,一般来说,一周中的某一天每年都会变化一天。这就是 y 术语的贡献;它每年都会为这一天加一。

但每四年就是一个闰年。这些人每四年多贡献一天。由于使用了虚拟年份,我们只需将 y/4 添加到总和中即可计算 y 年中发生了多少个闰日。 (请注意,此公式假设整数除法向下舍入。)

但这并不完全正确,因为每 100 年都不是闰年。所以我们必须减去y/100

只不过每 400 年就会有一次闰年。所以我们必须添加y/400

最后,我们只需添加该月的日期 d 以及与月份相关的表的偏移量(因为一年中的月份边界相当任意)。

然后取整个 mod 7,因为这就是一周的长度。

(例如,如果周是八天,这个公式会发生什么变化?显然,它会是 mod 8。此外,y 需要是 5*y,因为 365 % 8 == 5。月份表 t[] 也需要调整。)

顺便说一句,维基百科关于日历“直到 9999 年都有效”的说法完全是任意的。无论我们坚持使用公历多久,这个公式都是有效的,无论是10年、100年还是1000年年,或一百万年。

[编辑]

上述论证本质上是归纳证明。也就是说,假设该公式适用于特定的 (y,m,d),您证明它适用于 (y+1,m,d) 和 ( y、m、d+1)。 (其中 y 是从 3 月 1 日开始的“虚拟年份”。)所以关键问题是,当您从一年移动到下一年时,总和是否会发生正确的变化?了解了闰年规则,并且“虚拟年”在年底有额外的一天,这很简单。

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.

无人接听 2024-11-22 19:32:06

最近,我在此处撰写了有关此算法的博客文章。

算法背后的基本思想是对二月和一月进行计数
自上一年 12 月 31 日起的一周。对于所有其他月份,我们将从当前年份 12 月 31 日开始计算星期几。我们分两次计算
步骤 首先,我们计算当月 m 之前一个月的最后一天是星期几,然后我们只需添加 d 模 7。

BC 1 12 月 31 日是星期日,编码为 0,星期一为 1,依此类推。
所以我们有: 0 + y + y/4 - y/100 + y/400 其中 y -= m 3 计算
今年或上一年 12 月 31 日的星期几(取决于月份)。注意:365 % 7 == 1 这解释了为什么我们写 y 而不是 365*y。最后一个组件 d 很明显,因为我们从上个月的最后一天开始计算星期几。

需要解释的最后一部分是数组中的值,前两个值是自去年 12 月 31 日到月初 %7 的天数。对于其余月份,它们对从上个月月底到当年 12 月 31 日的天数取模 7 取反。换句话说,我们通过加法模 7 来减去天数,例如 (ab)%7 = (a+(7-b%7))%7

您可以在我的博客文章中找到更多解释。

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.

温柔少女心 2024-11-22 19:32:06

这可能不是像上面提到的一些完整的答案,但只是想添加有关此数组的一件事: 0 3 2 5 0 3 5 1 4 6 2 4

考虑从三月开始到结束的月份在二月就像其他人所说的那样:

  1. 三月
  2. 四月
  3. 五月
  4. 六月
  5. 七月
  6. 八月
  7. 九月
  8. 十月
  9. 十一月
  10. 十二月
  11. 一月
  12. 二月

从上面的编号样式写一月到十二月:

所以将其视为一个数组:
int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};

现在对于数组中的所有元素只需执行以下操作:(2.6*米 - 0.2) 模 7
将结果解析为整数,您将得到:
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;
}

这个:+ y/4 - y/100 + y/400 与闰年有关。检查闰年的算法是:

  1. 能被 400 整除 -> true
  2. 如果能被 100 整除但不能被 400 -> False
  3. 能被 4 整除 -> True

对上述订单执行检查。也许这就是为什么他们减去 y/100 并添加 y/4 和 y/4 的原因。 y/400。是的,愚蠢的逻辑

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.

旧人哭 2024-11-22 19:32:06

对于公历

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;
}

说明:

  • 请参阅注释数组
 t[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};

并将其与全年日历进行比较(在 linux/unix 中运行 cal 2 在终端中生成日历)注意该周的开始日期每个月的一天。

  • 每个正常年份每周都会移动一天,而闰年则每周会移动两天。如 (365%7)=1 和 (366%7)=2
 i= y+y/4-y/100+y/400
  • 但如果 y 是闰年 0 月和 1 月,我们不应该计算额外的一天
y-=m<3
  • 但通过这种方式,我们也删除了额外的一天也来自非闰年。因此,我们将在 2 月之后每月减去 1 天来填补空白。

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

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};

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文