在给出最终得分时计算篮球比赛的数量

时间:2012-12-04 21:17:58

标签: python recursion dictionary memoization

考虑到篮球比赛的最终得分,我如何计算导致最终得分的可能得分序列的数量。

每个得分可以是以下之一:来自访问团或主队的3分,2分,1分。例如:

basketball(3,0)=4

因为这些是4种可能的评分序列:

V3
V2, V1
V1, V2
V1, V1, V1

和:     篮球(88,90)= 2207953060635688897371828702457752716253346841271355073695508308144982465636968075

此外,我需要以递归的方式进行,没有任何全局变量(允许字典,可能是解决此问题的方法) 此外,该函数只能将结果作为参数(basketball(m,n))。

对于那些要求解决方案的人:

basketballDic={}
def basketball(n,m):
    count=0;
    if n==0 and m==0:
        return 1;
    if (n,m) in basketballDic:
        return basketballDic[(n,m)]
    if n>=3:
        count+= basketball(n-3,m)
    if n>=2:
        count+= basketball(n-2,m)
    if n>=1:
        count+= basketball(n-1,m)
    if m>=3:
        count+= basketball(n,m-3)
    if m>=2:
        count+= basketball(n,m-2)
    if m>=1:
        count+= basketball(n,m-1)
    basketballDic[(n,m)]=count;
    return count;

3 个答案:

答案 0 :(得分:2)

当你考虑递归算法时,你需要弄清楚两件事。

  1. 递归结束的基本情况是什么。
  2. 什么是递归案例。也就是说,如何从一个或多个先前的值中计算出一个值?
  3. 对于你的篮球问题,基本情况非​​常简单。当没有得分时,恰好有一组可能的篮子(它是空集)。所以basketball(0,0)需要返回1。

    递归案例有点难以思考。你需要逐步减少一个给定的分数,比如说(M,N),直到你到达(0,0),计算在途中获得每个分数的不同方法。有六种可能的方法可以让分数从之前的任何位置变为(M,N)(每支球队有1,2和3个篮筐),所以到达(M,N)的方式数量是达到(M-1,N),(M-2,N),(M-3,N),(M,N-1),(M,N-2)和(M)的方法的总和M,N-3)。所以那些是你想要做的递归调用(可能是在一些边界检查之后)。

    你会发现一个天真的递归实现需要很长时间来解决高分。这是因为它反复计算相同的值(例如,它可能会计算出只有一种方法可以获得(1,0)数百个单独时间的分数)。通过记住先前计算的结果,记忆可以帮助防止重复工作。同样值得注意的是问题是对称的(获得(M,N)分数的方法与得到(N,M)的方法相同)所以通过记住不仅仅是目前的结果,但也反过来。

答案 1 :(得分:1)

有两种方法可以完成,而且两者都不接近匹配您指定的输出。相关性较低的方法是计算最大可能的得分数。由于篮球有1分得分,这总是等于我们basketball()函数的两个输入的总和。第二种技术是计算得分次数的最小数量。这可以通过递归来完成,如下所示:

def team(x):
    if x:
        score = 3

        if x < 3:
            score = 2
        if x < 2:
            score = 1

        return 1 + team(x - score)
    else:
        return 0

def basketball(x, y):
    return team(x) + team(y)

这可以更简洁,更优雅地完成吗?当然,但这应该为你正在开发的那种无状态递归解决方案提供一个不错的起点。

答案 2 :(得分:0)

  

尝试使用递归从给定结果(每个可能的游戏 - 1,2,3点)减少直到我得到0但是为此我需要全局变量而我不能使用它。

也许这就是你揭示你需要的地方。您可以通过传递当前计数和/或根据需要返回使用的计数(或剩余计数)来避免全局。

在你的情况下,我认为你只需将点传递给递归函数并让它返回计数。将添加返回值,以便在递归展开时最终总计将累计。

修改

我写了一个能够生成正确结果的函数。这个问题被标记为“memoization”,使用它可以提高巨大的性能。没有它,一次又一次地处理相同的子序列。我用装饰器来实现记忆。

我喜欢@Maxwell对团队的单独处理,但这种方法不会产生你想要的数字。 (可能是因为你原来的措辞一点也不清楚,我已经改写了你的问题描述)。我在一个单一的功能中处理了6个家庭和访客评分的可能性。

我的计数错了,直到我意识到我需要计算的是我达到终点条件的次数。

解决方案

已发布其他解决方案。这是我的(不太可读)单行:

def bball(vscore, hscore):
    return 1 if vscore == 0 and hscore == 0 else \
        sum([bball(vscore-s, hscore) for s in range(1,4) if s <= vscore]) + \
        sum([bball(vscore, hscore-s) for s in range(1,4) if s <= hscore])

实际上我在函数定义之前也有这一行:

@memoize

我使用Michele Simionato的decorator module并记住了样本。虽然@Blckknght提到函数是commutative,所以你可以自定义memoize以利用它。

虽然我喜欢通用memoization提供的关注点分离,但我也很想用(类似的)初始化缓存:

cache = {(0, 0): 1}

并删除函数中0,0 args的特殊情况检查。