我在Gist中找到了这段代码,它计算一个月中的最后一天(或者换句话说:一个月中的天数):
private fun lastDayInMonth(month: Int, year: Int): Int {
return if (month != 2) {
31 - (month - 1) % 7 % 2
} else {
if (year and 3 == 0 && (year % 25 != 0 || year and 15 == 0)) {
29
} else {
28
}
}
}
有人可以解释一下这是如何以及为什么有效的吗?这是哪种算法,有名字或知名作者吗?
在看代码之前,首先让我们看看规则是什么。它们分为两部分:
忽略二月,月份 (a) 31 天和 30 天交替,持续六个月,然后 (b) 31 天和 30 天交替,持续五个月。
要确定二月,我们需要知道它是否是闰年。闰年的规则是1:
能被四整除的每一年都是闰年,能被100整除的年份除外,但如果能被400整除,这些百年年就是闰年。
这分为三部分:如果能被 4 整除,一年就是闰年,但如果能被 100 整除则不是闰年,但如果能被 400 整除就是闰年。
代码遵循类似的方法。
首先,代码计算出非二月中的天数。它使用表达式
(month - 1) % 7 % 2
,在0和1之间振荡,在适当的月份调整31到30天。 % 2
进行定期交替,并 % 7
在 8 月及以后“重置”((b) 部分)。
对于闰年,代码遵循相同的三部分包含/排除方法。它通过以下方式做到这一点:
通过使用按位 AND 检查是否
year and 3 == 0
来计算年份是否可以被 4 整除。要了解这一点,请观察当且仅当最后两个二进制数字中包含 00 时,年份才能被 4 整除,并且这些数字是唯一在按位 AND 与 3 进行比较时给出 0 的数字。
如果检查年份是否能被 25 整除,则计算年份是否能被 100 整除。这必须与我们从 1. 知道年份能被 4 整除的情况相同,如果这部分要改变最终结果,并且能被 4 整除的数字,当且仅当能被 100 整除的数字。
通过检查年份是否能被 16 整除来确定年份是否能被 400 整除(使用类似于 1. 的按位 AND 方法)。如果这是真的,并且它将改变表达式的结果,那么年份也一定能被 25 整除。这意味着这相当于计算出年份是否能被 400 整除,因为数字能被 400 整除当且仅当它能同时被 25 和 16 整除(观察 25 x 16 = 400)。
所以,计算已经相当简洁地表达了月日计算。尽管使用现代编译器,我怀疑按位运算是否比模运算更快。
这是哪种算法,有名字或知名作者吗?
它并不是真正的算法,而是以简洁的方式用代码表达众所周知的规则。