【问题标题】:Python challenge:zombiesPython挑战:僵尸
【发布时间】:2020-05-19 07:40:08
【问题描述】:

我正在尝试解决以下挑战,但无济于事: 僵尸从距离米开始,以每秒 0.5 米的速度移动。每一秒,你先射击一个僵尸,然后剩下的僵尸蹒跚前行 0.5 米。

如果任何僵尸设法达到 0 米,你就会被吃掉。如果你在射击所有僵尸之前用完了弹药,你也会被吃掉。为简单起见,我们可以忽略重新加载所花费的任何时间。

编写一个函数,接受僵尸总数、范围(米)和你拥有的子弹数。

如果你射杀了所有僵尸,返回“你射杀了所有 X 个僵尸。”如果你在杀死所有僵尸之前被吃掉,并且在弹药用完之前,返回“你在被吃掉之前射杀了 X 个僵尸:不堪重负。”如果你在射击所有僵尸之前用完了弹药,返回“你在被吃掉之前射击了 X 个僵尸:弹药用完了。”

(如果在剩下的僵尸到达你的同时你的弹药用完了,返回“你在被吃掉之前射杀了 X 只僵尸:不堪重负。”。)

到目前为止我的代码是:

def zombie_shootout(zombies, distance, ammo):
    if ammo >= zombies:
        ammo -= 1
        zombies -= 1
        distance -= 0.5
    elif ammo < zombies:
        print("You shot ",zombies,"zombies before being eaten: ran out of ammo.")
    elif distance == 0:
        print("You shot ",zombies,"zombies before being eaten: overwhelmed.")
    else:
        print("You shot all ", zombies,"zombies.")

我知道对于那些无法解决这个难题的人有一些解决方案,但它们很可能更加简洁和优雅,我想知道如何(如果可能的话)去解决这个问题我的方式(很多 ifs 和 elifs 并且可能在某个地方添加一段时间)。

【问题讨论】:

  • 你知道while 循环是什么吗? (这听起来可能很刻薄,但我保证这是真诚的)
  • 哈哈,是的,我想输入“while distance>0”,但在这种情况下这行不通,而且我不确定如何继续迭代我的程序(也许“for i在范围内(距离)”?)。
  • 你已经接近了。为什么你认为while distance &gt; 0 不起作用?
  • 我应该补充一下,作为一个新程序员,您可能有兴趣学习橡皮鸭编程en.wikipedia.org/wiki/Rubber_duck_debugging我认为它可以帮助您解决这里的问题
  • 我错过了什么吗?这个谜题不需要循环或任何类似的东西。这是简单的算术:return ammo &gt;= zombies and distance_in_metres &gt; zombies * 0.5

标签: python python-3.x algorithm if-statement while-loop


【解决方案1】:

让我们检查一下您的代码:您在 cmets 中说过您知道在某处需要一个 while 循环,所以让我们考虑一下我们应该在该循环上设置什么条件。我们想继续射击,直到我们用完弹药,僵尸吃掉我们,或者我们用完僵尸。我们可以使用all() 来检查所有变量是否大于0:

while all(x > 0 for x in (distance, ammo, zombies)):

这相当于:

while distance>0 and ammo>0 and zombies>0:

虽然此条件为True,但我们希望在您的问题中应用您在 if 语句中已有的逻辑。您还想打印被射杀的僵尸数量,所以让我们添加一个zombies_shot 变量,并在函数开始时将其设置为0,并在每次运行while 循环时递增它。我们现在有:

def zombie_shootout(zombies, distance, ammo):
    zombies_shot = 0
    while all(x>0 for x in (distance, ammo, zombies)):
        ammo -= 1
        zombies_shot += 1
        zombies -= 1
        distance -= 0.5

所以现在我们需要在跳出while 循环后检查条件。你几乎也有这个,但我们还要检查是否还有僵尸剩余,否则如果我们在最后一个僵尸到达我们之前射杀它,你的函数仍然会说我们被吃掉了。我们也可以使用新的zombies_shot 变量。

if ammo <= 0 and zombies > 0:
    print("You shot",zombies_shot,"zombies before being eaten: ran out of ammo.")
elif distance <= 0 and zombies > 0:
    print("You shot",zombies_shot,"zombies before being eaten: overwhelmed.")
else:
    print("You shot all", zombies_shot,"zombies.")

我们还可以添加 return 语句而不是 print 语句,但这取决于您。我们现在的全部功能是:

def zombie_shootout(zombies, distance, ammo):
    zombies_shot = 0
    while all(x>0 for x in (distance, ammo, zombies)):
        ammo -= 1
        zombies_shot += 1
        zombies -= 1
        distance -= 0.5
    if ammo <= 0 and zombies > 0:
        print("You shot",zombies_shot,"zombies before being eaten: ran out of ammo.")
    elif distance <= 0 and zombies > 0:
        print("You shot",zombies_shot,"zombies before being eaten: overwhelmed.")
    else:
        print("You shot all", zombies_shot,"zombies.")

【讨论】:

  • @Nathan 很公平,补充说明:)
  • 正如我在答案下方的评论中指出的那样,我不明白为什么要使用循环。
  • @KonradRudolph 显然这个问题可以通过简单的计算来解决——我认为这个练习的目的是教授基本的循环和函数,OP 在他的问题结束时承认了这一点。
  • @JohnDoe 使用类似: return 'You shot ' +zombies_shot + 'zombies' 。如果你使用的是 Python 3.6+,你也可以使用 return f'You shot {zombies_shot}zombies' using f-strings :)
  • 是的,我昨天终于弄明白了。 :) 非常感谢你的帮助! F 字符串很棒,旧的 python 复杂得多(%-formatting 和 str.format())。
【解决方案2】:

有一个简短的递归解决方案,但由于您想跟踪射杀僵尸的数量,我们需要编写一个递归帮助器来跟踪这个数字。

def zombie_rec(zombies, distance, ammo, shot):
    if zombies <= 0:
        return f"You shot all {shot} zombies."
    elif ammo <= 0:
        return f"You shot {shot} zombies before being eaten: ran out of ammo."
    elif distance <= 0:
        return f"You shot {shot} zombies before being eaten: overwhelmed."

    else:
        return zombie_rec(zombies - 1, distance - 0.5, ammo - 1, shot + 1)

def zombie(zombies, distance, ammo):
    print(zombie_rec(zombies, distance, ammo, 0))

既然递归会让人费解,那我们一步一步来吧。

递归的第一定律是首先检查您的结束条件。在这种情况下,我们有三个。

  1. 所有僵尸都死了,我们赢了。
  2. 距离为零,我们被吃掉了(现在)。
  3. 弹药为零,我们(最终)被吃掉了。

注意,我们应该按这个顺序检查它们,因为zombies == 0 表示即使其他两个也是 0,我们也活着。

如果以下条件都不成立,那么我们必须再转一圈。这可以通过递归调用来完成,注意

  1. zombiesammo 都减少了 1,distance 减少了 0.5。 (我们射杀了一个僵尸,而其余的则在前进)。
  2. 我们将shot 加 1 以跟踪本轮结束时的投篮次数。

类似于while 循环,我们应该仔细检查我们是否已经完成并且不会陷入无限循环。在这种情况下,我们确信zombiesdistanceammo 将达到零,因为它们每一步都减少 1。

再想一想,将三个结束条件切换为 &lt;= 将使这对错误参数(负数或小数值)更加稳健。

如果你有兴趣了解更多关于递归的知识,我强烈建议The Little Schemer

【讨论】:

  • 我喜欢这个递归函数的例子。您能否解释一下这些是如何工作的,以便@John Doe 也清楚?
  • 由于某种原因,这在网站上不起作用,但我在具有相同参数的 ide 中尝试了它,它工作正常,谢谢!
  • 与return(最后一行)一起工作,非常感谢!
【解决方案3】:

虽然@CDJB 的回答非常好,但我想指出另一种也有效但速度稍快的方法(从计算上讲)。这可能很重要的原因是,如果整个世界都被咬了,你有大约 7.000.000.000 个僵尸要处理,所以一个 while 循环会花费很长时间。

我们知道僵尸从x m 的距离开始,每行进 0.5 m,我们就可以射杀 1 个僵尸。这意味着我们可以拍摄:

 zombies_shot_distance = int(distance * 2)

我们也知道我们射杀的僵尸不能多于弹药,所以:

 zombies_shot_ammo = ammo

最大射杀僵尸数量是两者中最低的:

 zombies_shot_max = min(zombies_shot_distance, zombies_shot_ammo)

如果最大僵尸射击数量低于僵尸数量,我们知道我们将无法射击所有僵尸:

if zombies > zombies_shot_max:
    print("You shot", zombies_shot_max, "zombies")
    if zombies_shot_ammo <= zombies_shot_distance:
         print("because you ran out of ammo")
    else:
         print("because you were overwhelmed")
else:
    print("You shot all the zombies")

【讨论】:

  • 谢谢,知道解决问题的不同方法总是很高兴!
猜你喜欢
  • 2016-10-26
  • 2012-07-15
  • 2011-10-15
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 2018-06-06
  • 2011-03-14
  • 2012-03-20
相关资源
最近更新 更多