第19题-数周日
本文包含了欧拉项目第19题的解法
问题描述
这个问题翻译为中文就是:
给定以下信息
1900年1月1日是周一
30天一个月的月份有4月、6月、9月、11月,除了2月外剩下的月份都是31天,单独说2月,如果是平年则28天,如果是闰年则29天
闰年是能被4整除但是不能被100整除,或者是能被400整除的年份
求解:
在1901年1月1日到2000年12月31日之间有几个当月第一天是周日的日子
解题思路
本题没有什么很难的地方,主要是实现一个简单的日历,然后从日历中查出符合条件的日期来就行。正经的日历都是根据各种天文知识来实现的,不过这个简单的日历可以根据题目中给定的第一条信息推算出来,从1900年1月1日起,可以往前往后推算出对应的日期
rust语言实现
p19.rs
use algorithms::misc::leap_year::is_leap_year;
use std::cmp::Ordering;
pub struct SimpleDate {
pub year: usize,
pub month: usize,
pub day: usize,
}
impl SimpleDate {
pub fn new(year: usize, month: usize, day: usize) -> Self {
SimpleDate { year, month, day }
}
pub fn to_string(&self) -> String {
return format!("{}-{}-{}", self.year, self.month, self.day);
}
/// get the day in week, Sunday -> 0, Monday -> 1 ... Sat -> 6
pub fn weekday(&self) -> usize {
let mut start_date = SimpleDate::new(1900, 1, 1);
let mut day_diff = 1; // this day is monday
loop {
if start_date >= *self {
break;
}
day_diff += 1;
start_date = start_date.next_day();
}
day_diff % 7 // sunday is 0
}
/// add a day to self
pub fn next_day(&mut self) -> SimpleDate {
if self.month == 12 {
if self.day == 31 {
// last day of the year, to a new year
SimpleDate::new(self.year + 1, 1, 1)
} else {
// just add one day
SimpleDate::new(self.year, self.month, self.day + 1)
}
} else if [4, 6, 9, 11].contains(&self.month) {
// 30-day months
if self.day == 30 {
SimpleDate::new(self.year, self.month + 1, 1)
} else {
SimpleDate::new(self.year, self.month, self.day + 1)
}
} else if [1, 3, 5, 7, 8, 10].contains(&self.month) {
if self.day == 31 {
SimpleDate::new(self.year, self.month + 1, 1)
} else {
SimpleDate::new(self.year, self.month, self.day + 1)
}
} else if self.month == 2 {
if is_leap_year(self.year) {
if self.day == 29 {
SimpleDate::new(self.year, self.month + 1, 1)
} else {
SimpleDate::new(self.year, self.month, self.day + 1)
}
} else if self.day == 28 {
SimpleDate::new(self.year, self.month + 1, 1)
} else {
SimpleDate::new(self.year, self.month, self.day + 1)
}
} else {
panic!("error here, invalid month");
}
}
}
impl PartialEq for SimpleDate {
fn eq(&self, other: &SimpleDate) -> bool {
self.year == other.year && self.month == other.month && self.day == other.day
}
}
impl PartialOrd for SimpleDate {
fn partial_cmp(&self, other: &SimpleDate) -> Option<Ordering> {
if self.year < other.year {
return Some(Ordering::Less);
} else if self.year == other.year {
if self.month < other.month {
return Some(Ordering::Less);
} else if self.month == other.month {
if self.day < other.day {
return Some(Ordering::Less);
} else if self.day == other.day {
return Some(Ordering::Equal);
}
return Some(Ordering::Greater);
}
return Some(Ordering::Greater);
}
Some(Ordering::Greater)
}
}
pub fn count_sundays(s: &SimpleDate, end_date: &SimpleDate) -> usize {
let mut start_date = SimpleDate::new(s.year, s.month, s.day);
let mut weekday = start_date.weekday();
let mut num = 0;
while start_date < *end_date {
if weekday == 0 && start_date.day == 1 {
num += 1;
}
start_date = start_date.next_day();
weekday += 1;
weekday %= 7;
}
num
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_next_day() {
let mut start_date = SimpleDate::new(1900, 1, 1);
start_date = start_date.next_day();
assert_eq!(true, SimpleDate::new(1900, 1, 2) == start_date);
assert_eq!(true, start_date < SimpleDate::new(1900, 1, 3));
}
#[test]
fn test_weekday() {
let mut start_date = SimpleDate::new(1900, 1, 1);
start_date = start_date.next_day();
start_date = start_date.next_day();
assert_eq!(3, start_date.weekday());
}
#[test]
fn test_count_days() {
assert_eq!(
2,
count_sundays(&SimpleDate::new(1900, 1, 1), &SimpleDate::new(1900, 12, 31))
); // 1900-4-1, 1900-7-1
assert_eq!(
171,
count_sundays(&SimpleDate::new(1901, 1, 1), &SimpleDate::new(2000, 12, 31))
);
}
}
代码解释
构造一个简单的日期结构体 SimpleDate,并且赋予一些简单的方法,next_day是用来得到下一天日期的,weekday用来返回当前日期对应星期几
通过实现PartialEq和PartialOrder来比较日期大小
循环遍历获取到目标日期
下面测试模块中包含了几个测试用例,分别测试对应的几个公共方法,通过保证每个方法是正确的,从而让整体程序保证正确这种方法叫做单元测试。最后面那个test_count_days方法要用已知的结果来验证程序正确性,叫做功能测试。
通过生成1900年的日历,我们发现1号是周日的日期有:1900-04-01, 1900-07-01,符合第一个断言预期的结果。所以根据第二个断言,我们可以很确定算出来的171就是问题中需要我们求的日子
目前的实现稍微有点繁琐,聪明的你能想出更简单的办法来吗?
Last updated