2019-12-16 - Flawed Frequency Transmission
(original .ipynb)
Day 16 puzzle input is a long sequence of digits (mine is here) which represent a noisy input signal from a sensor. Part 1 involves applying an algorithm to process the signal and generate an output signal and returning the first 8 digits of it. Part 2 involves applying the same algorithm to your signal repeated 1000x its original length, and taking the first 8 digits (after a 7 digit offset, which is the first set of value in the signal). I did not like this one.
def pattern_for_position(position):
return [ 0 ] * position + [ 1 ] * position + [ 0 ] * position + [ -1 ] * position
def pattern_generator(position):
while True:
for pattern in pattern_for_position(position):
yield pattern
def final_digit(n):
return str(n)[-1]
def apply_fft_phase(signal):
signal_digits = [ int(d) for d in signal ]
processed_signal = []
# maybe use range, no need to enumerate here as we discard the element
for i, _ in enumerate(signal_digits):
pattern = pattern_generator(i + 1)
# always skip the first one
next(pattern)
total = sum([ next(pattern) * d for d in signal_digits ])
processed_signal.append(final_digit(total))
return "".join(processed_signal)
def process_signal(signal, phases, first_digits=None):
for n in range(phases):
signal = apply_fft_phase(signal)
if first_digits:
return signal[:first_digits]
return signal
assert "01029498" == process_signal("12345678", 4)
assert "24176176" == process_signal("80871224585914546619083218645595", 100, 8)
assert "73745418" == process_signal("19617804207202209144916044189917", 100, 8)
assert "52432133" == process_signal("69317163492948606335995924319873", 100, 8)
puzzle_input = open("puzzle_input/day16.txt", "r").read()
print(process_signal(puzzle_input, 100, 8))
12541048
def process_repeating_signal(signal, phases, repeating, offset=0, first_digits=None):
for n in range(phases):
signal = apply_fft_phase(signal, offset)
if first_digits:
return signal[offset:first_digits]
return signal
assert "01029498" == process_repeating_signal("12345678", 4, 1, 0)
assert "24176176" == process_repeating_signal("80871224585914546619083218645595", 100, 1, 0, 8)
assert "73745418" == process_repeating_signal("19617804207202209144916044189917", 100, 1, 0, 8)
assert "52432133" == process_repeating_signal("69317163492948606335995924319873", 100, 1, 0, 8)
# trying the above out showed that this is an extremely slow method however it turns out that with
# large enough `offset` we can skip a WHOLE bunch of calculations mostly because when we repeat
# the signal, the offset occurs during the [...1,1,1,1...] parts of the pattern
#
# ... and I had to look this up on Reddit :-(
# I thought an fft was already fast
def apply_fast_fft_phase(digits, offset):
total = sum(digits[offset:])
# add the 0-padding
next_digits = [0] * offset
# the first of the next digit is just the last digit of the sum of the previoufasdasdfj kill me
next_digits.append(int(final_digit(total)))
for n in range(offset + 2, 1 + len(digits)):
total -= digits[n - 2]
next_digits.append(int(final_digit(total)))
return next_digits
def fast_process_signal(signal, phase):
signal_digits = [ int(d) for d in signal ]
offset = int(signal[0:7])
while phase > 0:
signal_digits = apply_fast_fft_phase(signal_digits, offset)
phase -= 1
return "".join([str(d) for d in signal_digits[offset:(offset+8)]])
print(fast_process_signal(puzzle_input * 10000, 100))
62858988