Day 9 Parts 1 and 2
This commit is contained in:
parent
3784d613f6
commit
8e7f5288ce
5 changed files with 1165 additions and 1 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
My (attempted) solutions to [Advent of Code 2020](https://adventofcode.com/2020) in Elixir.
|
My (attempted) solutions to [Advent of Code 2020](https://adventofcode.com/2020) in Elixir.
|
||||||
|
|
||||||
<img width="975" alt="image" src="https://user-images.githubusercontent.com/498229/101474250-39a22900-398e-11eb-9716-42b0d65171e4.png">
|
<img width="975" alt="image" src="https://user-images.githubusercontent.com/498229/101621087-a71a8c00-3a58-11eb-9cf5-ca816dbba6ee.png">
|
||||||
|
|
||||||
## Strategy
|
## Strategy
|
||||||
|
|
||||||
|
|
52
day9/README
Normal file
52
day9/README
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
Day 9 Notes
|
||||||
|
|
||||||
|
+--------+
|
||||||
|
| Part 1 |
|
||||||
|
+--------+
|
||||||
|
|
||||||
|
$ elixir day9part1.exs
|
||||||
|
18272118
|
||||||
|
|
||||||
|
Thoughts:
|
||||||
|
|
||||||
|
Searching inside a sliding window.
|
||||||
|
|
||||||
|
Tricky to write efficiently in an immutable language.
|
||||||
|
I knew what I wanted to do, but working out how to write that took a lot of thought.
|
||||||
|
The solution completes in just over 1ms, so I'm pretty happy with it.
|
||||||
|
|
||||||
|
Every time the window slides, I decided to sort it, which means I could take the following shortcuts:
|
||||||
|
* When selecting the first number in the pair, stop early if added to the first number in the preamble it's
|
||||||
|
greater than the target, because we know the rest of the numbers are also too large.
|
||||||
|
* Similarly can do the same when checking for the second number in the pair.
|
||||||
|
* Avoids comparing pairs we already compared.
|
||||||
|
|
||||||
|
|
||||||
|
+--------+
|
||||||
|
| Part 2 |
|
||||||
|
+--------+
|
||||||
|
|
||||||
|
$ elixir day9part2.exs
|
||||||
|
2186361
|
||||||
|
|
||||||
|
Thoughts:
|
||||||
|
|
||||||
|
Initially hard-coded the answer from part 1, but then decided to re-use part one and pass it as an
|
||||||
|
input to keep the solution fully dynamic.
|
||||||
|
|
||||||
|
Similar to part 1, but now we have to check an unknown number of adjacent items. Again stop checking each
|
||||||
|
group as soon as the sum is too big, and move to the next group.
|
||||||
|
|
||||||
|
This answer also completes in about a millisecond (not including part 1).
|
||||||
|
|
||||||
|
P.S. I accidentally copied part 1 over part 2 after solving this, and had to write the solution again 🤦🏼♂️
|
||||||
|
|
||||||
|
+------------------+
|
||||||
|
| Overall Thoughts |
|
||||||
|
+------------------+
|
||||||
|
|
||||||
|
The most challenging thing here was working out how to write it in an immutable, recursive way cleanly.
|
||||||
|
I was considering using the :array module, but I think I managed to get everything working nicely with
|
||||||
|
lists without the need to do lots of expensive list-indexing. Pretty sure both answers are O(nlogn).
|
||||||
|
|
||||||
|
Probably will tidy up this more later, especailly Part 1, but done for now.
|
41
day9/day9part1.exs
Normal file
41
day9/day9part1.exs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
defmodule Day9Part1 do
|
||||||
|
def run do
|
||||||
|
File.read!("input")
|
||||||
|
|> String.split("\n", trim: true)
|
||||||
|
|> Enum.map(&String.to_integer/1)
|
||||||
|
|> find_invalid()
|
||||||
|
|> IO.puts()
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_invalid([_ | next] = numbers) do
|
||||||
|
{preamble, [number | _]} = Enum.split(numbers, 25)
|
||||||
|
sorted_preamble = Enum.sort(preamble)
|
||||||
|
max = number - hd(sorted_preamble)
|
||||||
|
|
||||||
|
if sum_in_preamble?(sorted_preamble, max, number) do
|
||||||
|
find_invalid(next)
|
||||||
|
else
|
||||||
|
number
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sum_in_preamble?([], _max, _number), do: false
|
||||||
|
|
||||||
|
def sum_in_preamble?([preamble_head | _], max, _number) when preamble_head >= max, do: false
|
||||||
|
|
||||||
|
def sum_in_preamble?([preamble_head | preamble_rest], max, number) do
|
||||||
|
if forms_pair?(preamble_head, preamble_rest, max, number) do
|
||||||
|
true
|
||||||
|
else
|
||||||
|
sum_in_preamble?(preamble_rest, max, number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def forms_pair?(_, [y | _], max, _number) when y > max, do: false
|
||||||
|
|
||||||
|
def forms_pair?(x, preamble_rest, _max, number) do
|
||||||
|
Enum.any?(preamble_rest, fn y -> x + y == number end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Day9Part1.run()
|
71
day9/day9part2.exs
Normal file
71
day9/day9part2.exs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
defmodule Day9Part2 do
|
||||||
|
def run do
|
||||||
|
input =
|
||||||
|
File.read!("input")
|
||||||
|
|> String.split("\n", trim: true)
|
||||||
|
|> Enum.map(&String.to_integer/1)
|
||||||
|
|
||||||
|
invalid_number = Day9Part1.find_invalid(input)
|
||||||
|
|
||||||
|
input
|
||||||
|
|> find_weakness(invalid_number)
|
||||||
|
|> IO.puts()
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_weakness([current | rest], invalid_number) do
|
||||||
|
case find_group(rest, [current], current, invalid_number) do
|
||||||
|
{:ok, group} -> Enum.min(group) + Enum.max(group)
|
||||||
|
_ -> find_weakness(rest, invalid_number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_group(_list, group, sum, target) when sum == target, do: {:ok, group}
|
||||||
|
def find_group(_list, _group, sum, target) when sum > target, do: :too_big
|
||||||
|
def find_group(_list, [], _sum, _target), do: :eol
|
||||||
|
|
||||||
|
def find_group([next | rest], group, sum, target) do
|
||||||
|
find_group(rest, [next | group], sum + next, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule Day9Part1 do
|
||||||
|
def run do
|
||||||
|
File.read!("input")
|
||||||
|
|> String.split("\n", trim: true)
|
||||||
|
|> Enum.map(&String.to_integer/1)
|
||||||
|
|> find_invalid()
|
||||||
|
|> IO.puts()
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_invalid([_ | next] = numbers) do
|
||||||
|
{preamble, [number | _]} = Enum.split(numbers, 25)
|
||||||
|
sorted_preamble = Enum.sort(preamble)
|
||||||
|
max = number - hd(sorted_preamble)
|
||||||
|
|
||||||
|
if sum_in_preamble?(sorted_preamble, max, number) do
|
||||||
|
find_invalid(next)
|
||||||
|
else
|
||||||
|
number
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sum_in_preamble?([], _max, _number), do: false
|
||||||
|
|
||||||
|
def sum_in_preamble?([preamble_head | _], max, _number) when preamble_head >= max, do: false
|
||||||
|
|
||||||
|
def sum_in_preamble?([preamble_head | preamble_rest], max, number) do
|
||||||
|
if forms_pair?(preamble_head, preamble_rest, max, number) do
|
||||||
|
true
|
||||||
|
else
|
||||||
|
sum_in_preamble?(preamble_rest, max, number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def forms_pair?(_, [y | _], max, _number) when y > max, do: false
|
||||||
|
|
||||||
|
def forms_pair?(x, preamble_rest, _max, number) do
|
||||||
|
Enum.any?(preamble_rest, fn y -> x + y == number end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Day9Part2.run()
|
1000
day9/input
Normal file
1000
day9/input
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue