原本是打算写一篇关于蔡勒公式等算法的文章,写着写着突然发现这篇内容可能并不能称之为“算法”,我以为,正规意义上的“算法”应该是研究数据结构二叉树之类的…但是《 百度百科.词条. 算法 》这样说:

算法(Algorithm) 是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用 系统的方法 描述解决问题的策略机制。也就是说,能够对 一定规范的输入,在有限时间内获得 所要求的输出

看完这段话我感觉之前那篇《[萌新算法] 公约数 & 公倍数》和本篇应该也符合这些描述,公式即为 系统的方法,输入的数字是 一定规范的输入,得到的结果是 所要求的输出。那就姑且把它们叫做“算法”,以后再慢慢摸索那种高深的算法?

欢迎查看整理完整的新篇《炒冷饭之日历程序

 

问题引入

C语言课程的期末大作业,要求完成一个多种功能具有一定实用意义的 Project 。供选八道选题中的一题:

题目1:日历问题
功能要求:
主函数提供功能菜单供用户选择,用户可以选择调用以下各个功能,也可以选择继续或退出程序。系统应提供以下功能:

  • 计算未来天数和星期:输入未来某天的日期,输出距今天还有多少天?是星期几?
  • 打印年历:输入一个年份,将该年的日历按下面图示格式输出到文件 cal.txt 中。要求每行同时打印3个月。每月1号是星期几可以利用 蔡勒公式 计算,百度下该公式。
  • 打印月历:输入年月,输出该月的日历,可以参考年历中月份的格式。

日历示例
日历示例

未来可能像 九首猫 同学一样在博客写作业了哈哈哈

滑稽
滑稽

这篇文章就来 百度百度计算星期的那些公式 学习推导这些公式…

算法

在开始计算之前,我们可能还需要了解一点关于历法的知识 233 :

  • 公历对日期的删减 参考百度百科 公元 词条

罗马教皇格里高利十三世在 1582 年组织了一批天文学家,根据哥白尼日心说计算出来的数据,对儒略历作了修改。将 1582 年 10 月 5 日到 14 日之间的 10 天宣布撤销 (到 1582 年时,儒略历的春分日(3 月 21 日)与地球公转到春分点的实际时间已相差 10 天。因此,格里历开始实行时,将儒略历 1582 年 10 月 4 日星期四的次日,为格里历 1582 年 10 月 15 日星期五,即有 10 天被删除,但原星期的周期保持不变),继 10 月 4 日之后为 10 月 15 日。后来人们将这一新的历法称为“格里高利历”,也就是今天世界上所通用的历法,简称格里历或公历。

地球绕太阳运行周期为 365 天 5 时 48 分 46 秒(合 365.24219 天)即一回归年(tropical year)。公历的平年只有 365 日,比回归年短约 0.2422 日,所余下的时间约为每四年累计一天,故第四年于 2 月末加 1 天,使当年的历年长度为 366 日,这一年就为闰年。现行公历中每 400 年有 97 个闰年。按照每四年一个闰年计算,平均每年就要多算出 0.0078 天,这样经过四百年就会多算出大约 3 天来。因此每四百年中要减少三个闰年。所以公历规定:年份是整百数时,必须是 400 的倍数才是闰年;不是 400 的倍数的世纪年,即使是 4 的倍数也不是闰年。这就是通常所说的:四年一闰,百年不闰,四百年再闰。 例如,2000 年是闰年,2100 年则是平年。

蔡勒公式

蔡勒公式(Zeller's Fulcrum)最早由德国牧师/数学家克里斯蒂安·蔡勒(Christian Zeller, 1822-1899)在 1886 年推导出。

// 1582 年 10 月 4 日前:
Week = ([Century/4] - 2*Century + Year + [Year/4] + [13*(Month+1)/5] + Day - 1)%7
// 1582 年 10 月 4 日后:
Week = ([Century/4] - 2*Century + Year + [Year/4] + [13*(Month+1)/5] + Day + 2

推导过程

占位待编辑...



基姆拉尔森公式

百度了一个也是用来算星期的公式,不过每年的 1 2 月要当作上一年的 13 14 月计算。

Week = (Day + 2*Month + 3*(Month+1)/5 + Year + Year/4 - Year/100 + Year/400) % 7

代码

用刚学的热乎的 C 语言实现,数组和结构体都没有用到,指针也只用到了很基础的用法。但是能用就行,管他那么多呢…

2020.2 更新了新的文章,请参考开篇提醒中的新文章!

#include <stdio.h>

// 定义全局变量供自定义函数处理 
int year0,month0,day0,year,month,day;
 
// 将部分功能提取作为 API 使用 
int dateAPI (int togetY, int togetM, int togetD, int type) {
    int week;
    switch (type) {
        case 1:
            // 判断是否闰年,返回指定年份总天数 
        case 2:
            // 判断月份,返回指定月份总天数
        case 3:
            // 基于 基姆拉尔森公式 修改,返回指定日期星期数
            // 这里周一至周日依次返回 1 ~ 7 ...
    }
}
 
void calDate () {
    // 计算天数
}

void printY () {
    // 打印年历
}

void printM () {
    // 打印月历
}

int main () {
    char act;
    printf("\n\n    * 使用本程序前请先输入今天的日期!格式:2019 1 1\n\n");
    scanf("%d%d%d",&year0,&month0,&day0);
    printf("\n    今天是%d年%d月%d日,欢迎使用日历服务。\n    * 请输入对应序号选择您需要的服务\n        1.计算未来天数和星期\n        2.打印年历\n        3.打印月历\n    * 按 q(uit) 可退出程序。\n\n",year0,month0,day0);
    while (scanf("%c",&act) == 1) {
        // switch 选择服务进入下一步函数,执行完毕后仍可继续选择 
        switch (act) {
            case '1':
                calDate();
                printf("\n    * 功能1执行完毕!请继续选择下一步操作:\n\n");
                break;
            case '2':
                printY();
                printf("\n    * 功能2执行完毕!请继续选择下一步操作:\n\n");
                break;
            case '3':
                printM();
                printf("\n    * 功能3执行完毕!请继续选择下一步操作:\n\n");
                break;
            case 'q':
                printf("\n    * 感谢您的使用~\n");
                return 0;
        }
    }
}

看看效果:

这里的图片找不到了呢

更多

关于当前日期

毕竟是个懒人,题目中要求的“今日”我采用了用户手动输入的方式定义,而通过 C 语言的 <time.h> 库可以实现对当前系统时间的直接获取。当时赶着交作业没仔细看,现在看了下后发现引用也很简单,如果当时写进作业代码里可能会有一点点加分吧。不想学习,请看后面压轴资料讲的比较详细,虽然我也没大看懂指针结构体这块,但是下面这个例子能用来获取当前年月日…能用就行……

#include <time.h>
int main() {
    time_t timep;               // 定义 time_t 类型的变量 timep 用于执行 time() 函数
    struct tm *p;               // 以 tm 结构返回的时间信息指针 p
    time(&timep);               // 获取机器日历时间
    p = gmtime(&timep);         // 返回 GMT 世界标准时间
    int yn = 1900 + p->tm_year; // 实际调试并不是用 1970 加,暂时不清楚是怎么用的
    int mn = 1 + p->tm_mon;
    int dn = 1 + p->tm_yday;
    
    printf("系统时间为:%d年%d月%d日\n",yn,mn,dn);
}

// 关于 tm 结构,从标准计时点 1970 年 1 月 1 日起累计
struct tm{
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
};

学习资料

感谢以下资料的详细讲解,本文表达模糊或错误的地方请参考以下资料学习:

一篇详细介绍公式推导的博文,and 你也可以使用这篇忘记加原文链接的转载:《[转载]蔡勒(Zeller)公式——计算任意一天是星期几 - sgao - 博客园嘿,他的这篇文章《先天八卦向后天八卦演进逻辑猜想》也非常有意思呢2333

一篇简短的更关注阐明如何作用蔡勒公式的文章

作为拓展阅读,你可以在这篇文章寻找一些关于历法的史话故事