-1

Given the final score of a basketball game, how i can count the number of possible scoring sequences that lead to the final score.

Each score can be one of: 3 point, 2 point, 1 point score by either the visiting or home team. For example:

basketball(3,0)=4

Because these are the 4 possible scoring sequences:

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

And: basketball(88,90)=2207953060635688897371828702457752716253346841271355073695508308144982465636968075

Also I need to do it in a recursive way and without any global variables(dictionary is allowed and probably is the way to solve this) Also, the function can get only the result as an argument (basketball(m,n)).

for those who asked for the solution:

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;
eli
  • 490
  • 1
  • 3
  • 22
  • uhhh.. tried to reduce from the given result (every possible play- 1,2,3 points) using recursion until i get to 0 but for that i need global variable and i cant use one,, – eli Dec 04 '12 at 21:21
  • Could you make a little clearer what `m` and `n` stand for? I also agree with @dusan that you should show what you have tried, otherwise this is just a "give me teh codez" question which will likely be closed. – Benjamin Bannier Dec 04 '12 at 21:23
  • What's the result for `basketball(1,1)`? Is "team A scores 1 point and then team B scores 1 point" distinct from "team B scores 1 point and then team A scores 1 point"? – Kevin Dec 04 '12 at 21:23
  • @honk n and m are the score of each team, and im not asking the code.. only an algorithm or any direction, – eli Dec 04 '12 at 21:25
  • @kevin the result will be 2- A team score 1 then B team score 1 (1-1), and the other one is B team score first then A team score(1-1) – eli Dec 04 '12 at 21:26
  • 1
    I'm not sure what you're asking. When you say, "number of plays", do you mean the count of play sequences that would give the final score? – jimhark Dec 04 '12 at 21:30
  • 1
    follow up of jimhark's question: Do you want to enumerate the solutions of `3*x1 + 2*y1 + z1 = N1` and `3*x2 + 2*y2 + z2 = N2`, or sequences like "team A scores 2, team B scores 1, team B scores 2", or even "the sequence of scores of team A are: 2,2,3... and the sequence of scores of team B are: 1,2,1...". These are three completely different problems. – nvlass Dec 04 '12 at 21:40
  • To generate the example values you have to count the possible scoring sequences. I've rewritten the question to reflect this. – jimhark Dec 04 '12 at 23:30
  • @user1375265, please post your solution when you have one. – jimhark Dec 05 '12 at 00:54
  • @jimhark added the solution – eli Dec 07 '12 at 11:30
  • @user1375265, that's good. I'll post my solution soon. – jimhark Dec 07 '12 at 21:43

3 Answers3

2

When you're considering a recursive algorithm, there are two things you need to figure out.

  1. What is the base case, where the recursion ends.
  2. What is the recursive case. That is, how can you calculate one value from one or more previous values?

For your basketball problem, the base case is pretty simple. When there's no score, there's exactly one possible set of baskets that has happened to get there (it's the empty set). So basketball(0,0) needs to return 1.

The recursive case is a little more tricky to think about. You need to reduce a given score, say (M,N), step by step until you get to (0,0), counting up the different ways to get each score on the way. There are six possible ways for the score to have changed to get to (M,N) from whatever it was previously (1, 2 and 3-point baskets for each team) so the number of ways to get to (M,N) is the sum of the ways to get to (M-1,N), (M-2,N), (M-3,N), (M,N-1), (M,N-2) and (M,N-3). So those are the recursive calls you'll want to make (after perhaps some bounds checking).

You'll find that a naive recursive implementation takes a very long time to solve for high scores. This is because it calculates the same values over and over (for instance, it may calculate that there's only one way to get to a score of (1,0) hundreds of separate times). Memoization can help prevent the duplicate work by remembering previously calculated results. It's also worth noting that the problem is symmetric (there are the same number of ways of getting a score of (M,N) as there are of getting (N,M)) so you can save a little work by remembering not only the current result, but also its reverse.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • One way to take advantage of the parameter symmetry is to use a frozenset of the parameters as the key to the memoization hash. – jimhark Dec 05 '12 at 01:01
  • The frozenset idea is a good one. In my quick implementation I used tuples for my keys and simply added the same value twice, once under `(M,N)` and once under `(N,M)`. – Blckknght Dec 05 '12 at 01:15
1

There are two ways this can be done, and neither come close to matching your specified outputs. The less relevant way would be to count the maximum possible number of scoring plays. Since basketball has 1 point scores, this will always be equal to the sum of both inputs to our basketball() function. The second technique is counting the minimum number of scoring plays. This can be done trivially with recursion, like so:

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)

Can this be done more tersely and even more elegantly? Certainly, but this should give you a decent starting point for the kind of stateless, recursive solution you are working on.

Alex V
  • 3,416
  • 2
  • 33
  • 52
  • This looks like a great answer to a tough question. – woz Dec 04 '12 at 21:48
  • Except it doesn't seem to give the correct answer. For `basketball(3, 0)` I'm getting `1` where the original question says it should be 4. – jimhark Dec 04 '12 at 21:59
  • @jimhark Did you read my answer? I explicitly said that neither of the solutions will give the output specified in the question. I explained that in the first sentence of the answer. – Alex V Dec 04 '12 at 22:00
  • @Maxwell, missed that. Okay. Got it. Thanks. – jimhark Dec 04 '12 at 22:01
  • @Maxwell, your mention of a terser solution got me thinking. I saw I could take my 17-line correct solution and, using a conditional expression and two list comprehensions, condense it down to a one line function. Of course clarity suffered but a 1 line solution is possible (but probably not recommended). – jimhark Dec 04 '12 at 23:57
0

tried to reduce from the given result (every possible play- 1,2,3 points) using recursion until i get to 0 but for that i need global variable and i cant use one.

Maybe this is where you reveal what you need. You can avoid a global by passing the current count and/or returning the used count (or remaining count) as needed.

In your case I think you would just pass the points to the recursive function and have it return the counts. The return values would be added so the final total would roll-up as the recursion unwinds.

Edit

I wrote a function that was able to generate correct results. This question is tagged "memoization", using it gives a huge performance boost. Without it, the same sub-sequences are processed again and again. I used a decorator to implement memoization.

I liked @Maxwell's separate handling of teams, but that approach will not generate the numbers you are looking for. (Probably because your original wording was not at all clear, I've since rewritten your problem description). I wound up processing the 6 home and visitor scoring possibilities in a single function.

My counts were wrong until I realized what I needed to count was the number of times I hit the terminal condition.

Solution

Other solutions have been posted. Here's my (not very readable) one-liner:

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])

Actually I also have this line just before the function definition:

@memoize

I used Michele Simionato's decorator module and memoize sample. Though as @Blckknght mentioned the function is commutative so you could customize memoize to take advantage of this.

While I like the separation of concerns provided by generic memoization, I'm also tempted to initialize the cache with (something like):

cache = {(0, 0): 1}

and remove the special case check for 0,0 args in the function.

jimhark
  • 4,938
  • 2
  • 27
  • 28