Calculating all possible ways to score in a football game (recursively)

℡╲_俬逩灬. 提交于 2020-04-16 09:33:43

问题


Assuming a simplified football scoring system where you can score {2, 3, 7} points, what are all the possible ways that you can score given a number of points?

I'm just working problems in the EPI book, and he solves this problem using DP. I wanted to practice a recursive technique, but I'm getting tripped up trying to save all the different ways in to a results list.

def find_scoring(points, scores):

    ways_to_score = [2, 3, 7]

    def score_finder(p, scores):
        print(scores, p)
        if p == 0:
            print('here')
            results.append(scores)
            return True
        elif p <= 1:
            return False

        for i in ways_to_score:
            scores.append(i)
            if not score_finder(p-i, scores):
                scores.pop()
                return False

    results = []
    score_finder(points, scores)
    return results

print(find_scoring(12, []))

For the above, I would expect to see [[2,2,2,2,2,2], [2,2,2,3,3], ....]

I cannot figure out how to pass the scores variable properly. Appreciate the assist!


回答1:


  • results.append(scores) only adds a reference to scores when we need a copy. We can do this with a slice: scores[:].
  • There is no need for if not score_finder(p-i, scores):; we want to pop regardless of whether we had a successful result generated in a child node or not.
  • It's a bit of a burden on the caller to pass the temporary scores list into the score_finder function.
  • In contrast, it's nice to parameterize the ways_to_score so that the caller can specify it, but use a default argument so they don't have to.

Here's a possible rewrite which addresses these issues and cleans up the logic a bit:

def find_scoring(points, ways_to_score=[2, 3, 7]):
    def score_finder(points, scores, result):
        if points == 0:
            result.append(scores[:])
        elif points > 0:
            for val in ways_to_score:
                scores.append(val)
                score_finder(points - val, scores, result)
                scores.pop()

        return result

    return score_finder(points, [], [])

We can also return a generator:

def find_scoring(points, ways_to_score=[2, 3, 7]):
    def score_finder(points, scores, result):
        if points == 0:
            yield scores[:]
        elif points > 0:
            for val in ways_to_score:
                scores.append(val)
                yield from score_finder(points - val, scores, result)
                scores.pop()

    yield from score_finder(points, [], [])

if __name__ == "__main__":
    print(list(find_scoring(12)))



回答2:


You can use a function that iterates through the possible scores and deduct the score from the given target points before making a recursive call, until the target points reaches 0:

def find_scoring(points, ways_to_score):
    if points:
        for score in ways_to_score:
            if points - score >= 0:
                for way_to_score in find_scoring(points - score, ways_to_score):
                    yield [score, *way_to_score]
    else:
        yield []

So that list(find_scoring(12, {2, 3, 7})) returns:

[[2, 2, 2, 2, 2, 2],
 [2, 2, 2, 3, 3],
 [2, 2, 3, 2, 3],
 [2, 2, 3, 3, 2],
 [2, 3, 2, 2, 3],
 [2, 3, 2, 3, 2],
 [2, 3, 3, 2, 2],
 [2, 3, 7],
 [2, 7, 3],
 [3, 2, 2, 2, 3],
 [3, 2, 2, 3, 2],
 [3, 2, 3, 2, 2],
 [3, 2, 7],
 [3, 3, 2, 2, 2],
 [3, 3, 3, 3],
 [3, 7, 2],
 [7, 2, 3],
 [7, 3, 2]]



回答3:


You're having trouble because you've tried to write the entire program in one fell swoop, without testing the pieces as you go. You now have several problems facing you at once.

First of all, you've convoluted your coding blocks and variables: you define score_finder inside find_scoring, create global variables (almost always a bad idea), and initialize those somewhat arbitrarily. I've unraveled your code a bit here:

ways_to_score = [2, 3, 7]
results = []

def score_finder(p, scores):
    print(scores, p)
    if p == 0:
        print('here')
        results.append(scores)
        return True
    elif p <= 1:
        return False

    for i in ways_to_score:
        scores.append(i)
        if not score_finder(p-i, scores):
            scores.pop()
            return False


def find_scoring(points, scores):

    score_finder(points, scores)
    return results


print(find_scoring(12, []))

With that done, you're trying to use scores for more than one purpose, which is where you lose your accounting. Also, you haven't defined well in your mind what score_finder does, as shown by (a) sometimes using the return value, other times not; (b) both returning a value and employing a side effect on a global variable.


To clean your code, back up to the general approach to this sort of problem. When your routine is called, it gets a reference list of scores (2, 3, 7) and a target sum. You have a loop to go through those scores and try each one in turn. If you found a solution, then add it to the list of solutions.

You have two ways to handle the addition: (a) pass the partial solution as part of the recursive call; when you hit your base case, add the full solution to your list; (b) return that "success" Boolean; as you crawl back up to the top level, build the solution, one "return" at a time. Only when you get back to the top level do you add the full solution to that master list.

You're a bit confused; you're trying to do both.

Is that enough to get you moving?



来源:https://stackoverflow.com/questions/57779421/calculating-all-possible-ways-to-score-in-a-football-game-recursively

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!