여러 개의 2진수에 대해, 같은 위치에서 가장 흔한 비트/덜 흔한 비트를 찾은 뒤 이래저래 처리하는 문제였다. 구현만 하는 건 어렵지 않을 것 같았는데(2진수 계산이라거나 하는 자잘한 게 귀찮긴 해도), 혹시나 나중에 이 코드를 재활용할 일이 생길까봐 되도록 확장성 있게 짜려고 고민하느라 시간도 에너지도 생각보다 많이 들였다. (굳이 언급하자면 bit_length = input |> hd() |> String.length()
같은 줄이 고민의 흔적. 그냥 상수로 했어도 됐을텐데.)
그렇게 첫 번째 문제를 풀고 났더니 두 번째 문제에서 전혀 다른 방향으로 코드를 확장해야 해서(1번 문제 코드에서 bit frequency를 굳이 위치별로 계산하도록 한 것은 2번 문제랑 코드를 공유하기 위해서 나중에 수정한 것이다), 다 풀고 나서 생각해 보니 '개발자가 처음에 생각하는 확장 가능성이란 정말 아무 짝에도 쓸모가 없는 것이 아닌가' 싶기도 하고 그렇다.
중간중간 나오는 중복되는 코드를 최대한 밖으로 빼려고 하다 보니 자연스럽게 재귀도 쓰게 됐다. 2진수 계산하는 부분 같은 건 조금 더 깔끔하게 할 수 있을 것 같은데, 귀찮으니 여기까지.
defmodule Day3 do
def parse_input do
path = "input/day3.txt"
File.read!(path)
|> String.split("\n")
end
def run_q1 do
input = parse_input()
bit_length = input |> hd() |> String.length()
bit_frequencies =
0..(bit_length - 1)
|> Enum.map(fn pos -> calculate_bit_frequency(input, pos) end)
gamma_rate =
bit_frequencies
|> Enum.map(&most_common_bit/1)
|> list_to_binary()
epsilon_rate =
bit_frequencies
|> Enum.map(&least_common_bit/1)
|> list_to_binary()
gamma_rate * epsilon_rate
end
def run_q2 do
input = parse_input()
oxygen = do_run_q2(input, 0, &most_common_bit/1)
co2 = do_run_q2(input, 0, &least_common_bit/1)
oxygen * co2
end
defp do_run_q2([number], _, _) do
number |> String.graphemes() |> list_to_binary()
end
defp do_run_q2(numbers, pos, predicate_to_keep_bit) do
keeping_bit =
numbers
|> calculate_bit_frequency(pos)
|> predicate_to_keep_bit.()
numbers
|> Enum.filter(fn number -> String.at(number, pos) == keeping_bit end)
|> do_run_q2(pos + 1, predicate_to_keep_bit)
end
defp calculate_bit_frequency(input, pos) do
input
|> Enum.reduce(
{0, 0},
fn
bit, {zero_count, one_count} ->
String.at(bit, pos)
|> then(fn
"0" -> {zero_count + 1, one_count}
"1" -> {zero_count, one_count + 1}
end)
end
)
end
defp list_to_binary(binary_list) do
binary_list
|> List.to_charlist()
|> List.to_integer(2)
end
defp most_common_bit({zero, one}) when zero > one, do: "0"
defp most_common_bit({zero, one}) when zero <= one, do: "1"
defp least_common_bit({zero, one}) when zero > one, do: "1"
defp least_common_bit({zero, one}) when zero <= one, do: "0"
end
'호두나무 공방 > Advent of Code' 카테고리의 다른 글
Advent of Code 2021 Day 6 in Elixir - Lanternfish (0) | 2021.12.06 |
---|---|
Advent of Code 2021 Day 5 in Elixir - Hydrothermal Venture (0) | 2021.12.06 |
Advent of Code 2021 Day 4 in Elixir - Giant Squid (0) | 2021.12.04 |
Advent of Code 2021 Day 2 in Elixir - Dive! (0) | 2021.12.02 |
Advent of Code 2021 Day 1 in Elixir - Sonar Sweep (4) | 2021.12.02 |