Source: Depth Finder
Part 1: Given a list of numbers, count how many times sequential numbers increase.
I’m going to be using the excellent typer library this time to make CLIs. Assume that I’ve always made app = typer.Typer() and have a app() in the main function. With that, all we have to do is write typed functions decorated with @app.command() (which I’ll also leave out).
def part1(data: typer.FileText):
count = 0
last_depth = None
for line in data:
current_depth = int(line)
if last_depth and current_depth > last_depth:
count += 1
last_depth = current_depth
print(count)
That’s the straight forward way to do the problem. Keep track of the previous value, if we have an increase, bump count, and then print it at the end:
$ python3 depth-finder.py part1 input.txt
1393
Part 2: Do the same with a 3-width sliding window.
A bit more complicated, so of course I’m going to completely do this one in a different more functional style:
def part2(data: typer.FileText, window_size: typing.Optional[int] = typer.Argument(1)):
# Convert all depths to ints
depths = list(map(int, data))
# Calculate each window (depth[...]) and the sum of depths for each window
slices = [
sum(depths[start: start + window_size])
for start in range(len(depths) - window_size + 1)
]
# Count if we have an increase (b > a) for each pair of depths
print(sum(
1 if b > a else 0
for a, b
in zip(slices, slices[1:])
))
Comments this time because it’s a bit more interesting. But essentially we have (as commented) three steps:
- Convert from strings to ints (can typer do this for me?)
- Calculate the windows using slices of lists (there should be something in itertools that does this for me, pairwise works for n=2 and more-itertools has a
sliding_windowfunction built in that does this) - For each pair of windows (using the
zip(ls, ls[1:])pattern), count if we increased using a generator +sum
It’s perhaps not the easiest to read code for everyone, but I think it’s elegant enough.
$ python3 depth-finder.py part2 input.txt 3
1359
If you want this written in the style of part1 instead:
def part2_simple(data: typer.FileText, window_size: typing.Optional[int] = typer.Argument(1)):
count = 0
last_depth = None
# typer.FileText does not work with len or slices, so convert to a list
data = list(data)
for start in range(len(data) - window_size):
current_depth = sum(map(int, data[start: start + window_size]))
if not last_depth or current_depth > last_depth:
count += 1
last_depth = current_depth
print(count)
With this tiny of input data… the timing doesn’t change at all:
$ python3 depth-finder.py part2-simple input.txt 3
1359
Using the test harness I mentioned in the summary post:
--- Day 1: Sonar Sweep ---
$ python3 depth-finder.py part1 input.txt
1393
# time 46772375ns / 0.05s
$ python3 depth-finder.py part2 input.txt 3
1359
# time 34292250ns / 0.03s
$ python3 depth-finder.py part2-simple input.txt 3
1359
# time 34505708ns / 0.03s
It is interesting that either windowed solution is slightly faster than the non-windowed solution though. A few less iterations? Cached file contents? Wouldn’t think it would matter that much, but so it goes.