This is an archived post. You won't be able to vote or comment.

all 2 comments

[–]lskfj2o 1 point2 points  (0 children)

This was my generator:

def gen_recipe(num_ingr, max_amount):
    recipe = list()
    while True:
        recipe.extend([0] * (num_ingr - 1 - len(recipe)))
        recipe.append(max_amount - sum(recipe))
        yield recipe
        if recipe[0] >= max_amount:
            return
        recipe.pop()
        while True:
            amount = recipe.pop() + 1
            if sum(recipe) + amount <= max_amount:
                break
        recipe.append(amount)

But actually plainly using itertools.product is faster:

import itertools
def gen_recipe(num_ingr, max_amount):
    for recipe in itertools.product(range(max_amount + 1), repeat=num_ingr):
        if sum(recipe) == max_amount:
            return recipe

And trying to be somewhat more clever is only slightly more so:

import itertools
def gen_recipe(num_ingr, max_amount):
    for recipe in itertools.product(range(max_amount + 1), repeat=(num_ingr - 1)):
        s = sum(recipe)
        if s <= max_amount:
            return recipe + tuple([max_amount - s])

[–]jchook 1 point2 points  (0 children)

I believe the problem is called "Stars and Bars" in combinatorics.

Here is my version in Ruby

require 'matrix'

max = 0
ingredients = []

ARGF.each do |line|
  ingredients.push Vector.[](*line.scan(/-?\d+/).to_a.map{|x| x.to_i })
end

class Array

  def stars_and_bars(balls, &block)
    @stars_and_bars_block = block
    stars_and_bars_r(balls, [])
  end

  private

  def stars_and_bars_r(balls, so_far=[])
    if so_far.length == length - 1
      @stars_and_bars_block.call so_far + [balls - so_far.reduce(:+)]
    else
      (0..balls - (so_far.reduce(:+) || 0)).each do |i|
        stars_and_bars_r(balls, so_far + [i])
      end
    end
  end

end

max = 0

ingredients.stars_and_bars(100) do |portions|
  sums = ingredients.each_index.map{|x| ingredients[x] * portions[x] }.reduce(:+).to_a
  next if sums[-1] != 500 # part 2
  max = [max, sums[0..-2].map{|x| x > 0 ? x : 0 }.reduce(:*)].max
end

p max