Optimise 2024.07

This commit is contained in:
akp 2024-12-07 12:50:06 +00:00
parent b2fa4b72b6
commit 82ffa429d7
No known key found for this signature in database
GPG key ID: CF8D58F3DEB20755
5 changed files with 50 additions and 46 deletions

View file

@ -3,7 +3,7 @@
Part 1 is: Part 1 is:
* greater than 450054910499 * greater than 450054910499
Before optimisation (pregenerating then testing operator combiantions using `itertools.product("*+|", length=n)`), runtime looks something like this: Before optimisation (pregenerating then testing operator combiantions using `itertools.product("*+|", length=n)`), run time looks something like this:
``` ```
Part 1: min 0.2671 seconds, max 0.2818 seconds, avg 0.2755 Part 1: min 0.2671 seconds, max 0.2818 seconds, avg 0.2755
@ -12,3 +12,11 @@ Part 2: min 23.2387 seconds, max 24.8753 seconds, avg 23.8805
It also appeared that using string concatenation as opposed to pure mathematical functions for the concatenation operator was marginally faster in Python. It also appeared that using string concatenation as opposed to pure mathematical functions for the concatenation operator was marginally faster in Python.
After optimisation (recursive solves working backwards through the numbers), the run time looks something like this:
```
Part 1: min 0.0214 seconds, max 0.041 seconds, avg 0.0233
Part 2: min 0.0215 seconds, max 0.0273 seconds, avg 0.0229
```
The intial version of this solve is in commit `b2fa4b7`.

View file

@ -14,58 +14,51 @@ def parse(instr: str) -> list[tuple[int, list[int]]]:
return res return res
def evaluate(ns: list[int], ops: Iterable[str]): def ends_with(x: int, y: int) -> bool:
acc = ns[0] ycard = int(math.log10(y)) + 1
for i, (v, op) in enumerate(zip(ns[1:], ops)): return (x - y) * (10**-ycard) == int(x * (10**-ycard))
if op == "*":
acc *= v
elif op == "+": def trim_int(x: int, y: int) -> int:
acc += v ycard = int(math.log10(y)) + 1
elif op == "|": return int((x - y) * (10**-ycard))
acc = (acc * (10 ** int(math.log10(v) + 1))) + v
else:
raise ValueError(f"unknown operation {op}") def solve(target: int, ns: list[int], use_concat: bool = False) -> bool:
return acc v = ns[-1]
rest = ns[:-1]
if len(rest) == 0:
return target == v
if target % v == 0:
# this represents a possible multiplication
if solve(int(target / v), rest, use_concat):
return True
if use_concat and ends_with(target, v):
# this is a possible concatenation
if solve(trim_int(target, v), rest, use_concat):
return True
# last resort, addition
return solve(target - v, rest, use_concat)
def one(instr: str): def one(instr: str):
cases = parse(instr) cases = parse(instr)
return itertools.accumulate(
cached_ops = {} target if solve(target, numbers, use_concat=False) else 0
for (target, numbers) in cases
n = 0 )
for (target, numbers) in cases:
num_ops = len(numbers) - 1
if num_ops not in cached_ops:
cached_ops[num_ops] = tuple(itertools.product("+*", repeat=num_ops))
for ops in cached_ops[num_ops]:
v = evaluate(numbers, ops)
if v == target:
n += v
break
return n
def two(instr: str): def two(instr: str):
cases = parse(instr) cases = parse(instr)
return itertools.accumulate(
cached_ops = {} target if solve(target, numbers, use_concat=True) else 0
for (target, numbers) in cases
n = 0 )
for (target, numbers) in cases:
num_ops = len(numbers) - 1
if num_ops not in cached_ops:
cached_ops[num_ops] = tuple(itertools.product("+*|", repeat=num_ops))
for ops in cached_ops[num_ops]:
v = evaluate(numbers, ops)
if v == target:
n += v
break
return n
def _debug(*args, **kwargs): def _debug(*args, **kwargs):

View file

@ -19,4 +19,5 @@ A day denoted with an asterisk means it has a visualisation.
| 03 - Mull It Over | ★ ★ | Python | The first instance of Advent of Parsing this year! | | 03 - Mull It Over | ★ ★ | Python | The first instance of Advent of Parsing this year! |
| 04* - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. | | 04* - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. |
| 05 - Print Queue | ★ ★ | Python | Before you dismiss and idea as being "too simple", make sure you check that it doesn't work. | | 05 - Print Queue | ★ ★ | Python | Before you dismiss and idea as being "too simple", make sure you check that it doesn't work. |
| 06 - Guard Gallivant | ★ ★ | Python | oh dear runtime (also I knew what I wanted to do for so long it just took me 3 hours to implement it properly) | | 06 - Guard Gallivant | ★ ★ | Python | oh dear runtime (also I knew what I wanted to do for so long it just took me 3 hours to implement it properly) |
| 07 - Bridge Repair | ★ ★ | Python | Maths? Backwards?? |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

View file

@ -12,3 +12,5 @@
{"day": 6, "part": 2, "runner": "py", "min": 15.881408452987671, "max": 17.086341857910156, "avg": 16.64130985736847, "n": 6} {"day": 6, "part": 2, "runner": "py", "min": 15.881408452987671, "max": 17.086341857910156, "avg": 16.64130985736847, "n": 6}
{"day": 7, "part": 1, "runner": "py", "min": 0.26709485054016113, "max": 0.28178858757019043, "avg": 0.2754525661468506, "n": 5} {"day": 7, "part": 1, "runner": "py", "min": 0.26709485054016113, "max": 0.28178858757019043, "avg": 0.2754525661468506, "n": 5}
{"day": 7, "part": 2, "runner": "py", "min": 23.23872661590576, "max": 24.87530255317688, "avg": 23.880544805526732, "n": 5} {"day": 7, "part": 2, "runner": "py", "min": 23.23872661590576, "max": 24.87530255317688, "avg": 23.880544805526732, "n": 5}
{"day": 7, "part": 1, "runner": "py", "min": 0.02138209342956543, "max": 0.04097461700439453, "avg": 0.023260540962219238, "n": 100}
{"day": 7, "part": 2, "runner": "py", "min": 0.021509647369384766, "max": 0.027263402938842773, "avg": 0.022869422435760497, "n": 100}