this repo has no description
1<!-- vim:ft=markdown --> 2 3<!-- livebook:{"persist_outputs":true} --> 4 5# Day 19 6 7## Section 8 9```elixir 10# File.read!("day19.txt") 11input = 12 File.read!("day19.txt") 13 |> String.split("\n\n") 14 |> Enum.map(fn scanner -> 15 scanner 16 |> String.split("\n", trim: true) 17 |> Enum.drop(1) 18 |> Enum.map(fn coords -> 19 coords 20 |> String.split(",") 21 |> Enum.map(&String.to_integer/1) 22 |> Enum.zip(~w[x y z]a) 23 |> Map.new(fn {v, k} -> {k, v} end) 24 end) 25 |> MapSet.new() 26 end) 27 28length(input) 29``` 30 31```output 3234 33``` 34 35$$ 36\left\{ 37\begin{alignat*}{3} 38b_{x_1} r_x + b_{x_0} = a_{x_1} \\ 39b_{x_2} r_x + b_{x_0} = a_{x_2} 40\end{alignat*} 41\right. 42\left\{ 43\begin{alignat*}{3} 44b_{y_1} r_y + b_{y_0} = a_{y_1} \\ 45b_{y_2} r_y + b_{y_0} = a_{y_2} \\ 46\end{alignat*} 47\right. 48\left\{ 49\begin{alignat*}{3} 50b_{z_1} r_z + b_{z_0} = a_{z_1} \\ 51b_{z_2} r_z + b_{z_0} = a_{z_2} \\ 52\end{alignat*} 53\right. 54$$ 55 56Where $$r_x, r_y, r_z \in \{-1, 1\}$$ and $$a_{xyz}, b_{xyz} = \text{const}$$. 57This mean that we need to solve: 58 59$$ 60B_x x + b_0 = a_x \\ 61B_y y + b_0 = a_y \\ 62B_z z + b_0 = a_z 63$$ 64 65Where: 66 67$$ 68B_d = \begin{bmatrix} 69{b_1}_d & 1 \\ 70{b_2}_d & 1 \\ 71\end{bmatrix} 72d = \begin{bmatrix} 73r_d \\ 74b_{d_0} 75\end{bmatrix} 76a_d = \begin{bmatrix} 77{a_1}_d \\ 78{a_2}_d 79\end{bmatrix} 80$$ 81 82By applying [Cramer's Rule](https://en.wikipedia.org/wiki/Cramer%27s_rule) we can solve these 83linear equations with: 84 85$$ 86e_d = \frac{ 87\begin{vmatrix} 88{b_1}_d & 1 \\ 89{b_2}_d & 1 90\end{vmatrix} 91}{ 92\begin{vmatrix} 93{a_1}_d & 1 \\ 94{a_2}_d & 1 95\end{vmatrix} 96} = \frac{{b_1}_d - {b_2}_d}{{a_1}_d - {a_2}_d} \\ 97b_{d_0} = \frac{ 98 \begin{vmatrix} 99{a_1}_d & {b_1}_d \\ 100{a_2}_d & {b_2}_d 101\end{vmatrix} 102}{ 103 \begin{vmatrix} 104{a_1}_d & 1 \\ 105{a_2}_d & 1 106\end{vmatrix} 107} = \frac{{a_1}_d {b_2}_d - {b_1}_d {a_2}_d}{{a_1}_d - {a_2}_d} 108 109```elixir 110defmodule Day19 do 111 def rebase(base, pairs1) do 112 pairs0 = pairs(base) 113 # Take common points 114 pa = Map.take(pairs0, Map.keys(pairs1)) 115 pb = Map.take(pairs1, Map.keys(pairs0)) 116 117 # Merge them 118 [{{a, b, axes1}, {_, _, axes2}} = p0 | others] = 119 Map.merge(pa, pb, fn _, a, b -> {a, b} end) 120 |> Map.values() 121 122 p1 = Enum.find(others, fn {{x, y, _}, _} -> a in [x, y] end) 123 p2 = Enum.find(others, fn {{x, y, _}, _} -> b in [x, y] end) 124 125 axes = axes_transformations(axes1, axes2) 126 127 a_1 = a 128 a_2 = b 129 b_1 = reaxe(select_common(p0, p1), axes) 130 b_2 = reaxe(select_common(p0, p2), axes) 131 132 transform = 133 for i <- ~w[x y z]a, into: %{} do 134 a_1 = a_1[i] 135 a_2 = a_2[i] 136 b_1 = b_1[i] 137 b_2 = b_2[i] 138 det_b = b_1 - b_2 139 r = div(a_1 - a_2, det_b) 140 141 b_0 = div(b_1 * a_2 - a_1 * b_2, det_b) 142 143 {i, {r, b_0}} 144 end 145 146 new_points = 147 pairs1 148 |> Map.values() 149 |> Enum.flat_map(&[elem(&1, 0), elem(&1, 1)]) 150 |> Enum.uniq() 151 |> Enum.map(&reaxe(&1, axes)) 152 |> do_rebase(transform) 153 |> MapSet.new() 154 155 %{x: {_, sx}, y: {_, sy}, z: {_, sz}} = transform 156 scanner = %{x: sx, y: sy, z: sz} 157 158 {new_points, scanner} 159 end 160 161 defp do_rebase(points, transform) do 162 points 163 |> Enum.map(fn p -> 164 Map.merge(p, transform, fn _k, d, {r, b} -> d * r + b end) 165 end) 166 |> MapSet.new() 167 end 168 169 defp select_common(a, b) do 170 case {a, b} do 171 {{_, {_, x, _}}, {_, {_, x, _}}} -> x 172 {{_, {x, _, _}}, {_, {x, _, _}}} -> x 173 {{_, {_, x, _}}, {_, {x, _, _}}} -> x 174 {{_, {x, _, _}}, {_, {_, x, _}}} -> x 175 end 176 end 177 178 defp axes_transformations(a, b) do 179 Map.merge(a, b, fn 180 _, a, b -> {a, b} 181 end) 182 |> Map.values() 183 |> Map.new() 184 end 185 186 defp reaxe(point, axes) do 187 %{ 188 x: point[axes[:x]], 189 y: point[axes[:y]], 190 z: point[axes[:z]] 191 } 192 end 193 194 def pairs(points) do 195 for a <- points, 196 b <- points, 197 a < b, 198 dx = abs(a.x - b.x), 199 dy = abs(a.y - b.y), 200 dz = abs(a.z - b.z), 201 into: %{}, 202 do: {{dx ** 2 + dy ** 2 + dz ** 2, dx + dy + dz}, {a, b, %{dx => :x, dy => :y, dz => :z}}} 203 end 204end 205``` 206 207```output 208{:module, Day19, <<70, 79, 82, 49, 0, 0, 26, ...>>, {:pairs, 1}} 209``` 210 211```elixir 212[first | rest] = Enum.map(input, &Day19.pairs/1) 213 214{joinable, later} = 215 Enum.split_with(rest, fn pairs -> 216 s1 = MapSet.new(first, fn {k, _} -> k end) 217 s2 = MapSet.new(pairs, fn {k, _} -> k end) 218 219 case MapSet.size(MapSet.intersection(s1, s2)) do 220 66 -> true 221 x when x < 66 -> false 222 end 223 end) 224 225{length(joinable), length(later)} 226``` 227 228```output 229{3, 30} 230``` 231 232```elixir 233# Distances between each pair of points in `first` 234 235points_set = MapSet.new(hd(input)) 236 237{points_set, scanners} = 238 Enum.reduce_while( 239 1..34, 240 {points_set, joinable, later, []}, 241 fn i, {set, joinable, later, scanners} -> 242 {time, {set, scanners}} = 243 :timer.tc(fn -> 244 Enum.reduce(joinable, {set, scanners}, fn new, {base, scanners} -> 245 {new, scanner} = Day19.rebase(base, new) 246 247 {MapSet.union(new, base), [scanner | scanners]} 248 end) 249 end) 250 251 IO.inspect(time / 1_000_000) 252 253 current = Day19.pairs(set) 254 255 Enum.split_with(later, fn pairs -> 256 s1 = MapSet.new(current, fn {k, _} -> k end) 257 s2 = MapSet.new(pairs, fn {k, _} -> k end) 258 259 MapSet.size(MapSet.intersection(s1, s2)) >= 66 260 end) 261 |> case do 262 {[], []} -> 263 {:halt, {set, scanners}} 264 265 {joinable, later} -> 266 IO.inspect({length(joinable), length(later)}, label: i) 267 {:cont, {set, joinable, later, scanners}} 268 end 269 end 270 ) 271 272MapSet.size(points_set) 273``` 274 275```output 2760.002822 2771: {5, 25} 2780.017052 2792: {5, 20} 2800.0355 2813: {7, 13} 2820.134655 2834: {6, 7} 2840.229952 2855: {5, 2} 2860.288351 2876: {1, 1} 2880.076665 2897: {1, 0} 2900.071942 291``` 292 293```output 294396 295``` 296 297```elixir 298for( 299 a <- scanners, 300 b <- scanners, 301 do: abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) 302) 303|> Enum.max() 304``` 305 306```output 30711828 308```