基于Python的列表递归
我正在为我的一个CS课程解决一个问题,我很有信心我有正确的想法,只是当我实现它的时候,我没有得到正确的答案 游戏是:有两个玩家和一个数字列表(即[3,7,8,1,6,4,5])。每个玩家轮流从列表的两端选择一个数字。一旦选择了一个数字,它将从列表中删除,然后对手可以从这个新列表中选择他们想要的结束。目标是在列表为空时获得最大的数字总和 我的想法:让我们从简单的列表[1,2,3,4,5]开始。当玩家1从开始或结束(1或5)选择一个数字时,我们现在有一个较小的列表供对手选择。因此,让我举一个使用此列表的示例: 我选5个。新的列表是[1,2,3,4],对手可以从中选择。我不知道他们会选择列表的哪一端,但我知道它只能是1或4。如果是1,那么当轮到我时,我的左边是[2,3,4]。如果他们选择4,我只剩下[1,2,3]。如果我选择1,它们将保留[2,3],如果我选择3,它们将保留[1,2],以此类推,直到列表中没有数字。对手也在尽最大努力获得最高分数,因此他们不会总是贪婪地选择更大的分数。球员们同样聪明,因此他们都会使用完全相同的策略为自己获得最高分数 这是一个明显的递归问题,每次都出现在较小的列表上 注意:我不希望给出代码。由于这是一门cs课程,我真的更希望得到关于我可能做错了什么的提示,这样我就可以学习,而不是得到代码 这是我写的代码:基于Python的列表递归,python,list,recursion,Python,List,Recursion,我正在为我的一个CS课程解决一个问题,我很有信心我有正确的想法,只是当我实现它的时候,我没有得到正确的答案 游戏是:有两个玩家和一个数字列表(即[3,7,8,1,6,4,5])。每个玩家轮流从列表的两端选择一个数字。一旦选择了一个数字,它将从列表中删除,然后对手可以从这个新列表中选择他们想要的结束。目标是在列表为空时获得最大的数字总和 我的想法:让我们从简单的列表[1,2,3,4,5]开始。当玩家1从开始或结束(1或5)选择一个数字时,我们现在有一个较小的列表供对手选择。因此,让我举一个使用此列
def Recursive(A):
# check if there is only one item left. If so, return it
if len(A) == 1:
return A[0]
# take the left item and recurse on the list if the opponent
# were to take the left side, and the list if the opponent
# were to take the right number
takeLeftSide = A[0] + max(Recursive(A[1:-1]), Recursive(A[2:len(A)]))
takeRightSide = A[-1] + max(Recursive(A[0:-2]), Recursive(A[1:-1]))
return max(takeLeftSide, takeRightSide)
if __name__ == '__main__':
A = [5,8,1,3,6]
print Recursive(A)
我相信我应该期望12,但在某些情况下,我的输出是19和14
感谢您的帮助,我已经为此工作了好几个小时,我知道一旦您尝试并深入研究递归,内容就会变得混乱和混乱。以下是我尝试过的代码(使用您的输入)。它几乎和你的一模一样
def r(a):
if len(a) == 1:
return a[0]
takeLeft = a[0] + max(r(a[1:-1]), r(a[2:]))
takeRight = a[-1] + max(r(a[:-2]), r(a[1:-1]))
return max(takeLeft, takeRight)
正确答案是19
原因是:假设我们在寻找可能的最高分数(而不是双方都使用最佳策略的分数),那么游戏将如下所示:
P1: 6 Leftover: [5,8,1,3]
P2: 3 Leftover: [5,8,1]
P1: 6 + 5 Leftover: [8,1]
P2: 3 + 1 Leftover: [8]
P1: 6 + 5 + 8 Leftover: []
P1有19个,P2有4个
编辑:下面是在最佳情况下使用给定(稍加修改)递归代码时发生的情况
def r(a):
if len(a) == 1:
return a[0]
if len(a) == 2 or len(a) == 3:
if a[0] > a[-1]:
return True
return False
takeLeft = a[0] + max(r(a[1:-1]), r(a[2:]))
takeRight = a[-1] + max(r(a[:-2]), r(a[1:-1]))
if takeLeft > takeRight:
return True
return False
def game(a):
p1 = 0
p2 = 0
p1Turn = True
while len(a) > 0:
isLeft = r(a)
nextValue = -1
if isLeft: #if it's from the left
nextValue = a[0]
a = a[1:]
else: #if it's from the right
nextValue = a[-1]
a = a[:-1]
if p1Turn:
p1 += nextValue
else:
p2 += nextValue
p1Turn = not p1Turn
print "P1:", p1, "P2:", p2
以下是此场景中的游戏效果:
P1: 6 Leftover: [5, 8, 1, 3]
P2: 5 Leftover: [8, 1, 3]
P1: 6 + 8 Leftover: [1, 3]
P2: 5 + 3 Leftover: [1]
P1: 6 + 8 + 1 Leftover: []
P1: 15 P2: 8
您的函数基于此输入返回19,它应该这样做。实际上,你从来没有考虑到第二个玩家的行为,你只是假设第二个玩家会选择让玩家1的分数最大化的数字。你应该使用
takeLeftSide = A[0] + (sum(A[1:]) - Recursive(A[1:]))
takeRightSide = A[-1] + (sum(A[:-1]) - Recursive(A[:-1]))
这相当于
takeLeftSide = sum(A) - Recursive(A[1:])
takeRightSide = sum(A) - Recursive(A[:-1])
但更容易理解
这意味着一个玩家可以从1端得到一个数字,然后从剩下的数字中得到其他玩家无法得到的所有分数。
如果你需要更多信息,或者我不清楚,请看一看为了让两名球员“同样聪明”,他们应该使用相同的方法(算法)在每一轮选择一方 为了编程,您应该根据给定的列表开发选择边(线的前面或后面)的算法,并为不同的玩家每次调用此算法 为了演示它,我选择了一个相对原始的贪婪算法,它总是在两者之间选择较大的一个:
def pick_greedy(A):
if len(A) == 0:
result = 0
elif len(A) == 1:
result = A.pop()
elif A[0] > A[-1]:
result = A[0]
A = A[1:]
else:
result = A[-1]
A = A[:-1]
return A, result
if __name__ == '__main__':
A = [5,8,1,3,6]
x = 0
y = 0
while A:
A, _ = pick_greedy(A)
x += _
A, _ = pick_greedy(A)
y += _
print "Player 1: {}; Player 2: {}".format(x, y)
输出
Player 1: 6; Player 2: 5
Player 1: 14; Player 2: 8
Player 1: 15; Player 2: 8
当前实现的一个问题是,播放器1应该获得次要调用的最小值,而不是最大值(播放器2将获得这些调用的最大值)。如果您进行了更改,您将得到预期的结果:
12
takeLeftSide = A[0] + min(Recursive(A[1:-1]), Recursive(A[2:len(A)]))
takeRightSide = A[-1] + min(Recursive(A[0:-2]), Recursive(A[1:-1]))
当前实现中还有三个问题:(a)随着输入列表的增大,速度非常慢;(b) 如果输入列表的值为偶数,则引发索引器
;和(c)如果输入列表为空,则引发运行时错误
但是这里有一种不同的方式来思考这个问题:
如果列表中有偶数个值,则玩家1可以通过决定是使用偶数索引的所有值还是使用奇数索引的所有值来保证胜利或平局
如果初始列表中有奇数个值,玩家1只需在左和右之间做出决定。最初的选择为玩家2创建了一个大小相等的列表,玩家2将回到上面提到的策略
因此,玩家1可以预测玩家2的移动,并相应地做出最初的左右选择
def f(xs):
evens = sum(xs[i] for i in range(0, len(xs), 2))
odds = sum(xs[i] for i in range(1, len(xs), 2))
if len(xs) % 2 == 0:
return max(evens, odds)
else:
lft = xs[0]
rgt = xs[-1]
return max(
lft + min(odds, evens - lft),
rgt + min(odds, evens - rgt),
)
if __name__ == '__main__':
from random import randint
tests = [
(0, []),
(5, [5]),
(30, [20, 1, 15, 9, 19]),
(12, [5, 8, 1, 3, 6]),
('big', [randint(0, 100) for _ in xrange(0, 99999)]),
]
for exp, vals in tests:
print exp, f(vals)
与递归实现不同,这种方法是
O(N)
,因此具有能够处理非常大的列表的优势。我不确定您想做什么。显然你实际上没有两个玩家,你只是根据一些逻辑进行选择。什么是Recursive()
应该返回?这看起来不像我自然会使用递归的东西。它返回一个玩家可以得到的最大和。我不知道如何真正添加玩家,因为我无法向该方法添加另一个参数。它不需要是递归的,一个空列表在布尔表达式中计算为False
,因此您可以使用while
循环。我的意思是,您实际上并不是在创建一个供两个人玩的游戏,这只是一个查找玩家可以获得的最高分数的函数。谢谢你的评论,我已经编辑了我的帖子,因为我应该更清楚。事实上,我应该寻找一名球员的最高分数,两名球员都同样聪明,并使用最佳策略获得最高分数。抱歉搞混了。我不想问你的答案,但为什么在P2第一次转弯时,wo