理解一个范例

-2 投票
5 回答
1342 浏览
提问于 2025-04-15 14:59
def solve(numLegs, numHeads):
    for numChicks in range(0, numHeads + 1):
        numPigs = numHeads - numChicks
        totLegs = 4*numPigs + 2*numChicks
        if totLegs == numLegs:
            return [numPigs, numChicks]
    return [None, None]

def barnYard(heads, legs):
    pigs, chickens = solve(legs, heads)
    if pigs == None:
        print "There is no solution."
    else:
        print 'Number of pigs: ', pigs
        print 'Number of Chickens: ', chickens

我正在学习Python,遇到了这个例子,能不能请大家用简单易懂的语言(或者伪代码)逐行解释一下这个代码在做什么。

非常感谢!

5 个回答

1

简单来说,solve这个函数是在检查所有可能的鸡和猪的组合,当找到符合条件的组合时,就把它返回。

鸡的数量加上猪的数量必须等于头的总数,所以它会从0到头的总数逐一检查每一种鸡的数量(这就是for range(0,NumHeads+1)的作用),然后把猪的数量设定为头的总数减去鸡的数量。

接下来,只需要计算出脚的数量,然后看看它们是否匹配就可以了。

2

Alex Martelli提到了一种代数解法,我在这里也把它写出来,以便完整性。这个方法可以通过同时方程来解决。作为一种简单的数学解法,对于腿和头的数量特别多的情况,它可能会更快一些 :-)

我们设定:

  • H 代表头的数量;
  • L 代表腿的数量;
  • C 代表小鸡的数量;
  • P 代表猪的数量。

给定 CP,我们可以用以下公式计算出其他两个变量:

H =  C +  P (1)
L = 2C + 4P (2)

我会在下面详细说明每一步的计算。数学方面比较在行的人可能会指出这些步骤可以合并,但我更喜欢逐步说明。从(1)中,我们可以计算出:

   H = C + P
=> 0 = C + P - H       [subtract H from both sides]
=> 0 = H - C - P       [multiply both sides by -1]
=> P = H - C           [add P to both sides] (3)

然后把这个结果代入(2):

    L = 2C + 4P
=>  L = 2C + 4(H - C)   [substitute H-C for P]
=>  L = 2C + 4H - 4C    [expand 4(H-C) to 4H-4C]
=>  L = 4H - 2C         [combine 2C-4C into -2C]
=>  0 = 4H - 2C - L     [subtract L from both sides]
=> 2C = 4H - L          [add 2C to both sides]
=>  C = 2H - L/2        [divide both sides by 2] (4)

现在你有两个公式,一个可以根据头和腿的数量计算小鸡的数量 (4),另一个可以根据小鸡和头的数量计算猪的数量 (3)

下面是用Python写的代码,里面有适当的检查,以确保不会出现一些奇怪的数学解,比如2个头和7条腿算出1.5只猪和0.5只小鸡,或者1个头和12条腿算出5只猪和-4只小鸡 :-)

def solve (numLegs, numHeads):
    # Use the formulae (these make integers).
    chicks = numHeads * 2 - int (numLegs / 2)
    pigs = numHeads - chicks

    # Don't allow negative number of animals.
    if chicks < 0 or pigs < 0:
        return [None, None]

    # Don't allow fractional animals.
    if chicks * 2 + pigs * 4 != numLegs:
        return [None, None]
    if chicks + pigs != numHeads:
        return [None, None]

    return [pigs, chicks]

当然,如果你输入的是头或腿的分数,结果就不一定了。这里有一个完整的测试程序,你可以尝试不同的值,以确保两种方法返回相同的结果:

import sys

def usage (reason):
    print "Error: %s"%(reason)
    print "Usage: solve <numHeads> <numLegs>"
    sys.exit (1);

def solve1 (numLegs, numHeads):
    for numChicks in range (0, numHeads + 1):
        numPigs = numHeads - numChicks
        totLegs = 4 * numPigs + 2 * numChicks
        if totLegs == numLegs:
            return [numPigs, numChicks]
    return [None, None]

def solve2 (numLegs, numHeads):
    chicks = numHeads * 2 - int (numLegs / 2)
    pigs = numHeads - chicks
    if chicks < 0 or pigs < 0:           return [None, None]
    if chicks * 2 + pigs * 4 != numLegs: return [None, None]
    if chicks + pigs != numHeads:        return [None, None]
    return [pigs, chicks]

if len (sys.argv) != 3:
    usage ("Wrong number of parameters (%d)"%(len (sys.argv)))

try:    heads = int (sys.argv[1])
except: usage ("Invalid <numHeads> of '%s'"%(sys.argv[1]))

try:    legs = int (sys.argv[2])
except: usage ("Invalid <numLegs> of '%s'"%(sys.argv[2]))

print "[pigs, chicks]:"
print "  ", solve1 (legs, heads)
print "  ", solve2 (legs, heads)
8

solve 这个函数的作用是计算需要多少只小鸡(每只1个头,2条腿)和多少只猪(每只1个头,4条腿),才能达到给定的头和腿的总数。

它采用了一种“暴力破解”的方法,也就是非常简单直接的方式:

  • 它会尝试所有可能的小鸡数量,从0只到指定的头数(这就是循环 for numChicks in range(0, numHeads + 1): 的作用,因为 range 会生成从起始值到结束值前一个的整数);
  • 对于每一个给定的小鸡数量 numChicks,它会计算出需要多少只猪来满足头的总数,计算方式是 numPigs = numHeads - numChicks
  • 然后,它会计算这些小鸡和猪一共有多少条腿,计算方式是 totLegs = 4*numPigs + 2*numChicks
  • 接着,它会检查 totLegs 是否等于要求的腿的数量:如果相等,就返回一个包含小鸡和猪数量的列表,表示这个问题的解;
  • 最后,如果循环结束后还没有返回任何值,就说明没有解,它会返回一个列表,里面的两个值都是 None,表示无解。

barnYard 只是把解决方案交给 solve,然后以易读的方式打印出来,可能是“没有解决方案”,或者是漂亮地显示小鸡和猪的数量。

现在,为了继续进步,想想看 solve 是否可以写得更高效。显然,如果腿的数量少于头的两倍,或者多于头的四倍,或者是奇数,那么就没有解——也许 solve 可以先检查这些情况,然后立即返回 [None, None]。你能写出这个代码吗……?

这可能不太明显,但每一种头和腿的组合都有解——而且可以通过简单的算术运算找到解,而不需要循环。想一想,也许可以借助初中数学的知识……

撰写回答