Some notes that I thought would be worth mentioning up front:
- Advent of Code randomizes the input for each user, with the input format being 1,000 12-digit binary numbers delimited by newlines.
- I've verified that my solution works by submitting the generated answer on the website.
Here is the important part of the problem statement, transcribed from Day 3 - Advent of Code 2021:
You need to use the binary numbers in the diagnostic report to generate two new binary numbers (called the gamma rate and the epsilon rate). The power consumption can then be found by multiplying the gamma rate by the epsilon rate.
Each bit in the gamma rate can be determined by finding the most common bit in the corresponding position of all numbers in the diagnostic report. For example, given the following diagnostic report:
00100 11110 10110 10111 10101 01111 00111 11100 10000 11001 00010 01010
Considering only the first bit of each number, there are five
0
bits and seven1
bits. Since the most common bit is1
, the first bit of the gamma rate is1
.The most common second bit of the numbers in the diagnostic report is
0
, so the second bit of the gamma rate is0
.The most common value of the third, fourth, and fifth bits are
1
,1
, and0
, respectively, and so the final three bits of the gamma rate are110
.So, the gamma rate is the binary number
10110
, or22
in decimal.The epsilon rate is calculated in a similar way; rather than use the most common bit, the least common bit from each position is used. So, the epsilon rate is
01001
, or9
in decimal. Multiplying the gamma rate (22
) by the epsilon rate (9
) produces the power consumption,198
.Use the binary numbers in your diagnostic report to calculate the gamma rate and epsilon rate, then multiply them together. What is the power consumption of the submarine? (Be sure to represent your answer in decimal, not binary.)
My solution
#!/usr/bin/env python3
from pathlib import Path
def load_numbers(input_path: Path) -> list[int]:
with input_path.open() as f:
return [
int(binary_string, 2)
for line in f
if (binary_string := line.strip())
]
def binary_diagnostic_part_one(input_path: Path) -> int:
NUM_BINARY_DIGITS = 12
zero_counts = [0] * NUM_BINARY_DIGITS
# used to flip all bits in gamma rate to get epsilon rate
BIT_MASK = 2 ** NUM_BINARY_DIGITS - 1 # 0b1111_1111_1111
numbers = load_numbers(input_path)
for number in numbers:
for i in range(NUM_BINARY_DIGITS):
binary_digit = (number >> i) & 1
zero_counts[i] += int(binary_digit == 0)
# calculate gamma rate from zero counts
# starting from MSB -> LSB
n = len(numbers)
gamma_rate = 0
for num_zeros in reversed(zero_counts):
gamma_digit = 0 if num_zeros > n - num_zeros else 1
gamma_rate = (gamma_rate << 1) | gamma_digit
epsilon_rate: int = gamma_rate ^ BIT_MASK
return gamma_rate * epsilon_rate
if __name__ == "__main__":
print(binary_diagnostic_part_one(Path("input.txt")))
Sample input.txt
A pared-down version of the actual input.txt, for brevity.
010110011101
101100111000
100100000011
111000010001
001100010011
010000111100
001000100011
001000100111
010001111110
111101001011
011000101011
Output
3987500