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_window
function 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.