天天看點

關于計算兩日期之間經過多少天的超巧妙算法

首先聲明:本文引自一部落客原創部落格

原創位址:https://blog.csdn.net/chinaeran/article/details/43601699

昨天呢,剛剛閱讀了這個代碼,大部分都還可以看懂,有一兩個地方屬實難懂,但細細思來,方知部落客此代碼超神奇。簡直巧妙至極。

是以來細細解析一下此代碼。

話不多說。我們先來看一下原文。

#include <stdio.h>

#include <stdlib.h>

int day_diff(int year_start, int month_start, int day_start

, int year_end, int month_end, int day_end)

{

int y2, m2, d2;

int y1, m1, d1;

m1 = (month_start + 9) % 12;

y1 = year_start - m1/10;

d1 = 365*y1 + y1/4 - y1/100 + y1/400 + (m1*306 + 5)/10 + (day_start - 1);

m2 = (month_end + 9) % 12;

y2 = year_end - m2/10;

d2 = 365*y2 + y2/4 - y2/100 + y2/400 + (m2*306 + 5)/10 + (day_end - 1);

return (d2 - d1);

}

int main(void)

printf("%d\n", day_diff(2015, 1, 1, 2015, 1, 8));

printf("%d\n", day_diff(2015, 1, 29, 2015, 2, 9));

return 0;

這裡呢是原文代碼。

可以看到非常簡短,接下來,就讓你真正見識到這個代碼的巧妙之處。

接下來我們先來看一下樓主的解析

算法解析:

該算法總體思想是計算給定日期到 0年3月1日的天數,然後相減,擷取天數的間隔。

m1 = (month_start + 9) % 12; 用于判斷日期是否大于3月(2月是判斷閏年的辨別),還用于紀錄到3月的間隔月數。

y1 = year_start - m1/10; 如果是1月和2月,則不包括目前年(因為是計算到0年3月1日的天數)。

    其中 365*y1 是不算閏年多出那一天的天數,

    y1/4 - y1/100 + y1/400  是加所有閏年多出的那一天,

(m2*306 + 5)/10 用于計算到目前月到3月1日間的天數,306=365-31-28(1月和2月),5是全年中不是31天月份的個數

(day_start - 1) 用于計算目前日到1日的間隔天數

————————————————

我們由簡到繁,慢慢分析:

其中關于最後的  (day_start - 1)就不用多說了吧。

我們主要來講講沒别的地方。

先來看這裡:

d1 = 365*y1 + y1/4 - y1/100 + y1/400

本文思路在這在說一下:即計算每個日期距離0年三月一日的相差天數,在做差即得兩日期之間天數。

好了言歸正傳所謂閏年四年一閏,百年不閏,四百又多一閏。

我們從0年3月一日開始算到我們輸入哪一年的3月一日截止先計算整年的天數總和。

0年的二月已過。是以考慮從1年到截止年的閏年個數。

用y1/4算出可被4整除的年數,我們記為疑是閏年年數。後我們減去y1/100,去掉其中的不是閏年年數,但是由于四百年又多一閏是以我們又加上了y1/400;

這樣就可以完美的算出所經過的年的總天數,關于這裡的年數還涉及後面的(- m1/10)一項,這個我們後面來細細解答,這裡是本文最巧妙的地方之一,,先留個懸念哈。

好了我們接着往下看。

這個就比較巧妙了,需要與

 (m1*306 + 5)/10和(- m1/10)兩項結合來分析。這裡就到了作者算法最為精妙的部分了,不得不說這個代碼真的神奇。

OK,繼續。來看(- m1/10),和m1 = (month_start + 9) % 12;的結合。

先來看,怎麼樣才能讓餘數大于10呢,1,2,這兩個結果對吧。如果是m1大于10 的話呢,可知年數會減一。

簡單點來說呢就是用計算年總天數的計算到當年3月一号的天數總和,

如果當年不到三月,即1,2月,我們就往前推一年,計算到上一年的3月一号的天數,

至于多出來的天數我們接着看,别急。

縣來看如果是1,n那麼結果就是10,

即為一月份,意思是距離上一年的3月一号過去了10個月,對吧,

即上一年的3,4,5,6,7,8,9,10,11,12

其中一共有多少天呢,其中3.5.7.8.10.11位31天每月。

即結果為30*10+6

帶入 (m1*306 + 5)/10即得真的為306;是不是很神奇。

我們将這個公式一一帶入算一下。

下面呢 ,我們先看m1.再看月份,再看天數。

2,,,,,餘11,4,5,6,7,8,9,10,11,12,1對吧,帶入剛好為337;

3,,,,,餘00

4,,,,,餘13帶入得31

5,,,,,餘二3,4帶入61;

6,,,,,餘三3,4,5帶入92

7,,,,,餘四3,4,5,6帶入122

8.,,,,,餘五3,4,5,6,7帶入153

9,,,,,餘六,3,4,5,6,7,8帶入184

10,,,,,餘七,3,4,5,6,7,8,9滴入214

11,,,,,餘8,3,4,5,6,7,8,9,10帶入245

12,,,,,餘9,,3,4,5,6,7,8,9,10,11帶入275

OK,是不是發現都對,是不是很神奇。

我也這樣覺得,我來為大家分析其玄妙之處

先來分析月份問題。

随餘數的增加月份為3  4  5  6  7  8  9  10  11  12  1  

先來看為31天的月份有,3  5  7  8  9 10   12   1

看一下其中大部分30,31天的月份間隔,對吧,

簡單看一下即可看出由于306後面的6的原因及後面5的作用下

我們把它稍微化簡一下化為(m1*300+5+m1*6)/10

m1*300的我們直接濾掉

(5+6*m1)/10;

可以看到

1得1

2得1

3得2

最容易的得知的就是每加二回多一天即上面的初步規律30,31間隔出現

其中有兩個特例,7到8,12到2

包含7,和7,8月份的餘數分别為5,6

可以得出符合,

再看1到12你會發現一樣符合,

到底為什麼呢

看6

多出來的6是最最中心的地方

月份每加一,在其原來的31,30緊挨着變化的情況下會多出來一個1

而當月份為5,10時,會湊乘5,而後再過一個月,按30,31來看為 偶數,是30天,不過由于6多出來的五和多一月多出來的6結合,又會造就一個31天,而且和前一個31天還是挨着的

是以,7月到8月的特例就包含了。

再看12月到1月的情況12月時餘數為10,一月為11.

是不是又湊成一個5,後面一加有多出來一天。

OK,本次解析到此結束,

慢慢領悟此代碼的玄妙之處吧

再見。嘿嘿

再次聲明

代碼不是我的

轉載自https://blog.csdn.net/chinaeran/article/details/43601699

個人作品,

如有錯誤,請指出;

如要轉載,請注明出處。

三克油。。

繼續閱讀