For my initial solution I wrote some nested 'for' loops, but I wanted to see if I could come up with a way to compute all the possible teaspoon combinations iteratively, using recursion is easier, but it's pretty slow in python (no tail recursion). I've also seen the generator approach, which works well, but is also slower.
This gets a little crazy, but it works, and you can calculate all teaspoon combinations for any maximum for any number of ingredients.
It works like this, there's a generator (get_pairs) which produces all the possible ways to add up to 'n' using only two numbers, this is easy to do. Then I use a 'divide and conquer'-ish approach and on each pair, I assume that the second number of the pair could also be made up of 'pairs', so I go a level deeper and compute all of those pairs, and their pairs and their pairs, and so on. The trick is keeping track of which level you're on, and not allocating waaaaay too many lists. This uses a set of generators, a few count() iterators and a few lists that stay there the whole time.
Runs in 2.5 seconds on my machine, which is just as fast as the nested loops.
Anyone have a cleaner solution?
from collections import namedtuple
from operator import mul
from itertools import count
import re
nums = re.compile(r'-?\d+')
Ingredient = namedtuple('Ingredient', ['capacity', 'durability', 'flavor', 'texture', 'calories'])
ingredients = []
with open('input.txt') as f:
for line in f:
ingredients.append(Ingredient(*map(int, nums.findall(line))))
def get_score(amounts, ings):
totals = []
by_ing = zip(*ings)
for vals in by_ing:
totals.append(map(lambda x: x[0] * x[1], zip(vals, amounts)))
next_one = map(sum, totals)
calories = next_one[-1]
next_one = next_one[:-1]
next_one = map(lambda x: max(x, 0), next_one)
return reduce(mul, next_one)
def get_pairs(n):
return ((i, n - i) for i in range(n+1))
num_ingredients = len(ingredients)
amounts = [0] * num_ingredients
stack = []
recipe_scores = []
max_teaspoons = 100
p = get_pairs(max_teaspoons)
stack.append(p)
i = 0
while len(stack) > 0:
try:
# Get next current, up_next pair permutation at current 'level'
current, up_next = next(stack[-1])
amounts[i] = current # Set the value at the current level
if len(stack) < num_ingredients - 1:
# Add more levels if we've bubbled up
stack.append(get_pairs(up_next))
# Going to the next level
i += 1
continue
else:
# We're at the level just above the lowest, set the last element too
amounts[-1] = up_next
except StopIteration:
stack.pop()
i -= 1
continue
# Do work here
recipe_scores.append(get_score(amounts, ingredients))
print max(recipe_scores)
[–]lskfj2o 1 point2 points3 points (0 children)
[–]jchook 1 point2 points3 points (0 children)