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 +
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.