理解一个范例
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 个回答
简单来说,solve
这个函数是在检查所有可能的鸡和猪的组合,当找到符合条件的组合时,就把它返回。
鸡的数量加上猪的数量必须等于头的总数,所以它会从0到头的总数逐一检查每一种鸡的数量(这就是for range(0,NumHeads+1)
的作用),然后把猪的数量设定为头的总数减去鸡的数量。
接下来,只需要计算出脚的数量,然后看看它们是否匹配就可以了。
Alex Martelli提到了一种代数解法,我在这里也把它写出来,以便完整性。这个方法可以通过同时方程来解决。作为一种简单的数学解法,对于腿和头的数量特别多的情况,它可能会更快一些 :-)
我们设定:
H
代表头的数量;L
代表腿的数量;C
代表小鸡的数量;P
代表猪的数量。
给定 C
和 P
,我们可以用以下公式计算出其他两个变量:
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)
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]
。你能写出这个代码吗……?
这可能不太明显,但每一种头和腿的组合都有解——而且可以通过简单的算术运算找到解,而不需要循环。想一想,也许可以借助初中数学的知识……