aboutsummaryrefslogtreecommitdiff
path: root/2025
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-12-10 18:15:14 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2025-12-10 18:15:14 +0100
commitb52eef5b9399ab3888fcfe1bc9ca23c7a9f7b026 (patch)
treedf3c5525c55e5d21ce07b9bab2359468cd9827a3 /2025
parent4dc2097b38697614dba7a58bf2ec53e1e54e52fe (diff)
downloadaoc-b52eef5b9399ab3888fcfe1bc9ca23c7a9f7b026.tar.gz
aoc-b52eef5b9399ab3888fcfe1bc9ca23c7a9f7b026.zip
Day 10 2025
Diffstat (limited to '2025')
-rw-r--r--2025/10/a.py35
-rw-r--r--2025/10/b-debug.py133
-rw-r--r--2025/10/b-recursive-slow.py66
-rw-r--r--2025/10/b.py125
-rw-r--r--2025/README.md6
5 files changed, 363 insertions, 2 deletions
diff --git a/2025/10/a.py b/2025/10/a.py
index 5fdb11b..52fa0b0 100644
--- a/2025/10/a.py
+++ b/2025/10/a.py
@@ -1,6 +1,37 @@
1import fileinput 1import fileinput
2 2
3def readline(line):
4 m = 0
5 for i in range(1, line.index(']')):
6 if line[i] == '#':
7 m |= 1 << (i-1)
8
9 b = []
10 j = line.index(']')
11 while '(' in line[j:]:
12 i = line[j:].index('(') + j + 1
13 j = line[i:].index(')') + i
14 x = 0
15 for y in line[i:j].split(','):
16 x |= 1 << int(y)
17 b.append(x)
18
19 return m, b
20
21def works(m, b, s):
22 x = 0
23 for j in range(len(s)):
24 if s[j] == '1':
25 x ^= b[j]
26 return x == m
27
28def sol(m, b):
29 s = 99999999
30 for i in range(2**len(b)):
31 if works(m, b, bin(i)[2:].rjust(len(b), '0')):
32 s = min(s, i.bit_count())
33 return s
34
3with fileinput.input() as lines: 35with fileinput.input() as lines:
4 for line in lines: 36 print(sum(sol(*readline(line)) for line in lines))
5 ...
6 37
diff --git a/2025/10/b-debug.py b/2025/10/b-debug.py
new file mode 100644
index 0000000..e6f9961
--- /dev/null
+++ b/2025/10/b-debug.py
@@ -0,0 +1,133 @@
1import fileinput
2from math import gcd
3
4inf = 999999999
5
6def readline(l):
7 b = [int(x) for x in l[l.index('{')+1:l.index('}')].split(',')]
8
9 A = [[] for j in b]
10 c = []
11
12 end = l.index(']')
13 while '(' in l[end:]:
14 begin = l[end:].index('(') + end + 1
15 end = l[begin:].index(')') + begin
16 bu = [int(x) for x in l[begin:end].split(',')]
17 c.append(min(b[j] for j in bu))
18 for i in range(len(b)):
19 A[i].append(1 if i in bu else 0)
20
21 return A, b, c
22
23def printabc(A, b, c):
24 print("--")
25 for i in range(len(b)):
26 print(A[i], [b[i]])
27 print(f"Parameter bounds: {c}")
28 print("--")
29
30def swaprow(A, b, i, j):
31 if i != j:
32 A[i], A[j] = A[j], A[i]
33 b[i], b[j] = b[j], b[i]
34
35def swapcol(A, c, i, j):
36 if i != j:
37 for k in range(len(A)):
38 A[k][i], A[k][j] = A[k][j], A[k][i]
39 c[i], c[j] = c[j], c[i]
40
41def reducerow(A, b, i, j):
42 if A[i][i] != 0:
43 x = A[i][i]
44 y = -A[j][i]
45 d = gcd(x, y)
46 A[j] = [(y*A[i][k]+x*A[j][k])//d for k in range(len(A[i]))]
47 b[j] = (y*b[i]+x*b[j])//d
48
49def reduce(A, b, c):
50 for i in range(len(A[0])):
51 # Swap columns until there is one in position i with at least
52 # one non-zero element.
53 I = []
54 k = i
55 while len(I) == 0 and k < len(A[0]):
56 swapcol(A, c, i, k)
57 I = [j for j in range(i, len(A)) if A[j][i] != 0]
58 k += 1
59
60 # If no such column is found, we are done
61 if len(I) == 0:
62 break
63
64 # Swap rows so that A[i][i] is non-zero
65 swaprow(A, b, i, I[0])
66
67 # Reduce all other rows
68 for j in range(i+1, len(A)):
69 reducerow(A, b, i, j)
70
71 # Remove all rows of zero and check if the system is solvable
72 I = [i for i in range(len(A)) if any(a != 0 for a in A[i])]
73 if any(b[i] != 0 for i in range(len(A)) if i not in I):
74 printabc(A, b, c)
75 print("Unsolvable!")
76 exit(1)
77 A = [A[i] for i in I]
78 b = [b[i] for i in I]
79
80 # TODO continue with back substitution?
81 for i in range(len(A)-1, -1, -1):
82 for j in range(i):
83 reducerow(A, b, i, j)
84
85 # Clean all rows to minimize coefficients (unnecessary, but makes
86 # numbers smaller).
87 for i in range(len(A)):
88 d = gcd(*A[i]) * (-1 if A[i][i] < 0 else 1)
89 A[i] = [A[i][k]//d for k in range(len(A[i]))]
90 b[i] = b[i]//d
91
92 return A, b, c
93
94def paramcomb(nparam, c):
95 if nparam == 0:
96 return [[]]
97
98 ret = []
99 for i in range(c[-nparam]+1):
100 ret += [[i, *l] for l in paramcomb(nparam-1, c)]
101 return ret
102
103def solve_system_min_sum(A, b, c):
104 #nparam = len(A[0])-len(A)
105 #print(f"{nparam}: {paramcomb(nparam, c)}")
106
107 k = len(A[0]) - len(A)
108 mins = inf
109 for c in paramcomb(k, c):
110 sol = sum(c)
111 for i in range(len(A)):
112 p = sum(c[j]*A[i][len(A[0])-k+j] for j in range(len(c)))
113 a = (b[i] - p)//A[i][i]
114 if a < 0 or a*A[i][i] != b[i] - p:
115 sol = inf
116 break
117 sol += a
118 mins = min(mins, sol)
119 return mins
120
121with fileinput.input() as lines:
122 sols = []
123 k = 1
124 for line in lines:
125 print(f"doing line {k}: {line[:-1]}")
126 A, b, c = readline(line)
127 #printabc(A, b, c)
128 A, b, c = reduce(A, b, c)
129 #printabc(A, b, c)
130 sols.append(solve_system_min_sum(A, b, c))
131 k += 1
132 print(sols)
133 print(sum(sols))
diff --git a/2025/10/b-recursive-slow.py b/2025/10/b-recursive-slow.py
new file mode 100644
index 0000000..a52b50e
--- /dev/null
+++ b/2025/10/b-recursive-slow.py
@@ -0,0 +1,66 @@
1import fileinput
2from functools import cache
3
4def readline(line):
5 b = []
6 j = line.index(']')
7 while '(' in line[j:]:
8 i = line[j:].index('(') + j + 1
9 j = line[i:].index(')') + i
10 x = 0
11 for y in line[i:j].split(','):
12 x |= 1 << int(y)
13 b.append(x)
14
15 begin = line.index('{')+1
16 end = line.index('}')
17 j = tuple(int(x) for x in line[begin:end].split(','))
18
19 return tuple(b), j
20
21def newj(j, b, n):
22 return tuple(j[i]-n if b & (1<<i) else j[i] for i in range(len(j)))
23
24def can(b, i):
25 return b & (1 << i) != 0
26
27inf = 99999999999
28@cache
29def sol(b, j, best):
30 print(f"{b} {j} {best}")
31 if all(x == 0 for x in j):
32 return 0
33 if any(x < 0 for x in j):
34 return inf
35 if len(b) == 0:
36 return inf
37 for i in range(len(j)):
38 if j[i] > 0 and not any(can(bb, i) for bb in b):
39 return inf
40
41 # Optimization: if any of the buttons is the last one available that
42 # toggles a certain jolt, we press it as much as needed.
43 for i in range(len(j)):
44 for k in range(len(b)):
45 bb = b[:k] + b[k+1:]
46 if can(b[k], i) and not any(can(x, i) for x in bb):
47 return j[i] + sol(bb, newj(j, b[k], j[i]), best)
48
49 c = 0
50 while all(x >= 0 for x in j) and c <= best:
51 best = min(best, c + sol(b[1:], j, best))
52 j = newj(j, b[0], 1)
53 c += 1
54
55 return best
56
57with fileinput.input() as lines:
58 sols = []
59 c = 1
60 for line in lines:
61 print(f"doing line {c}: {line}")
62 c += 1
63 sols.append(sol(*readline(line), inf))
64 print(sols)
65 print(sum(sols))
66
diff --git a/2025/10/b.py b/2025/10/b.py
new file mode 100644
index 0000000..57fff46
--- /dev/null
+++ b/2025/10/b.py
@@ -0,0 +1,125 @@
1import fileinput
2from math import gcd
3
4inf = 999999999
5
6# Read an input line and return a triple A, b, c where:
7# A is the matrix of coefficient (A[i][j] is 1 if pressing the j-th button
8# increases the i-th counter, 0 otherwise).
9# b is the list of required final values of each counter.
10# c is a list of bounds for the number of times each button can be pressed:
11# c[i] is the minimum required value of a counter increased by button i.
12def readline(l):
13 b = [int(x) for x in l[l.index('{')+1:l.index('}')].split(',')]
14
15 A = [[] for j in b]
16 c = []
17
18 end = l.index(']')
19 while '(' in l[end:]:
20 begin = l[end:].index('(') + end + 1
21 end = l[begin:].index(')') + begin
22 bu = [int(x) for x in l[begin:end].split(',')]
23 c.append(min(b[j] for j in bu))
24 for i in range(len(b)):
25 A[i].append(1 if i in bu else 0)
26
27 return A, b, c
28
29# swaprow, swapcol and reducerow are utility functions for the matrix
30# row reduction procedure.
31def swaprow(A, b, i, j):
32 if i != j:
33 A[i], A[j] = A[j], A[i]
34 b[i], b[j] = b[j], b[i]
35
36def swapcol(A, c, i, j):
37 if i != j:
38 for k in range(len(A)):
39 A[k][i], A[k][j] = A[k][j], A[k][i]
40 c[i], c[j] = c[j], c[i]
41
42def reducerow(A, b, i, j):
43 if A[i][i] != 0:
44 x = A[i][i]
45 y = -A[j][i]
46 d = gcd(x, y)
47 A[j] = [(y*A[i][k]+x*A[j][k])//d for k in range(len(A[i]))]
48 b[j] = (y*b[i]+x*b[j])//d
49
50# Reduce the matrix A by row, including back subsitution. Returns a triple
51# A, b, c where:
52# A is the reduced matrix in the form [D|p] with D diagonal and p a matrix
53# whose number of columns is the number of free parameters for Ax = b.
54# b and c are the adjusted versions of the input paramters with the same
55# names. In particular, b is adjusted every time a row is reduced and
56# when rows are swapped, while c (the list of bounds) is adjusted when
57# two columns are swapped (which is equivalent to changing the order of
58# two buttons).
59def reduce(A, b, c):
60 for i in range(len(A[0])):
61 # Swap columns until there is one in position i with at least
62 # one non-zero element.
63 I = []
64 k = i
65 while len(I) == 0 and k < len(A[0]):
66 swapcol(A, c, i, k)
67 I = [j for j in range(i, len(A)) if A[j][i] != 0]
68 k += 1
69
70 # If no such column is found, we are done
71 if len(I) == 0:
72 break
73
74 # Swap rows so that A[i][i] is non-zero
75 swaprow(A, b, i, I[0])
76
77 # Reduce all other rows
78 for j in range(i+1, len(A)):
79 reducerow(A, b, i, j)
80
81 # Remove all rows of zero and check if the system is solvable
82 I = [i for i in range(len(A)) if any(a != 0 for a in A[i])]
83 A = [A[i] for i in I]
84 b = [b[i] for i in I]
85
86 # Back substitution
87 for i in range(len(A)-1, -1, -1):
88 for j in range(i):
89 reducerow(A, b, i, j)
90
91 return A, b, c
92
93# Find all combinations of parameters respecting the bounds in c. The
94# free parameters are always the last n-rank(A) columns of the matrix A
95# (where n is the total number of columns).
96def paramcomb(nparam, c):
97 if nparam == 0:
98 return [[]]
99
100 ret = []
101 for i in range(c[-nparam]+1):
102 ret += [[i, *l] for l in paramcomb(nparam-1, c)]
103 return ret
104
105# Solve the integer linear system Ax = b, minimizing the sum of the
106# coordinates of x.
107def solve_system_min_sum(A, b, c):
108 k = len(A[0]) - len(A)
109 mins = inf
110 for c in paramcomb(k, c):
111 sol = sum(c)
112 for i in range(len(A)):
113 cc = (c[j]*A[i][len(A[0])-k+j] for j in range(len(c)))
114 s = b[i]-sum(cc)
115 a = s // A[i][i]
116 # If a is negative or not integer, we skip this solution
117 if a < 0 or s % A[i][i] != 0:
118 sol = inf
119 break
120 sol += a
121 mins = min(mins, sol)
122 return mins
123
124with fileinput.input() as lines:
125 print(sum(solve_system_min_sum(*reduce(*readline(l))) for l in lines))
diff --git a/2025/README.md b/2025/README.md
index d75a4d3..dc87240 100644
--- a/2025/README.md
+++ b/2025/README.md
@@ -11,6 +11,7 @@ Example
11 11
12``` 12```
13Day -Part 1- -Part 2- 13Day -Part 1- -Part 2-
14 10 00:27:43 11:51:51
14 9 00:05:05 02:11:41 15 9 00:05:05 02:11:41
15 8 00:29:14 00:33:02 16 8 00:29:14 00:33:02
16 7 00:05:27 00:20:40 17 7 00:05:27 00:20:40
@@ -173,3 +174,8 @@ For example with this input:
173The first version of my program return 4851 instead of 202. I added a 174The first version of my program return 4851 instead of 202. I added a
174check for this case in `b-fixed.py`, hopefully it works in 100% of the 175check for this case in `b-fixed.py`, hopefully it works in 100% of the
175cases now. 176cases now.
177
178### Day 10: Factory
179
180Phew, this was a hard one!
181(More details coming soon, for now read the code and the comments)

Generated with cgit - Back to sebastiano.tronto.net