shritesh
Advent of Code 2023 - Day 6
This was way too easy after the last few days. Simple map, filter and count.
Most Liked
Aetherus
Today’s puzzle is all about quadratic equation.
If the total time of a game is t, the speed of the boat (i.e. the time holding that button) is v, the distance the boat traveled is s, then the equation is
v * (t - v) = s
which can be normalized to
(v ** 2) - (t * v) + s = 0
All the speeds that result in better distance are between the two solutions of v of that equation.
lud
Aaaah. I wish I knew maths …
I hesitate to post my solution because it is so much more complex, but anyway.
Part 2 was around 4 seconds with the algorithm of part 1 so I used a binary search to find the two bounds:
defmodule AdventOfCode.Y23.Day6 do
alias AoC.Input, warn: false
def read_file(file, _part) do
file |> Input.stream!(trim: true) |> Enum.take(2)
end
def parse_input([times, distances], :part_one) do
times = int_list_no_header(times)
distances = int_list_no_header(distances)
Enum.zip(times, distances)
end
def parse_input(["Time: " <> times, "Distance: " <> distances], _) do
{single_int(times), single_int(distances)}
end
defp int_list_no_header(string) do
string
|> String.split(" ", trim: true)
|> Enum.drop(1)
|> Enum.map(&String.to_integer/1)
end
defp single_int(string) do
string
|> String.split(" ", trim: true)
|> Enum.join()
|> String.to_integer()
end
def part_one(problem) do
problem
|> Enum.map(&count_wins/1)
|> Enum.product()
end
defp count_wins({time, best}) do
0..time
|> Enum.map(&hold_time_to_distance(&1, time))
|> Enum.filter(&(&1 > best))
|> length()
end
defp hold_time_to_distance(hold = speed, time) do
duration = time - hold
_distance = speed * duration
end
def part_two({time, distance_record}) do
half = trunc(time / 2)
left = binary_search(&find_left_bound(&1, time, distance_record), 1, half)
right = binary_search(&find_right_bound(&1, time, distance_record), half, time)
right - left + 1
end
defp find_left_bound(hold, time, record) do
left = hold_time_to_distance(hold - 1, time)
right = hold_time_to_distance(hold, time)
case {left, right} do
{d1, d2} when d1 < record and d2 > record -> :eq
{_d1, d2} when d2 < record -> :lt
{d1, _d2} when d1 > record -> :gt
end
end
defp find_right_bound(hold, time, record) do
left = hold_time_to_distance(hold, time)
right = hold_time_to_distance(hold + 1, time)
case {left, right} do
{d1, d2} when d1 > record and d2 < record -> :eq
{_d1, d2} when d2 > record -> :lt
{d1, _d2} when d1 < record -> :gt
end
end
def binary_search(ask, min, max) do
n = div(min + max, 2)
case ask.(n) do
# n is lower than the answer
:lt -> binary_search(ask, n + 1, max)
# n is greater than the answer
:gt -> binary_search(ask, min, n - 1)
:eq -> n
end
end
end
And it takes less than 100µs for part 2 which is really nice.
But still, I would like to understand your maths ![]()
sabiwara
This is due to compiled versus interpreted code.
In this case, wrapping this within a module inside livebook/IEx will compile the code and run in ~1s, while just running the code directly in interpreted mode (uses :erl_eval under the hood) will be slow when you have many iterations.
defmodule MyCell do
def run do
for r <- 1..46828479, r * (46828479 - r) > 347152214061471 do 1 end |> Enum.count()
end
end
MyCell.run()
Popular in Challenges
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








