Leap Year
Bạn nhận được một năm year trong lịch Gregory. Trả về true nếu đó là năm nhuận, tức là năm có ngày 29 tháng 2, và false nếu không phải.
Một năm là năm nhuận khi chia hết cho 4, ngoại trừ các năm chia hết cho 100, những năm này không phải năm nhuận, trừ khi chúng cũng chia hết cho 400.
Hàm
- yearinteger
- năm cần kiểm tra
- Trả vềboolean
- true nếu năm đó có ngày 29 tháng 2, false nếu không
Ràng buộc
1 ≤ year ≤ 106
Ví dụ
- Đầu vào
- year = 2024
- Đầu ra
- true
- Giải thích
2024chia hết cho4và không phải là năm tròn thế kỷ, nên đó là năm nhuận.
- Đầu vào
- year = 1900
- Đầu ra
- false
- Giải thích
1900chia hết cho4, nhưng cũng chia hết cho100và không chia hết cho400, nên áp dụng ngoại lệ năm tròn thế kỷ: không phải năm nhuận.
- Đầu vào
- year = 2000
- Đầu ra
- true
- Giải thích
2000chia hết cho100, điều này sẽ khiến năm đó không phải năm nhuận, nhưng năm đó cũng chia hết cho400, nên lại trở thành năm nhuận. Đây là năm nhuận.
Gợi ý
Mở từng gợi ý một. Mỗi gợi ý tiết lộ thêm một chút.
Số dư
0sau khi chia cho4là quy tắc đầu tiên. Những năm nào vi phạm quy tắc này?Năm tròn thế kỷ làm mất hiệu lực quy tắc này, còn các bội số của
400lại làm mất hiệu lực quy tắc thế kỷ. Mọi bội số của400cũng là bội số của100và4, vì vậy thứ tự kiểm tra sẽ quyết định đáp án.Trước tiên, hãy kiểm tra khả năng chia hết cho
400, sau đó đến100, rồi đến4, và để quy tắc đầu tiên khớp quyết định kết quả. Hoặc kết hợp các quy tắc: chia hết cho 4 và không chia hết cho 100, hoặc chia hết cho 400.
Lời giải
Mỗi quy tắc cần tính một số dư, nên cần thực hiện ba phép kiểm tra tính chia hết. Điểm dễ mắc bẫy là thứ tự: với 2000, một năm thế kỷ đồng thời là bội số của 400, các quy tắc cho kết quả khác nhau. Hãy kiểm tra quy tắc cụ thể nhất trước, hoặc viết một biểu thức có dấu ngoặc để giữ nguyên các trường hợp ngoại lệ.
Kiểm tra các quy tắc theo thứ tự từ cụ thể nhất đến ít cụ thể nhất
Ý tưởng
Chia hết cho 4 thì năm đó là năm nhuận, chia hết cho 100 thì hủy điều đó, còn chia hết cho 400 thì hủy việc hủy đó. Mọi bội số của 400 đều là bội số của 100, và mọi bội số của 100 đều là bội số của 4, vì vậy quy tắc cụ thể nhất phù hợp sẽ quyết định đáp án.
Kiểm tra 400, rồi 100, sau đó 4, và trả về kết quả ngay khi gặp điều kiện phù hợp đầu tiên. 2000 dừng ở lần kiểm tra đầu tiên, 1900 ở lần thứ hai, 2024 ở lần thứ ba. Năm nào không khớp với điều kiện nào thì không chia hết cho 4, nên đó là năm thường.
Thuật toán
- Nếu
year % 400 == 0, trả vềtrue. - Nếu
year % 100 == 0, trả vềfalse. - Nếu
year % 4 == 0, trả vềtrue. - Nếu không, trả về
false.
def isLeapYear(year):
if year % 400 == 0:
return True
if year % 100 == 0:
return False
if year % 4 == 0:
return True
return FalseMột biểu thức logic
Ý tưởng
Đọc quy tắc theo hai cách để xác định năm nhuận: chia hết cho 4 và không phải năm tròn thế kỷ, hoặc chia hết cho 400. Câu đó trở thành (year % 4 == 0 and year % 100 != 0) or year % 400 == 0.
2024 thỏa mãn nửa đầu. 1900 và 2000 đều không thỏa mãn vì là năm tròn thế kỷ; chỉ 2000 thỏa mãn nửa sau, vì 1900 % 400 bằng 300. Nhóm điều kiện thành chia hết cho 4 và (không phải năm tròn thế kỷ hoặc là bội số của 400) sẽ cho cùng một kết quả với mọi năm.
Thuật toán
- Tính
year % 4 == 0 and year % 100 != 0: chia hết cho 4 và không phải năm tròn thế kỷ. - Tính
year % 400 == 0. - Trả về
truenếu một trong hai điều kiện đúng.
def isLeapYear(year):
# Every 4th year, except centuries, except every 400th year.
return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0
Lỗi thường gặp và trường hợp biên
- Chỉ kiểm tra
year % 4 == 0. Điều này đúng với mọi năm từ1901đến2099, nên lỗi này khó bị phát hiện, nhưng lại sai với1900và2100. - Kiểm tra
100trước400trong chuỗi câu lệnh if. Phép kiểm tra thế kỷ trả vềfalsevới2000trước khi phép kiểm tra400chạy. - Bỏ phần kiểm tra chia hết cho 4.
year % 100 != 0 or year % 400 == 0xem2023là năm nhuận vì2023không phải năm tròn thế kỷ. - Dùng
year / 4khi bạn muốn lấy số dư. Phép chia cho biết4chia được bao nhiêu lần; số dư cho biết phép chia có hết hay không.
Câu hỏi thường gặp3
Độ phức tạp thời gian của việc kiểm tra năm nhuận là bao nhiêu?
O(1) về thời gian và bộ nhớ: nhiều nhất là ba phép toán lấy số dư và một vài phép so sánh, bất kể năm nào.
Vì sao năm 1900 không phải năm nhuận nhưng năm 2000 lại là năm nhuận?
Cả hai đều là năm tròn thế kỷ, chia hết cho 100, nên thông thường không phải năm nhuận. 2000 cũng chia hết cho 400, và quy tắc đó được ưu tiên hơn quy tắc năm tròn thế kỷ. Khi chia 1900 cho 400, số dư là 300, nên đây vẫn là năm bình thường.
Tại sao lại có các quy tắc về thế kỷ?
Một năm dương lịch kéo dài khoảng 365.2422 ngày. Thêm một ngày nhuận sau mỗi 4 năm cho số ngày trung bình là 365.25, thừa khoảng 3 ngày sau mỗi 400 năm. Bỏ ngày nhuận ở 3 trong mỗi 4 năm tròn thế kỷ giúp loại bỏ số ngày thừa đó và đưa số ngày trung bình về 365.2425, nhờ vậy lịch luôn khớp với các mùa.
Bài tập tương tự
Các bài tập dùng cùng ý tưởng. Giải hai hoặc ba bài là bạn sẽ nhớ được mẫu này.
def isLeapYear(year):
# Viết code ở đâyKiểm thử #1
Kiểm thử #2
Kiểm thử #3
Đối số
year = 2024
Đầu ra mong đợi
true