I cleaned up the code formatting, factored out a bunch of utility functions, and converted it to a linear programming problem. See what you think:
from decimal import Decimal
def get_name(s):
return s.split(',')[0]
def get_price(s):
return Decimal(s.split('$')[-1])
def get_menu(fname):
with open(fname) as inf:
budget = get_price(inf.next())
items = [(get_name(line), get_price(line)) for line in inf]
return budget, items
def integer_linear_solver(coefficients, total, index=None):
"""
Given coefficients [c0, c1, ... cN] and total,
generate all integer solutions to the equation
s0*c0 + s1*c1 + ... + sN*cN == total
and return as [s0, s1, ... sN]
"""
if index is None:
# Start at the end and work back
# (this avoids having to repeatedly slice coefficients)
index = len(coefficients) - 1
c = coefficients[index]
max_s = total // c
if index: # more coefficients available - recurse
for s in range(max_s + 1): # [0 .. max_s]
for sol in integer_linear_solver(coefficients, total - s*c, index-1):
yield sol + [s]
else: # last coefficient
if not(total % c):
# no remainder -> exact solution found
yield [max_s]
def print_sol(coeffs, labels):
items = ('{0} {1}'.format(c, l) for c,l in zip(coeffs, labels) if c > 0)
print(', '.join(items))
def main():
budget,items = get_menu('menu.txt')
names,prices = zip(*items)
solutions = 0
for sol in integer_linear_solver(prices, budget):
print_sol(sol, names)
solutions += 1
if solutions:
print('{} solutions found'.format(solutions))
else:
print("There is no combination of dishes that will equal your budget.")
if __name__=="__main__":
main()
Run against your first example menu, it gives
2 side salad, 2 hot wings, 1 mozzarella sticks
2 french fries, 2 side salad, 1 sampler plate
1 mixed fruit, 3 side salad, 1 sampler plate
...
1 mixed fruit, 1 pasta1, 11 TESTER
15 TESTER
109 solutions found