This commit is contained in:
2024-12-08 21:36:04 +01:00
parent 9ef0275a61
commit 0ba1f371ed
18 changed files with 1016 additions and 115 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 122 KiB

20
2024/05/findings.md Normal file
View File

@@ -0,0 +1,20 @@
# Day 5
# Part 1
What a joy! First day where a bit of Computer Science might actually help.
So to me this looks like a [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph), since if there has to be an order, cycles cannot be allowed. Otherwise the problem could not be solved.
For not I just hope that it's one cohesive DAG, and not 2 or more. Technically there is a possibility that more than one, disjoined DAGs are defined by the rules. If this occurs, we'll see.
![DAG Image](./day_5_dag_test.svg)
We can see that there in this case there is one "start" and one "end". In this case `97` is the start and `13` the end. Again, there might be more than one "start" or "end".
My idea is to precompile an order so that `97=0` and `13=7` so that we then just map the numbers to their ascending number and check if the array is already sorted. Why? My assumption is that you do the work upfront and then it's efficient to check for each input.
The start and ends are easy, what about the _middle_ nodes? We can do a so called transitive reduction, simplifying the graph. Basically, if we know that `97` needs to be before `61` and `13` and at the same time `61` needs to be before `13`, we can delete the `97` before `13` link, as it's already specified going over `61`. Basically we eliminate all the "shortcuts". This gives us a nice line in this case.
![reduced DAG](./day_5_dag_reduced.svg)
_no code has been written until now, this might go terribly wrong 💩_

95
2024/05/main.py Normal file
View File

@@ -0,0 +1,95 @@
from typing import List, Union, Tuple, Set
from dataclasses import dataclass
import networkx as nx
import matplotlib.pyplot as plt
@dataclass
class Node:
id: int
links: List
@dataclass
class Manual:
rules: Tuple[int]
updates: Tuple[int]
def check_update(self, update: Tuple[int]) -> bool:
cur = 0
for n in update:
if n not in self.order:
continue
m = self.order[n]
if m < cur:
return False
cur = m
return True
def check(self):
total = 0
for update in self.updates:
valid = self.check_update(update)
if valid:
total += update[len(update) // 2]
return total
def build_rules(self):
unique: Set[int] = set()
for rule in self.rules:
unique.add(rule[0])
unique.add(rule[1])
nodes = {n: Node(id=n, links=[]) for n in unique}
for start, end in self.rules:
nodes[start].links.append(end)
# Test
G = nx.DiGraph()
for rule in self.rules:
G.add_edge(*rule)
print(G.number_of_nodes())
TR = nx.transitive_reduction(G)
subax1 = plt.subplot(121)
nx.draw(TR, with_labels=True, font_weight="bold")
plt.show()
sorted = list(nx.topological_sort(G))
print(sorted)
# Topological sorting
# https://cs.stackexchange.com/a/29133
order = {n: i for i, n in enumerate(sorted)}
self.order = order
@staticmethod
def parse(raw: str):
rules_raw, updates_raw = raw.strip().split("\n\n")
rules = [tuple((map(int, line.split("|")))) for line in rules_raw.splitlines()]
updates = [
tuple(map(int, line.split(","))) for line in updates_raw.splitlines()
]
return Manual(rules, updates)
def solve(raw: str) -> int:
# Part 1
part1 = 0
part2 = 0
m = Manual.parse(raw)
m.build_rules()
part1 = m.check()
return (part1, part2)
# Test
with open("./2024/05/test.txt", "r") as f:
result = solve(f.read().strip())
print(result)
# Input
with open("./2024/05/input.txt", "r") as f:
result = solve(f.read().strip())
print(result)