this repo has no description
at master 6.4 kB view raw
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!("2021/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<!-- livebook:{"output":true} --> 32 33``` 3434 35``` 36 37To find rotation of point $b_1$ and $b_2$ around shift point $b_0$ 38 39$$ 40\left\{ 41\begin{alignat*}{3} 42{b_1}_x r_x + {b_0}_x = {a_1}_x \\ 43{b_2}_x r_x + {b_0}_x = {a_2}_x 44\end{alignat*} 45\right. 46\left\{ 47\begin{alignat*}{3} 48{b_1}_y r_y + {b_0}_y = {a_1}_y \\ 49{b_2}_y r_y + {b_0}_y = {a_2}_y \\ 50\end{alignat*} 51\right. 52\left\{ 53\begin{alignat*}{3} 54{b_1}_z r_z + {b_0}_z = {a_1}_z \\ 55{b_2}_z r_z + {b_0}_z = {a_2}_z \\ 56\end{alignat*} 57\right. 58$$ 59 60Where $$r_x, r_y, r_z \in \{-1, 1\}$$ and $$a_{xyz}, b_{xyz} = \text{const}$$. 61This mean that we need to solve: 62 63$$ 64B_x r_x + b_0 = a_x \\ 65B_y r_y + b_0 = a_y \\ 66B_z r_z + b_0 = a_z 67$$ 68 69Where: 70 71$$ 72B_d = \begin{bmatrix} 73{b_1}_d & 1 \\ 74{b_2}_d & 1 \\ 75\end{bmatrix} 76b_0 = \begin{bmatrix} 77{b_0}_d \\ 78{b_0}_d 79\end{bmatrix} 80a_d = \begin{bmatrix} 81{a_1}_d \\ 82{a_2}_d 83\end{bmatrix} 84$$ 85 86By applying [Cramer's Rule](https://en.wikipedia.org/wiki/Cramer%27s_rule) we can solve these 87linear equations with (we are looking for $b_0$ and $r$): 88 89$$ 90r_d = \frac{ 91\begin{vmatrix} 92{a_1}_d & 1 \\ 93{a_2}_d & 1 94\end{vmatrix} 95}{ 96\begin{vmatrix} 97{b_1}_d & 1 \\ 98{b_2}_d & 1 99\end{vmatrix} 100} = \frac{{a_1}_d - {a_2}_d}{{b_1}_d - {b_2}_d} \\ 101b_{d_0} = \frac{ 102 \begin{vmatrix} 103{b_1}_d & {a_1}_d \\ 104{b_2}_d & {a_2}_d 105\end{vmatrix} 106}{ 107 \begin{vmatrix} 108{b_1}_d & 1 \\ 109{b_2}_d & 1 110\end{vmatrix} 111} = \frac{{b_1}_d {a_2}_d - {a_1}_d {b_2}_d}{{b_1}_d - {b_2}_d} 112$$ 113 114```elixir 115defmodule Day19 do 116 def rebase(base, pairs1) do 117 pairs0 = pairs(base) 118 # Take common points 119 pa = Map.take(pairs0, Map.keys(pairs1)) 120 pb = Map.take(pairs1, Map.keys(pairs0)) 121 122 # Merge them 123 [{{a, b, axes1}, {_, _, axes2}} = p0 | others] = 124 Map.merge(pa, pb, fn _, a, b -> {a, b} end) 125 |> Map.values() 126 127 p1 = Enum.find(others, fn {{x, y, _}, _} -> a in [x, y] end) 128 p2 = Enum.find(others, fn {{x, y, _}, _} -> b in [x, y] end) 129 130 axes = axes_transformations(axes1, axes2) 131 132 a_1 = a 133 a_2 = b 134 b_1 = reaxe(select_common(p0, p1), axes) 135 b_2 = reaxe(select_common(p0, p2), axes) 136 137 transform = 138 for d <- ~w[x y z]a, into: %{} do 139 a_1 = a_1[d] 140 a_2 = a_2[d] 141 b_1 = b_1[d] 142 b_2 = b_2[d] 143 det_b = b_1 - b_2 144 r = div(a_1 - a_2, det_b) 145 146 b_0 = div(b_1 * a_2 - a_1 * b_2, det_b) 147 148 {d, {r, b_0}} 149 end 150 151 new_points = 152 pairs1 153 |> Map.values() 154 |> Enum.flat_map(&[elem(&1, 0), elem(&1, 1)]) 155 |> Enum.uniq() 156 |> Enum.map(&reaxe(&1, axes)) 157 |> do_rebase(transform) 158 |> MapSet.new() 159 160 %{x: {_, sx}, y: {_, sy}, z: {_, sz}} = transform 161 scanner = %{x: sx, y: sy, z: sz} 162 163 {new_points, scanner} 164 end 165 166 defp do_rebase(points, transform) do 167 points 168 |> Enum.map(fn p -> 169 Map.merge(p, transform, fn _k, d, {r, b} -> d * r + b end) 170 end) 171 |> MapSet.new() 172 end 173 174 defp select_common(a, b) do 175 case {a, b} do 176 {{_, {_, x, _}}, {_, {_, x, _}}} -> x 177 {{_, {x, _, _}}, {_, {x, _, _}}} -> x 178 {{_, {_, x, _}}, {_, {x, _, _}}} -> x 179 {{_, {x, _, _}}, {_, {_, x, _}}} -> x 180 end 181 end 182 183 defp axes_transformations(a, b) do 184 Map.merge(a, b, fn 185 _, a, b -> {a, b} 186 end) 187 |> Map.values() 188 |> Map.new() 189 end 190 191 defp reaxe(point, axes) do 192 %{ 193 x: point[axes[:x]], 194 y: point[axes[:y]], 195 z: point[axes[:z]] 196 } 197 end 198 199 def pairs(points) do 200 for a <- points, 201 b <- points, 202 a < b, 203 dx = abs(a.x - b.x), 204 dy = abs(a.y - b.y), 205 dz = abs(a.z - b.z), 206 into: %{}, 207 do: {{dx ** 2 + dy ** 2 + dz ** 2, dx + dy + dz}, {a, b, %{dx => :x, dy => :y, dz => :z}}} 208 end 209end 210``` 211 212<!-- livebook:{"output":true} --> 213 214``` 215{:module, Day19, <<70, 79, 82, 49, 0, 0, 29, ...>>, {:pairs, 1}} 216``` 217 218```elixir 219[first | rest] = Enum.map(input, &Day19.pairs/1) 220 221{joinable, later} = 222 Enum.split_with(rest, fn pairs -> 223 s1 = MapSet.new(first, fn {k, _} -> k end) 224 s2 = MapSet.new(pairs, fn {k, _} -> k end) 225 226 case MapSet.size(MapSet.intersection(s1, s2)) do 227 66 -> true 228 x when x < 66 -> false 229 end 230 end) 231 232{length(joinable), length(later)} 233``` 234 235<!-- livebook:{"output":true} --> 236 237``` 238{3, 30} 239``` 240 241```elixir 242# Distances between each pair of points in `first` 243 244points_set = MapSet.new(hd(input)) 245 246{points_set, scanners} = 247 Enum.reduce_while( 248 1..34, 249 {points_set, joinable, later, []}, 250 fn i, {set, joinable, later, scanners} -> 251 {time, {set, scanners}} = 252 :timer.tc(fn -> 253 Enum.reduce(joinable, {set, scanners}, fn new, {base, scanners} -> 254 {new, scanner} = Day19.rebase(base, new) 255 256 {MapSet.union(new, base), [scanner | scanners]} 257 end) 258 end) 259 260 # IO.inspect(time / 1_000_000) 261 262 current = Day19.pairs(set) 263 264 Enum.split_with(later, fn pairs -> 265 s1 = MapSet.new(current, fn {k, _} -> k end) 266 s2 = MapSet.new(pairs, fn {k, _} -> k end) 267 268 MapSet.size(MapSet.intersection(s1, s2)) >= 66 269 end) 270 |> case do 271 {[], []} -> 272 {:halt, {set, scanners}} 273 274 {joinable, later} -> 275 # IO.inspect({length(joinable), length(later)}, label: i) 276 {:cont, {set, joinable, later, scanners}} 277 end 278 end 279 ) 280 281MapSet.size(points_set) 282``` 283 284<!-- livebook:{"output":true} --> 285 286``` 287warning: variable "i" is unused (if the variable is not meant to be used, prefix it with an underscore) 288 2021/day19.livemd#cell:w7cczzbdb2mrwtou27usauoozuqydf3v:9 289 290warning: variable "time" is unused (if the variable is not meant to be used, prefix it with an underscore) 291 2021/day19.livemd#cell:w7cczzbdb2mrwtou27usauoozuqydf3v:10 292 293``` 294 295<!-- livebook:{"output":true} --> 296 297``` 298396 299``` 300 301```elixir 302for( 303 a <- scanners, 304 b <- scanners, 305 do: abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) 306) 307|> Enum.max() 308``` 309 310<!-- livebook:{"output":true} --> 311 312``` 31311828 314```