给定日期(年月日)求星期几(蔡勒公式?没那么简单!)

4 篇文章 1 订阅

前言

前几日做到一个机试题,给出一个日期,让你输出那天是星期几,这种题无疑两种思路:一是从今天(前提是知道今天日期及周几)开始推算,计算今天与目标日期差的天数再取模运算,考虑到还要考虑闰年什么的,立即推->用下一种方法;直接利用一个什么公式来计算(问题是这样算起来容易,但公式不好记啊啊啊)。

蔡勒公式

w = ( C / 4 − 2 C + Y + Y / 4 + 13 ( M + 1 ) / 5 + D − 1 ) % 7 w =(C/4-2C+Y+Y/4+13(M+1)/5+D-1)\%7 w=(C/42C+Y+Y/4+13(M+1)/5+D1)%7

其中 / / / 为除法运算,结果取整(即商), % \% % 为取模运算; C C C 表示已过世纪数,即本世纪减一, Y Y Y 表示 世纪内的年份,即一般是年份的后两位数字 M M M 代表月份,如果 M < 2 M<2 M<2,则加12,并且年份减1(即将某年的1月、2月,看作上一年的13月、14月来带入计算,并且这一步要先于计算 C C C D D D 表示日。

w w w 为 0 时表示星期日,不为0时, w w w是几便是星期几。

话不多说,上代码(C++),代码输入输出格式是按照机试格式要求来的

input:
2
2020 4 8
2000 2 5
output:
3
6
#include <iostream>
#include <cmath>

using namespace std;
int cen(int y);

int main(void)
{
	int n;
	int y, m, d;
	while (cin >> n)
	{
		for (int i = 0; i < n; i++)
		{
			cin >> y >> m >> d;
			if (m < 3)
			{
				y--;
				m += 12;
			}	
			int y1 = y - cen(y)*100;

			int w = (y1 + y1/4 + cen(y)/4 - 2*cen(y) + 13*(m+1)/5 + d - 1) % 7;
			
			if (0 == w)
				w = 7;
			cout << w << endl;
		}
	}
	return 0;
}

int cen(int y)	// 已过世纪数
{
	return y/100;
}

这样好像测试的两个例子都正确,但是真的有这么简单?╮(╯_╰)╭
输入如下测例试试

2006 4 4
2100 12 2

结果

-5
-3

所以问题出在哪呢,enmmm,结果是通过蔡勒公式直接计算出来的,结果为负就说明蔡勒公式的被除数部分 ( C / 4 − 2 C + Y + Y / 4 + 13 ( M + 1 ) / 5 + D − 1 ) (C/4-2C+Y+Y/4+13(M+1)/5+D-1) (C/42C+Y+Y/4+13(M+1)/5+D1) 可能为负,然后就涉及到负数取模的问题,,关于负数取模,百度一下就会发现不同的语言实现方法并不相同!

  • C/C++/Java 采用了 truncate 除法。
    -7 % 3 = -7 - (-7) / 3 * 3= -7 - (-2) * 3 = -7 + 6 = -1
  • python 则采用floor 除法
    -7 % 3 = -7 - (-7) / 3 * 3= -7 - (-3) * 3 = -7 + 9 = 2

可以看出两种取模方法根本在于除法是采用向上取整还是向下取整。很明显蔡勒公式中的负数取模方式采用的是向下取整!
这与 C++ 的负数取模方式不同,所以对于负数取模,我们需要进一步处理;

代码更改如下:

#include <iostream>
#include <cmath>

using namespace std;
int cen(int y);	// 已过世纪数

int main(void)
{
	int n;
	int y, m, d;
	while (cin >> n)
	{
		for (int i = 0; i < n; i++)
		{
			cin >> y >> m >> d;
			if (m < 3)
			{
				y--;
				m += 12;
			}	
			int y1 = y - cen(y)*100;// or y1 = y % 100

			int w = (y1 + y1/4 + cen(y)/4 - 2*cen(y) + 13*(m+1)/5 + d - 1);

			if (w < 0)	// 负数取模
				w = 7 - (-w) % 7;
			else
				w = w % 7;
			
			if (0 == w)
				w = 7;

			cout << w << endl;
		}
	}
	return 0;
}

int cen(int y)	// 已过世纪数
{
	return y/100;
}

拓展

万年历(公历)

/* 输入日期(年月日),打印本月日历 **/
#include <iostream>
#include <iomanip>

using namespace std;
int week(const int y, const int m, const int d);	// 计算一号是周几
void printMon(const int y, const int m, const int d);	// 打印月历
bool isLeap(const int y);	// 判断闰年

int main(void)
{
	int y, m, d;
	cin >> y >> m >> d;
	printMon(y, m, d);

	return 0;
}

int week(const int y, const int m, const int d)	// 利用蔡勒公式求星期几
{
	int y1 = y;
	int m1 = m;
	int d1 = d;
	if (m1 < 3)
	{
		m1 += 12;
		y1--;
	}
	int cen = y1 / 100;
	int y2 = y1 % 100;

	int w = (y2 + y2/4 + cen/4 - 2*cen + 13*(m1+1)/5 + d1 - 1);

	if (w < 0)	// 负数取模
		w = 7 - (-w) % 7;
	else
		w = w % 7;
	
	if (0 == w)
		w = 7;
	return w;
}

void printMon(const int y, const int m, const int d)	// 打印月历
{
	int mon[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	int day = mon[m-1];
	int w = week(y, m, d);
	if (isLeap(y) && 2==m)
		day++;
	
	cout << "Sun  " << "Mon  " << "Tues  " << "Wed  " << "Thur " << "Fri  " << "Sat\n";
	if (7 == week(y, m, 1))
	{
		for (int i = 1; i <= day; i++)
		{
			cout << setiosflags(ios::right) << setw(3) << i << "  ";
			if (i % 7 == 0) // 周六后换行
				cout << endl;
		}
	}
	else
	{
		for (int i = 0; i < week(y, m, 1); i++)	// 控制一号前的距离
			cout << "     ";

		for (int i = 1; i <= day; i++)
		{
			cout << setiosflags(ios::right) << setw(3)  << i << "  ";
			if ((i+week(y, m, 1)) % 7 == 0)	// 周六后换行
				cout << endl;
		}
	}
	cout << endl;
}

bool isLeap(const int y)
{
	if (y%400==0 || (y%100!=0 && y%4==0))
		return true;
	else
		return false;
}

效果:
在这里插入图片描述

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值