Day 14: much quicker algo

This commit is contained in:
Dece 2019-12-15 15:18:42 +01:00
parent 6677498314
commit eb858068dc

View file

@ -1,22 +1,34 @@
import collections
import math import math
import re import re
EX1 = [
"10 ORE => 10 A",
"1 ORE => 1 B",
"7 A, 1 B => 1 C",
"7 A, 1 C => 1 D",
"7 A, 1 D => 1 E",
"7 A, 1 E => 1 FUEL",
]
def main(): def main():
with open("day14.txt", "rt") as input_file: with open("day14.txt", "rt") as input_file:
lines = [line.rstrip() for line in input_file.readlines()] lines = [line.rstrip() for line in input_file.readlines()]
line_re = re.compile(r"(.+) => (.+)") line_re = re.compile(r"(.+) => (.+)")
raw_transmos = [line_re.match(line).groups() for line in lines] raw_reactions = [line_re.match(line).groups() for line in lines]
transmos = {} reactions = {}
for inputs, outputs in raw_transmos: for inputs, outputs in raw_reactions:
mult, result = outputs.split(" ") mult, result = outputs.split(" ")
input_comps = [i.split(" ") for i in inputs.split(", ")] input_comps = [i.split(" ") for i in inputs.split(", ")]
input_tuples = [(int(q), n) for q, n in input_comps] input_tuples = [(int(q), n) for q, n in input_comps]
transmos[result] = int(mult), input_tuples reactions[result] = int(mult), input_tuples
# Part 1 # Part 1
ore_per_full = get_required_ore(transmos, "FUEL", 1, {}) ore_per_full = get_required_ore(reactions, 1)
print("Required ore for 1 FUEL:", ore_per_full) print("Required ore for 1 FUEL:", ore_per_full)
return
# Part 2 # Part 2
# Input gives 397771 OPF. # Input gives 397771 OPF.
@ -30,7 +42,7 @@ def main():
# 10^12 / x / 397771 = 0.985 -> x = 2552294 # 10^12 / x / 397771 = 0.985 -> x = 2552294
# Now on to our search. # Now on to our search.
fuel = 2552294 fuel_q = 2552294
cursor = 10000 cursor = 10000
last_result = 0 last_result = 0
min_target = 10**12 - ore_per_full min_target = 10**12 - ore_per_full
@ -38,8 +50,8 @@ def main():
was_below = False was_below = False
was_above = False was_above = False
while last_result not in range(min_target, max_target): while last_result not in range(min_target, max_target):
last_result = get_required_ore(transmos, "FUEL", fuel, {}) last_result = get_required_ore(reactions, fuel_q)
print("{} FUEL requires {} ORE.".format(fuel, last_result)) print("{} FUEL requires {} ORE.".format(fuel_q, last_result))
if last_result < min_target: if last_result < min_target:
if was_above: if was_above:
cursor /= 2 cursor /= 2
@ -49,30 +61,34 @@ def main():
cursor /= 2 cursor /= 2
fuel -= cursor fuel -= cursor
def get_required_ore(transmos, name, quantity, leftovers): def get_required_ore(reactions, quantity):
if name == "ORE": stored_map = collections.defaultdict(int)
return quantity needs = [("FUEL", quantity)]
ore_per_full = 0
mult, comps = transmos[name] while needs:
num_reactions = math.ceil(quantity / mult) needed, needed_q = needs.pop(0)
print(f"Need {needed_q} {needed}.")
created = 0 if needed == "ORE":
if name in leftovers: print(f"- {needed_q} ORE required.")
created += leftovers[name] ore_per_full += needed_q
leftovers[name] = 0 continue
react_mult, subcomps = reactions[needed]
req_ore = 0 if stored_map[needed]:
for _ in range(num_reactions): print(f"- Already got {stored_map[needed]} in storage.")
if created >= quantity: needed_q -= stored_map[needed]
break stored_map[needed] = 0
req_ore += sum(get_required_ore(transmos, rn, rq, leftovers) for rq, rn in comps) num_reacts = math.ceil(needed_q / react_mult)
created += mult print(f"- Requires {num_reacts} reaction(s) ({react_mult} {needed} per R)")
if num_reacts:
if created > quantity: new_needs = [(name_sc, num_sc * num_reacts) for num_sc, name_sc in subcomps]
leftover = created - quantity print(f"- New needs: {new_needs}")
leftovers[name] = leftovers.get(name, 0) + leftover needs += new_needs
produced = react_mult * num_reacts
return req_ore print(f"- Produced {produced} {needed}.")
if produced - needed_q > 0:
print(f"- Stored {produced - needed_q} {needed} leftovers.")
stored_map[needed] += produced - needed_q
return ore_per_full
if __name__ == "__main__": if __name__ == "__main__":