问题
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 toscores
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 topop
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 thescore_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