1. 程式人生 > 其它 >儒略曆解題報告

儒略曆解題報告

簡單總結

儒略日

從上次 csp 考場上下來之後,就一直對這題很有心理陰影。

然後想著今天補一補這個題,那麼一些經驗就是,不要太害怕他,不要總想著如何快速推一些東西,靜下心來一點點看,然後多定義一些打表的陣列,這個資料萬一用到了呢?多定義一些簡單函式,可以極大簡化程式碼內部的細節特判,全部扔到函式裡面判斷。

首先我們考慮把格里高利曆和儒略曆劃分開來,因為這個分界線是非常的明顯的,而且分界線前後計算閏年的方式不同。

這個時候不要對自己的手算過於自信,當然,這個手算很容易,但是作為一個信競生,也許我們應該更好的利用手邊的工具,況且,前期的一些暴力打表和小函式,對於這類大模擬是很有用的。

那麼我們首先把一些基礎的定義出來,比如說如何判斷閏年:

inline int gr(int x) {
    if (!(x % 400)) return 1;
    if ((x % 100) && !(x % 4)) return 1;
    return 0;
}

inline int rr(int x) {
    if (!(x % 4)) return 1;
    return 0;
}

中英混合命名,第一個是 \(g\) 格里高利 \(r\) (run) 年,第二個同理。

然後再把月份有多少天定義出來:

for (int i = 1; i <= 12; ++i) {
    if (i == 2) d[i] = 28;
    else if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) d[i] = 31;
    else d[i] = 30;
}

這個時候我們就可以很快的求出儒略曆到底多少天了:

int sd = 0;
for (int i = -4712; i <= 1581; ++i) {
    sd += rr(i) ? 366 : 365;
}
for (int i = 1; i <= 9; ++i) {
    sd += d[i];
}

sd += 3;

暴力列舉整年並處理,暴力列舉整月並處理,最後處理多餘的天,之所以這裡 \(+=3\),是因為我們是按照 \(day\) 來判斷的,而 \(day\) 表示的是往後走了多少天,所以這裡不包括最後一天因為最後一天不能往後走了。

那麼對於詢問給出的 \(day\)

如果 if (day > 2299160) 就可以判斷會到達格里高利曆,否則不會。

好了,那麼接下來我們知道會走到什麼歷了,那麼對於某個歷怎麼計算呢?

注意到儒略曆四年是一個週期,而格里高利曆四百年是一個週期,於是我們可以打出一個欽定第一年為閏年的 400 年的表,具體來說,我們打出 \(ny[k], nm[k], nd[k]\) 表示從某個閏年 \(1\)\(1\) 日開始走,會走到 400 年內的哪一年哪一月哪一日,這個處理也不難。

for (int i = 0, k = 0; i < 400; ++i) {
    for (int j = 1; j <= 12; ++j) {
        int dd = d[j];
        if (j == 2 && gr(i)) ++dd;
        for (int ds = 1; ds <= dd; ++ds) {
            ny[k] = i, nm[k] = j, nm[k] = ds;
            ++k;
        }
    }
}

列舉年月日,搞一個變數記錄一下多少天了就行。

於是接下來就可以週期處理:

while (q--) {
    i64 day;
    std::cin >> day;
    int t = 0;
    if (day > 2299160) { // g 歷
        day -= 2299161;
        day += 139810;
        // 手動修正到從 1200-1-1 開始走
        // 因為我們的表是要求從某個閏年開始,特殊的,格里高利欽定是必須為 400 倍數的某個閏年,畢竟你看那個打表方式就是欽定這是第一個 400 閏年,後面都不是
        t = 1200 + (day / 146097) * 400;
        // 從 1200 開始走,看有多少個 400 年

        day %= 146097; // 400 年 400 年的幹掉
    }
    else { // r 歷
        t = -4712 + (day / 1461) * 4; 
        // 4 年 4 年的幹掉
        day %= 1461;
    }
    if (t + ny[day] > 0) { // 如果年份 + 剩餘天數走的年份是 DC
        std::cout << nd[day] << ' ' << nm[day] << ' ' << t + ny[day] << '\n';
    }
    else { // 否則是 BC 捏
        std::cout << nd[day] << ' ' << nm[day] << ' ' << -(t + ny[day] - 1) << ' ' << "BC" << '\n';
    }
}

至於那個修正是怎麼求的,第一步是先幹到格里高利第一天,然後往前修正,這個往前修正需要修正多少天也好求:

int sd = 0;
for (int i = 1200; i <= 1581; ++i) 
    sd += gr(i) ? 366 : 365;
for (int i = 1; i < 10; ++i) 
    sd += d[i];
sd += 14;
std::cout << sd << '\n';

那麼整體的程式碼如下:

#include <bits/stdc++.h>

using i64 = long long;
int d[13];

inline int gr(int x) {
    if (!(x % 400)) return 1;
    if ((x % 100) && !(x % 4)) return 1;
    return 0;
}

inline int rr(int x) {
    if (!(x % 4)) return 1;
    return 0;
}

int ny[146097], nm[146097], nd[146097];

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    for (int i = 1; i <= 12; ++i) {
        if (i == 2) d[i] = 28;
        else if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) d[i] = 31;
        else d[i] = 30;
    }

    for (int i = 0, k = 0; i < 400; ++i) {
        for (int j = 1; j <= 12; ++j) {
            int dd = d[j];
            if (j == 2 && gr(i)) ++dd;
            for (int ds = 1; ds <= dd; ++ds) {
                ny[k] = i, nm[k] = j, nd[k] = ds;
                ++k;
            }
        }
    }

    int q;
    std::cin >> q;

    while (q--) {
        i64 day;
        std::cin >> day;
        int t = 0;
        if (day > 2299160) { // g
            day -= 2299161;
            day += 139810;
            t = 1200 + (day / 146097) * 400;

            day %= 146097;
        }
        else { // r
            t = -4712 + (day / 1461) * 4;
            day %= 1461;
        }
        if (t + ny[day] > 0) {
            std::cout << nd[day] << ' ' << nm[day] << ' ' << t + ny[day] << '\n';
        }
        else {
            std::cout << nd[day] << ' ' << nm[day] << ' ' << -(t + ny[day] - 1) << ' ' << "BC" << '\n';
        }
    }
}
我不想就這樣淪陷,迷失在黑夜,我將燃燒這生命,就算再壯烈。