判断某年是否为闰年

Calculating if a year is a leap year

在 HackerRank 上,我试图解决一个闰年挑战,当我提交代码时,它通过了五个测试用例但失败了一个:当它试图检查 1992 年是否为闰年时。如果有人可以帮助我,我将不胜感激。下面是问题:

An extra day is added to the calendar almost every four years as February 29, and the day is called a leap day. It corrects the calendar for the fact that our planet takes approximately 365.25 days to orbit the sun. A leap year contains a leap day.

In the Gregorian calendar, three conditions are used to identify leap years:

The year can be evenly divided by 4, is a leap year, unless:

The year can be evenly divided by 100, it is NOT a leap year, unless:

The year is also evenly divisible by 400. Then it is a leap year. This means that in the Gregorian calendar, the years 2000 and 2400 are leap years, while 1800, 1900, 2100, 2200, 2300 and 2500 are NOT leap years. Source

任务

Given a year, determine whether it is a leap year. If it is a leap year, return the Boolean True, otherwise return False.

Note that the code stub provided reads from STDIN and passes arguments to the is_leap function. It is only necessary to complete the is_leap function.

我对此的回答是:

def is_leap(year):
    leap = False
    if year%4==0 and (year%100==0) and year%400==0:
        leap=True
    elif not(year%4==0) and not(year%100==0) and not(year%400==0):
        leap=False
    else:
        leap=False
        
    return leap

year = int(input())

Link 到问题 -> https://ibb.co/R0tGvnf

您现在的代码设置方式完全忽略了年份是否可以被四整除。只有同时被100和400整除才会考虑。

def is_leap(year):
    leap = False
    # this line asks if it is divisible by 400, 4, and 100 at the same time
    if year%4==0 and (year%100==0) and year%400==0:
        leap=True
    # this line asks if its not divisible by 400, 4 and 100 at the same time
    elif not(year%4==0) and not(year%100==0) and not(year%400==0):
        leap=False
    
    # you never check the case when it is divisible by 4 and not 
    # divisible by 100, and 400.  Also skipped is if it is divisible by 
    # 4 and 100, but not 400
    else:
        leap=False
        
    return leap

year = int(input())

试试这个方法

def is_leap(year):
    if year%4==0:
        if year%100==0:
            if year%400==0:
                return True
            return False
        return True
    return False

解决方案 A

规范中的措辞是做这件事,它说检查此条件以了解是否是闰年。然后它说我可以改变主意,如果...但是我可以再次改变主意,如果...

我们可以在代码中做同样的事情。如果是闰年我们就存,能被100整除就改,能被400整除就再改。

def is_leap(year):
    leap = False

    # The year can be evenly divided by 4, is a leap year
    if year%4 == 0:
        leap = True

    # Unless the year can be evenly divided by 100
    if year%4 == 0 and year%100 == 0:
        leap = False

    # Unless the year is also evenly divisible by 400
    if year%4 == 0 and year%100 == 0 and year%400 == 0:
        leap True

    return leap

解决方案 B

当您在代码中看到这种类型的模式时——其中每个条件都包含先前的条件,您可能想知道是否有一种方法可以重构它,而且确实存在。我们可以通过两种不同的方式去除多余的重复。我们可以像@alexpdev 那样处理嵌套的 if 语句,或者我们可以将所有内容组合成一个巨大的 if 语句,看起来像这样:

def is_leap(year):
    if (
        # The year can be evenly divided by 4, is a leap year
        <logic would go here>

        # Unless the year can be evenly divided by 100
        and not <logic would go here>

        # Unless the year is also evenly divisible by 400
        and not <logic would go here>
    ):
        return True
    return False

这里是 a discussion on which method is better. Some people don't like combining everything into a single if statement when there are a lot of conditions involved, but I don't mind doing so as long as it's tactfully done (like the way Deestan 这个问题中的提及,通常伴随着解释复杂逻辑的注释)。稍后我会回来使用这个方法,但首先我想看看我们是否可以像这样把所有东西放在一起

解决方案 C

    # doesn't work
    if year%4 == 0:
        leap = True
    elif year%4 == 0 and year%100 == 0:
        leap = False
    elif year%4 == 0 and year%100 == 0 and year%400 == 0:
        leap True

我觉得我们应该能够使用 if/elif 链,这也是你想要做的,但我们最终遇到了麻烦,因为如果 year%4 == 0 是错误的。那永远行不通,所以……也许我们可以反过来做。规范并不是那么简单,所以让我们开始反转它,看看我们是否可以将它改写成更程序化的东西:

The year is a leap year unless:

  1. It is not divisible by 4
  2. It is divisible by 100 and not 400

是的,我认为这会很好用。编写代码非常简单:

def is_leap(year):
    # It is not divisible by 4
    if year%4 != 0:
        return False

    # It is divisible by 100 and not 400
    elif year%100 == 0 and year%400 != 0:
        return False

    return True

解决方案 B

酷。因此,这是我们如何对此进行编码的一种选择。现在让我们完成那个 巨大的 if 语句 。看起来它可能会变得粗糙,但这实际上会产生一个非常优雅的解决方案。

让我们从第一个条件开始

The year can be evenly divided by 4, is a leap year

def is_leap(year):
    return year%4 == 0

然后我们将其与

结合起来

Unless the year can be evenly divided by 100

def is_leap(year):
    return year%4 == 0 and not (year%100 == 0)

在我们继续前进的过程中,我们必须注意我们的结合方式。我们最终会把三个条件组合在一起,但是第三个条件只修改第二个条件,所以我们需要确保我们在这里正确使用括号。

Unless the year is also evenly divisible by 400

def is_leap(year):
    return year%4 == 0  and not (year%100 == 0  and not (year%400 == 0))

这将计算我们是否有闰年。如果我们愿意,我们可以使用 x != ynot(x==y) 相同的事实,我们可以使用 De Morgan's Law 将该函数转换为

def is_leap(year):
    return year%4 == 0 and (year%100 != 0 or year%400 == 0)

最终解A

这通常是不可能的,但在这个特定问题中,我们可以使用 400 是 100 的倍数这一事实来简化解决方案 A,如下所示:

def is_leap(year):
    leap = False

    # If we can divide they year by 4, it's probably a leap year
    if year%4 == 0:
        leap = True

    # But every 100 years we skip a leap year
    if year%100 == 0:
        # when true it also means the year is divisible by 100
        # so we can skip checking those conditions
        leap = False

    # Unless the year is also divisible by 400
    if year%400 == 0:
        # when true it also means the year is divisible by 100 and by 4
        # so we can skip checking those conditions
        leap True

    return leap

但是,我们应该添加一些评论来解释我们所做的优化。特别是因为如果需要添加无法以相同方式优化的新条件,我们不希望将来引入错误。有了这些评论,解决方案变得有点冗长。

最终方案B

添加一些评论

def is_leap(year):
    return (
        # First check if we have a normal leap year
        year%4 == 0
        # However, anything divisible by 100 is not considered a leap year
        # Unless the year is also a multiple of 400
        and (year%100 != 0 or year%400 == 0)
    )

这是一个非常优雅的解决方案,也是执行速度最快的。

最终解C

检查 False 条件并返回 True 作为默认值对我来说似乎有点不自然。通常情况恰恰相反。我们可以添加第二个函数 is_not_leap

def is_not_leap(year):
    # It is not divisible by 4
    if year%4 != 0:
        return True

    # It is divisible by 100 and not 400
    elif year%100 == 0 and year%400 != 0:
        return True

    return False

def is_leap(year):
    return not is_not_leap(year)

但是现在当我们做 year%4 != 0 时,它有点像双重否定。我们正在检查它是否不能被 4 整除以确定它是否是闰年。这仍然令人困惑。这个解决方案对我来说似乎不自然。

获胜者

在所有三个解决方案中,解决方案 B 是我最喜欢的。它没有任何双重否定,没有隐藏的优化,它只是工作,而且遵循逻辑并不难。