LoginSignup
8
1

闘魂Elixir ── Advent of code 2023 Day 5 Part 2 を Livebook で楽しむ

Last updated at Posted at 2023-12-26

$\huge{元氣ですかーーーーッ!!!}$
$\huge{元氣があればなんでもできる!}$

$\huge{闘魂とは己に打ち克つこと。}$
$\huge{そして闘いを通じて己の魂を磨いていく}$
$\huge{ことだと思います}$

はじめに

@torifukukaiou さんの パク リスペクト記事です

Elixir Livebook で Advent of Code 2023 の問題を解いてみます

実装したノートブックはこちら

問題はこちら

Part 1 はこちら

セットアップ

Kino AOC をインストールします

Mix.install([
  {:kino_aoc, "~> 0.1.5"}
])

Kino AOC の使い方はこちらを参照

入力の取得

Day 5 の入力を取得します

スクリーンショット 2023-12-26 15.45.30.png

私の答え

私の答えです。
折りたたんでおきます。
▶を押して開いてください。

details

回答用のモジュールです

入力を扱いやすい形にするための parse と、回答を作るための resolve 関数を持っています

defmodule Resolver do
  def parse(input) do
    input
    |> String.split("\n\n")
    |> Enum.into(%{}, fn group ->
      group
      |> String.split("\n")
      |> parse_group()
    end)
  end

  defp parse_group(group) when length(group) == 1 do
    seeds =
      group
      |> hd()
      |> String.split(" ")
      |> tl()
      |> Enum.map(&String.to_integer(&1))
      |> Enum.chunk_every(2)
      |> Enum.map(fn [start, len] ->
        %{
          start_index: start,
          end_index: start + len - 1
        }
      end)

    {:seeds, seeds}
  end

  defp parse_group(group) do
    group_name =
      group
      |> hd()
      |> String.slice(0..-6)
      |> String.replace("-", "_")
      |> String.to_atom()

    mappings =
      group
      |> tl()
      |> Enum.map(fn line ->
        [dst, src, len] =
          line
          |> String.split(" ")
          |> Enum.map(&String.to_integer(&1))

        %{
          src: src,
          dst: dst,
          len: len
        }
      end)

    {group_name, mappings}
  end

  def resolve(maps) do
    0
    |> Stream.iterate(& &1 + 1)
    |> Enum.reduce_while(nil, fn location, _acc ->
      seed =
        location
        |> reverse_search(maps.humidity_to_location)
        |> reverse_search(maps.temperature_to_humidity)
        |> reverse_search(maps.light_to_temperature)
        |> reverse_search(maps.water_to_light)
        |> reverse_search(maps.fertilizer_to_water)
        |> reverse_search(maps.soil_to_fertilizer)
        |> reverse_search(maps.seed_to_soil)

      maps.seeds
      |> Enum.any?(fn %{start_index: start_index, end_index: end_index} ->
        seed >= start_index and seed <= end_index
      end)
      |> if do
        {:halt, location}
      else
        {:cont, nil}
      end
    end)
  end

  defp reverse_search(key, mappings) do
    target_mapping =
      Enum.find(mappings, fn mapping ->
        key >= mapping.dst and key <= (mapping.dst + mapping.len - 1)
      end)

    if is_nil(target_mapping) do
      key
    else
      target_mapping.src + key - target_mapping.dst
    end
  end
end

parse は Part 1 では無駄に難しく考えていました

\n\n で先に区切れば単純に空白行でグループ化できますね

resolve ではついに計算量を考慮しなければならなくなりました

seeds が範囲指定になったため、 Part 1 と同じロジックでは、いつまで経っても計算が終わりません

最小値を求める問題なので、 location の方から逆引きするようにして、 0 から順に種 ID が seeds の範囲内に入っているかチェックしていきます

まとめ

いよいよ計算量を考えないといけなくなリました

ここからが本番、という感じですね

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1