Seriously short of time to do this today, so I used Memoize rather than roll my own memoization implementation, if I get a chance I’ll re-write using a hand rolled cache.
Still happy with the solution and run-time though.
Solution for 2024 day 11
part_one: 193607 in 12.01ms
part_two: 229557103025807 in 252.98ms
Name ips average deviation median 99th %
part_two 7.63 K 131.09 μs ±50.31% 123.25 μs 226.81 μs
part_one 7.53 K 132.72 μs ±54.50% 124.13 μs 231.54 μs
Comparison:
part_two 7.63 K
part_one 7.53 K - 1.01x slower +1.64 μs
defmodule Aoc2024.Solutions.Y24.Day11 do
require Integer
alias AoC.Input
use Memoize
def parse(input, _part) do
Input.read!(input)
|> String.trim()
|> String.split(" ", trim: true)
|> Enum.map(&String.to_integer/1)
end
def part_one(problem) do
Enum.reduce(problem, 0, fn stone, acc ->
acc + apply_rules(stone, 0, 25)
end)
end
def part_two(problem) do
Enum.reduce(problem, 0, fn stone, acc ->
acc + apply_rules(stone, 0, 75)
end)
end
defmemo apply_rules(stone, count, target) do
if count == target do
1
else
target_stones = rule(stone)
case target_stones do
[_first, _second] ->
Enum.reduce(target_stones, 0, fn stone, acc ->
acc + apply_rules(stone, count + 1, target)
end)
stone ->
apply_rules(stone, count + 1, target)
end
end
end
defmemo rule(stone) do
if stone == 0 do
1
else
stone_string = Integer.to_string(stone)
digits = String.length(Integer.to_string(stone))
case Integer.is_even(digits) do
true ->
String.split_at(stone_string, div(digits, 2))
{left, right} = String.split_at(stone_string, div(digits, 2))
[
String.to_integer(left),
String.to_integer(right)
]
_ ->
stone * 2024
end
end
end
defmemo split_into_chunks(options) do
workers = :erlang.system_info(:schedulers_online)
options_count = Enum.count(options)
options_per_chunk = :erlang.ceil(options_count / workers)
Enum.chunk_every(options, options_per_chunk)
end
end