(转载)星期几问题——蔡勒公式

前言

这篇博客主要是参考网上的一个帖子,然后自己推断验证,在原创和转载之间犹豫了很久,鉴于我确实做了很多推导工作,姑且就认为我是原创吧!
 
简介星期制度
星期制度是一种古老的制度。据说因为《圣经×创世纪》中规定上帝用六天的时间创世纪,第七天休息,所以人们也就以七天为一个周期来安排自己的工作和生活,而星期天是休息日。
 
求星期几的思路
 
原点差值取余法
首先,先要知道在想算的日子之前的一个确定的日子是星期几,拿这一天作为推算的标准,也就是相当于一个计算的“原点”。其次,知道想算的日子和这个确定的日子之间相差了多少天,用7除以这个日期的差值,余数就是想算的日子的星期在确定的日子的星期之后的多少天。如果余数是0,就表示这两天的星期相同。显然,如果把这个作为“原点”的日子选为星期日,那么余数正好就是星期几。
 
“原点”选取
直接计算两个日期之间的天数,还是不免繁琐。比如1982年7月29日和2004年5月1日之间相隔794天,这不是一下子就能计算出来的,它包括了三个时间段:
1982年7月29日以后这一年的剩余天数
1983年-2003年这21个整年的全部天数
从2004年1月1日到2004年5月1日的经过的天数
为了简化日期的计算,可以考虑减少第一部分的天数计算,因此考虑可以把原点日期选取为公元前1年12月31日(ps:恰好这天就是周日)。这样的话,第一段时间为0,第二段时间的整年数为想计算日期所在的年数减一(ps:计算多少天注意考虑闰年)。第三段日期正常计算即可。
 
公历的置闰规则
公历的平年是365天,闰年是366天。置闰的方法是能被4整除的年份在2月加一天,但能被100整除的不闰,能被400整除的又闰。符合规则公式可以是(c语言描述):
[cpp] 
if( (year % 4 == 0  && year % 100 != 0) || )year % 400 == 0))  
 
第一个星期几计算公式
对于从公元前1年(或者说公元0年)12月31日到某一日子的年份Y之间的所有整年中的闰年数,就等于
 
[cpp]  
runnian = [(Y - 1) / 4] - [(Y - 1) / 100] + [(Y - 1) / 400]  
[]表示取整,第一项表示需要加上所有被4整除的年份数,第二项表示需要减掉被100整除的年份数,第三项表示需要再加上被400整除的年份数。
 
这样我们就得到了第一个计算某一天是星期几的公式 :
 
[cpp]  
w = (Y - 1) * 365 + [(Y - 1) / 4] - [(Y - 1) / 100] + [(Y - 1) / 400] + D  
其中D是这个日子在这一年中累积的天数。
 
简化计算公式
间隔天数w的作用仅仅是为了得到它除以7之后的余数,这就可以启发我们是不是可以简化这个w值,只要找一个和它余数相同的较小的数来代替,用数轮上的术语来说,就是找一个和它同余的较小的正整数,照样可以计算出准确的星期数。
显然,w这么大的原因是因为公式的第一项(Y - 1) * 365太大了,可以这样化减:
 
[html]  
(Y - 1) * 365 = (Y - 1) * (364 - 1)  
= (Y - 1) * (7 * 52 + 1)  
= 52 * (Y - 1) * 7 + (Y - 1)  
 
结果中的第一项是7的倍数,除以7余数为0,因此(Y - 1) * 365 可以被(Y - 1)代替。
简化后的计算星期几的公式为:
 
[cpp]  
w  = (Y - 1)+ [(Y - 1) / 4] - [(Y - 1) / 100] + [(Y - 1) / 400] + D  
 
九度acm求星期几
题目
[html]  
题目描述:  
We now use the Gregorian style of dating in Russia. The leap years are years with number divisible by 4 but not divisible by 100, or divisible by 400.  
For example, years 2004, 2180 and 2400 are leap. Years 2004, 2181 and 2300 are not leap.  
Your task is to write a program which will compute the day of week corresponding to a given date in the nearest past or in the future using today’s agreement about dating.  
输入:  
There is one single line contains the day number d, month name M and year number y(1000≤y≤3000). The month name is the corresponding English name starting from the capital letter.  
输出:  
Output a single line with the English name of the day of week corresponding to the date, starting from the capital letter. All other letters must be in lower case.  
样例输入:  
9 October 2001  
14 October 2001  
样例输出:  
Tuesday  
Sunday  
提示:  
Month and Week name in Input/Output:  
January, February, March, April, May, June, July, August, September, October, November, December  
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday  
 
ac代码(c语言)
[cpp] 
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
  
struct date  
{  
    int day;  
    char month[12];  
    int year;  
};  
  
int strToMonth(char *);  
void printWeek(int, int, int);  
  
  
int main()  
{  
    struct date today;  
    int count, m;  
  
    while(scanf("%d %s %d", &today.day, today.month, &today.year) != EOF)  
    {  
        m = strToMonth(today.month);  
        printWeek(today.day, m, today.year);  
    }  
  
    return 0;  
}  
  
void printWeek(int day, int month, int year)  
{  
    int number, week, i;  
    int m[12] = {31,28,31,30,31,30,31,31,30,31,30,31};  
    if((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))  
    {  
        m[1] = 29;  
    }  
  
    number = (year - 1)  + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;  
  
    for(i = 0; i < month - 1; i ++)  
    {  
        number += m[i];  
    }  
    number += day;  
  
    week = number % 7;  
  
    switch(week)  
    {  
        case 0:  
            printf("Sunday\n");  
            break;  
        case 1:  
            printf("Monday\n");  
            break;  
        case 2:  
            printf("Tuesday\n");  
            break;  
        case 3:  
            printf("Wednesday\n");  
            break;  
        case 4:  
            printf("Thursday\n");  
            break;  
        case 5:  
            printf("Friday\n");  
            break;  
        case 6:  
            printf("Saturday\n");  
            break;  
    }  
}  
  
int strToMonth(char *month)  
{  
    if(strcmp(month, "January") == 0)  
    {  
        return 1;  
    }else if(strcmp(month, "February") == 0)  
    {  
        return 2;  
    }else if(strcmp(month, "March") == 0)  
    {  
        return 3;  
    }else if(strcmp(month, "April") == 0)  
    {  
        return 4;  
    }else if(strcmp(month, "May") == 0)  
    {  
        return 5;  
    }else if(strcmp(month, "June") == 0)  
    {  
        return 6;  
    }else if(strcmp(month, "July") == 0)  
    {  
        return 7;  
    }else if(strcmp(month, "August") == 0)  
    {  
        return 8;  
    }else if(strcmp(month, "September") == 0)  
    {  
        return 9;  
    }else if(strcmp(month, "October") == 0)  
    {  
        return 10;  
    }else if(strcmp(month, "November") == 0)  
    {  
        return 11;  
    }else if(strcmp(month, "December") == 0)  
    {  
        return 12;  
    }  
}  
posted @ 2014-02-14 11:54  kin2321  阅读(464)  评论(0编辑  收藏  举报