Day 9 Parts 1 and 2

This commit is contained in:
Adam Millerchip 2020-12-09 20:03:34 +09:00
parent 3784d613f6
commit 8e7f5288ce
5 changed files with 1165 additions and 1 deletions

View file

@ -2,7 +2,7 @@
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

52
day9/README Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff