5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 4

Advent of code 2024 Day 13 Part 2 を Livebook で楽しむ

Last updated at Posted at 2024-12-18

はじめに

Advent of code 2024 Day 13 の Part 2 を解きます

問題文はこちら

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

Part 1 はこちら

セットアップ

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

また、今回は行列演算を使いたいので、 Nx もインストールします

Mix.install([
  {:kino_aoc, "~> 0.1"},
  {:nx, "~> 0.9"}
])

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

入力の取得

"Advent of Code Helper" スマートセルを追加し、 Day 13 の入力を取得します

スクリーンショット 2024-12-18 16.44.58.png

私の答え

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

回答

Part 1 の回答のままでは、組み合わせの数が多くなりすぎて終わりません

組み合わせを探索するのではなく、以下の連立方程式を解く方針にしましょう

$$
n_aa_x + n_bb_x = p_x
$$

$$
n_aa_y + n_bb_y = p_y
$$

($n_a$ は A ボタンを押す回数、 $n_b$ は B ボタンを押す回数)

この連立方程式は行列を使うと以下のように表現できます

expresssion.png

expresssion (1).png

expresssion (2).png

$$
Ax=b
$$

sample_input =
  """
  Button A: X+94, Y+34
  Button B: X+22, Y+67
  Prize: X=8400, Y=5400
  
  Button A: X+26, Y+66
  Button B: X+67, Y+21
  Prize: X=12748, Y=12176
  
  Button A: X+17, Y+86
  Button B: X+84, Y+37
  Prize: X=7870, Y=6450
  
  Button A: X+69, Y+23
  Button B: X+27, Y+71
  Prize: X=18641, Y=10279
  """
  |> String.trim()

クレーンゲーム機を A と b の行列で表現するように読み込みます

また、桁数が大きいため、型を :f64 に指定します

parse_machines = fn input ->
  input
  |> String.split("\n\n")
  |> Enum.map(fn machine_rows ->
    Regex.named_captures(
      ~r/\+(?<ax>\d+).*\+(?<ay>\d+)\n.*\+(?<bx>\d+).*\+(?<by>\d+)\n.*=(?<px>\d+).*=(?<py>\d+)/,
      machine_rows
    )
  end)
  |> Enum.map(fn %{"ax" => ax, "ay" => ay, "bx" => bx, "by" => by, "px" => px, "py" => py} ->
    %{
      A: Nx.tensor([
          [String.to_integer(ax), String.to_integer(bx)],
          [String.to_integer(ay), String.to_integer(by)]
      ], type: :f64),
      b: Nx.tensor([
        String.to_integer(px) + 10000000000000,
        String.to_integer(py) + 10000000000000
      ], type: :f64)
    }
  end)
end
machines = parse_machines.(sample_input)

実行結果

[
  %{
    b: #Nx.Tensor<
      f64[2]
      [10000000008400.0, 10000000005400.0]
    >,
    A: #Nx.Tensor<
      f64[2][2]
      [
        [94.0, 22.0],
        [34.0, 67.0]
      ]
    >
  },
  %{
    b: #Nx.Tensor<
      f64[2]
      [10000000012748.0, 10000000012176.0]
    >,
    A: #Nx.Tensor<
      f64[2][2]
      [
        [26.0, 67.0],
        [66.0, 21.0]
      ]
    >
  },
  %{
    b: #Nx.Tensor<
      f64[2]
      [10000000007870.0, 10000000006450.0]
    >,
    A: #Nx.Tensor<
      f64[2][2]
      [
        [17.0, 84.0],
        [86.0, 37.0]
      ]
    >
  },
  %{
    b: #Nx.Tensor<
      f64[2]
      [10000000018641.0, 10000000010279.0]
    >,
    A: #Nx.Tensor<
      f64[2][2]
      [
        [69.0, 27.0],
        [23.0, 71.0]
      ]
    >
  }
]

Nx には連立方程式を解くための関数が用意されています

Nx.LinAlg.solve(A, b) で x を求めることができます

Nx.LinAlg.solve(
  Nx.tensor([[26, 67], [66, 21]], type: :f64),
  Nx.tensor([10000000012748, 10000000012176], type: :f64)
)

実行結果

#Nx.Tensor<
  f64[2]
  [118679050709.00002, 103199174542.0]
>

誤差はありますが、答えがほぼ整数になっています

連立方程式が解けないケースを実行してみます

Nx.LinAlg.solve(
  Nx.tensor([[94, 22], [34, 67]], type: :f64),
  Nx.tensor([10000000008400, 10000000005400], type: :f64)
)

実行結果

#Nx.Tensor<
  f64[2]
  [81081081161.08109, 108108108148.10811]
>

結果が整数ではありません

このことを利用して、ある程度の誤差を許容しつつ、結果が整数であれば解けたと考えられます

get_prize = fn %{A: a, b: b} ->
  x = Nx.LinAlg.solve(a, b)

  if x |> Nx.to_list() |> Enum.all?(&(round(&1 * 100) == round(&1) * 100)) do
    x |> Nx.to_list() |> Enum.map(&round(&1))
  else
    [0, 0]
  end
end
Enum.map(machines, &get_prize.(&1))

実行結果

[[0, 0], [118679050709, 103199174542], [0, 0], [102851800151, 107526881786]]

この結果を使ってコストを計算します

get_cost = fn machines ->
  machines
  |> Enum.map(fn machine ->
    machine
    |> get_prize.()
    |> then(fn [num_a, num_b] ->
      num_a * 3 + num_b
    end)
  end)
  |> Enum.sum()
end
get_cost.(machines)

実行結果

875318608908

実際の入力を使えば答えが出ます

puzzle_input
|> parse_machines.()
|> get_cost.()

まとめ

問題文から ChatGPT に画像を生成してもらいました

aoc2024_day13_2.png

Nx に連立方程式を解いてもらいました

桁数が大きすぎる場合、考慮しないといけないことが色々ありますね

5
0
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?