星期數(shù)計(jì)算算法(一) - Sakamoto算法

內(nèi)容同步于我的博客:https://blog.bigrats.net/archives/sakamoto-algorithm.html

算法代碼

int dayofweek(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;
}

原理及推導(dǎo)

首先,1年1月1日為星期w0,由于365 = 527 + 1,366 = 527+2,因此每過一個(gè)平年,對(duì)應(yīng)日期的星期數(shù)便加1,每過一個(gè)閏年,3月前對(duì)應(yīng)日期的星期號(hào)遞增1,3月開始對(duì)應(yīng)日期的星期號(hào)遞增2。

一、假設(shè)我們計(jì)算y年1月1日的星期數(shù)

(1)我們首先計(jì)算一共有多少個(gè)閏年:因?yàn)槊克哪暧幸粋€(gè)閏年,同時(shí)考慮到能被100整除但不能被400整除的年份不是閏年,因此從1年到y(tǒng)年一共有y/4 - y/100 + y/400個(gè)閏年

(2)因此我們可以得到y(tǒng)年1月1日的星期數(shù)計(jì)算方法:(其中H為一個(gè)修正常數(shù))

if(isLeap(y)){
    w = (y + y/4 - y/100 + y/400 - 1 + H) % 7;
    // 因?yàn)?月1日在3月前,因此當(dāng)y為閏年時(shí),計(jì)算的天數(shù)增值多了1,我們把它減去
else {
    w = (y + y/4 - y/100 + y/400 + H) % 7;
}

二、接下來我們來計(jì)算ymd日的星期數(shù)

(1)由于我們已經(jīng)得到了y年1月1日的星期數(shù),我們只需算出md日相對(duì)于1月1日的天數(shù)增值即可計(jì)算的ymd日的星期數(shù)。

對(duì)于平年:deltaDays_noleap[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}

對(duì)于閏年:deltaDays_leap[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}

模7取余數(shù)后為:

deltaWofMonth_noleap[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
deltaWofMonth_leap[12] = {0, 3, 4, 0, 2, 5, 7, 3, 6, 1, 4, 6};

(2)從而我們得到了ymd日的星期數(shù)計(jì)算方法:

if(isLeap(y)){
    w = (y + y/4 - y/100 + y/400 - 1 + deltaWofMonth_leap[m - 1] + d + H) % 7;
else {
    w = (y + y/4 - y/100 + y/400 + deltaWofMonth_noleap[m - 1] + d + H) % 7;
}

(3)我們發(fā)現(xiàn),在3月以后,實(shí)際有:deltaWofMonth_noleap[i] % 7 == (deltaWofMonth_leap[i] - 1) % 7

因此我們可以僅采用平年的deltaWofMonth_noleap,得到計(jì)算方法如下:

if(isLeap(y)){
    if(m < 3){
        w = (y + y/4 - y/100 + y/400 - 1 + deltaWofMonth_noleap[m - 1] + d + H) % 7;
    } else {
        w = (y + y/4 - y/100 + y/400  + deltaWofMonth_noleap[m - 1] + d + H) % 7;
    }
else {
    w = (y + y/4 - y/100 + y/400 + deltaWofMonth_noleap[m - 1] + d + H) % 7;
}

可以合并簡(jiǎn)化為:

if(m < 3 && isLeap(y)) {
    w = (y + y/4 - y/100 + y/400 - 1 + deltaWofMonth_noleap[m - 1] + d + H) % 7;
}else {
    w = (y + y/4 - y/100 + y/400  + deltaWofMonth_noleap[m - 1] + d + H) % 7;
}

(4)現(xiàn)在來進(jìn)一步簡(jiǎn)化算法,當(dāng)m < 3時(shí),令y = y – 1,則有:當(dāng)y為閏年時(shí),y + y/4 - y/100 + y/400減少了2,當(dāng)y為平年時(shí),y + y/4 - y/100 + y/400減少了1。因此,算法可以簡(jiǎn)化為:

if(m < 3) {
    y--;
    w = (y + y/4 - y/100 + y/400 + 1 + deltaWofMonth_noleap[m - 1] + d + H) % 7;
} else {
    w = (y + y/4 - y/100 + y/400 + deltaWofMonth_noleap[m - 1] + d + H) % 7;
}

修改deltaWofMonth,其中,1月和2月不變,將3月之后的所有月的數(shù)值減1后模7,得到:

deltaW = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

從而代碼簡(jiǎn)化為:

y -= (m < 3);
w = (y + y/4 - y/100 + y/400 + deltaW[m - 1] + d + H + 1) % 7;

(5)經(jīng)過調(diào)試,得到修正常數(shù)H = 6,因此得到算法如下:

int dayofweek(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;
}

三、一些說明

由于目前使用的歷法并不是從1年1月1日開始使用的,而是從1582年年底才開始使用的格利戈里歷,并且在1582年不存在10月5日到10月14日這10天,因此此算法只能準(zhǔn)確計(jì)算1582年10月15日開始之后的星期數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 三位數(shù)除以一位數(shù),商可能是兩位數(shù),也可能是三位數(shù)。(百位夠除時(shí)商是三位數(shù),百位不夠除時(shí)是兩位數(shù)。) 464÷4...
    七月小七閱讀 1,656評(píng)論 0 0
  • 日歷的實(shí)現(xiàn)原理 這一篇只說明日歷實(shí)現(xiàn)的算法原理 首先我們想一下日歷的樣子 。在每一個(gè)月中都是28~31天 。每個(gè)月...
    蘇永茂閱讀 3,274評(píng)論 0 10
  • 如果不注意,大概很多人認(rèn)為“閏月”與“閏年”是一個(gè)意思,其實(shí)不是,雖說只是一字之差,所包含的意思卻相差很遠(yuǎn)。 “閏...
    雨落未驚風(fēng)閱讀 13,516評(píng)論 1 2
  • 最近得了一種一矯情就不舒服的病,新建了無數(shù)個(gè)Microsoft Word文檔,敲上三兩行字,然后呆住很久,然后像個(gè)...
    愛唱歌的十月閱讀 372評(píng)論 0 2
  • 一、整體產(chǎn)品概念 企業(yè)的營(yíng)銷人員對(duì)顧客的許諾(充滿誘惑的價(jià)值主張)與交付給顧客的產(chǎn)品著正具備的性能之間往往存在著差...
    阿東咚咚咚閱讀 1,220評(píng)論 1 2

友情鏈接更多精彩內(nèi)容