mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-26 21:30:31 +01:00
Improve solution for day 17.
This commit is contained in:
26
day-17/README.md
Normal file
26
day-17/README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Day 17 efficient solution
|
||||||
|
|
||||||
|
Day 17 is an instance of the subset sum problem. This problem asks whether for
|
||||||
|
a (multi)set of integers *V*, there is a non-empty subset of integers summing up to
|
||||||
|
exactly *s*. This problem is NP-complete.
|
||||||
|
|
||||||
|
The brute force approach of this is trying every possible set in the powerset
|
||||||
|
of *V* to see if they match. This is inpractical however, because a powerset of
|
||||||
|
a set of size *n* contains 2<sup>2</sup> sets.
|
||||||
|
|
||||||
|
In the exercise, we have 20 buckets, and 2<sup>20</sup> is still
|
||||||
|
brute-forcable. There is a smarter approach.
|
||||||
|
|
||||||
|
We split the list of buckets in two lists of (approximately) the same size. We
|
||||||
|
then take the powersets of those two lists and compute the sum for each entry.
|
||||||
|
This leaves us with a total of 2<sup>n / 2 + 1</sup> entries. We then sort both
|
||||||
|
sublists on the total value of each entry.
|
||||||
|
|
||||||
|
Finally, we iterate of the first list, and use binary search to see whether
|
||||||
|
there is an appropriately sized entry in the second list. This gives us a final
|
||||||
|
complexity of *n* times 2<sup>*n*/2</sup>, allowing the solution to be computed
|
||||||
|
instantly.
|
||||||
|
|
||||||
|
The algorithm above can be modified to find all combinations, not just one, in
|
||||||
|
time proportional to the number of solutions. This is implemented in the final
|
||||||
|
program.
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
from __future__ import print_function, division
|
from __future__ import print_function, division
|
||||||
import fileinput
|
import fileinput
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import bisect
|
||||||
|
|
||||||
buckets = []
|
def value(buckets, choice):
|
||||||
|
total = 0
|
||||||
|
for value in buckets:
|
||||||
|
if choice % 2 == 1:
|
||||||
|
total += value
|
||||||
|
|
||||||
for line in fileinput.input():
|
choice //= 2
|
||||||
buckets.append(int(line))
|
|
||||||
|
|
||||||
def works(bucketCombination, target):
|
return total
|
||||||
for idx, value in enumerate(buckets):
|
|
||||||
|
|
||||||
if bucketCombination % 2 == 1:
|
|
||||||
target -= value
|
|
||||||
|
|
||||||
bucketCombination = bucketCombination // 2
|
|
||||||
|
|
||||||
return target == 0
|
|
||||||
|
|
||||||
def ones(x):
|
def ones(x):
|
||||||
n = 0
|
n = 0
|
||||||
@@ -27,11 +23,40 @@ def ones(x):
|
|||||||
|
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
def partition(a_list):
|
||||||
|
pivot = len(a_list) // 2
|
||||||
|
|
||||||
|
return a_list[:pivot], a_list[pivot:]
|
||||||
|
|
||||||
|
def partitionList(buckets):
|
||||||
|
result = [(value(buckets, x), ones(x)) for x in range(1 << len(buckets))]
|
||||||
|
result.sort()
|
||||||
|
return result
|
||||||
|
|
||||||
|
buckets = []
|
||||||
|
|
||||||
|
for line in fileinput.input():
|
||||||
|
buckets.append(int(line))
|
||||||
|
|
||||||
|
partition1, partition2 = partition(buckets)
|
||||||
|
|
||||||
|
values1 = partitionList(partition1)
|
||||||
|
values2 = partitionList(partition2)
|
||||||
|
|
||||||
possible = defaultdict(lambda: 0)
|
possible = defaultdict(lambda: 0)
|
||||||
|
|
||||||
for x in range(1 << len(buckets)):
|
i = 0
|
||||||
if works(x, 150):
|
|
||||||
n = ones(x)
|
target = 150
|
||||||
possible[n] += 1
|
|
||||||
|
for entry in values1:
|
||||||
|
|
||||||
|
i = bisect.bisect_left(values2, (target - entry[0], 0))
|
||||||
|
|
||||||
|
while i < len(values2) and entry[0] + values2[i][0] == target:
|
||||||
|
possible[entry[1] + values2[i][1]] += 1
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
print("Total possibilities:", sum(possible.values()))
|
||||||
|
print("Minimal possibilities:", possible[min(possible.keys())])
|
||||||
|
|
||||||
print(sum(possible[x] for x in possible), possible[min(possible.keys())])
|
|
||||||
|
|||||||
Reference in New Issue
Block a user