Skip to content

Commit 1b4a354

Browse files
committed
2021 d15: fix walkthrough errors and wording
1 parent 7e53c49 commit 1b4a354

File tree

1 file changed

+48
-14
lines changed

1 file changed

+48
-14
lines changed

2021/README.md

+48-14
Original file line numberDiff line numberDiff line change
@@ -2860,9 +2860,9 @@ path. We want to know the lowest possible total risk level for a path that gets
28602860
from the entrance (top-left) to the exit (bottom-right), passing through any of
28612861
the cells in the grid.
28622862

2863-
We can think about the grid as a graph where nodes are the cells of the grid,
2864-
and each node is connected to its neighboring nodes (just as each cell of the
2865-
grid is connected to its neighboring cells).
2863+
We can think about the grid as a directed graph with as much nodes as there are
2864+
cells in the grid, connected between each others just like cells are connected
2865+
to their neighboring cells.
28662866

28672867
What about the edges? Moving from a given cell A to a neighboring cell B (thus
28682868
entering B) costs us as much as the risk level of B: we can represent this an
@@ -2872,8 +2872,7 @@ level of B, and thus we have another different edge going from B to A with a
28722872
weight equal to the risk level of B. In other words, the edges entering a node
28732873
have the same weight as the risk level of the cell corresponding to that node.
28742874

2875-
It should be pretty clear how to think about the grid as a graph now, but in
2876-
case it isn't, consider the following example with a small grid and its
2875+
Do you see consider the following example with a small grid and its
28772876
corresponding graph representation (`S` = entrance, `E` = exit):
28782877

28792878
```none
@@ -2918,18 +2917,23 @@ def neighbors4(r, c, h, w):
29182917
```
29192918

29202919
Similarly to what we did two years ago for [2019 day 6 part 2][2019-d06-p2], we
2921-
will implement Dijkstra's algorithm using a [min-heap][wiki-min-heap] as a queue
2922-
to hold the nodes to visit and always pop the one with the shortest distance
2923-
from the source. The [`heapq`][py-heapq] module is exactly what we need. A
2924-
`defaultdict` that returns `float('inf')` (positive floating point infinity,
2925-
which compares greater than any integer) as the default value is also useful to
2926-
treat not-yet-seen nodes as being infinitely distant.
2920+
will implement Dijkstra's algorithm using a [min-heap][wiki-min-heap] as a
2921+
[priority queue][wiki-priority-queue] to hold the nodes to visit and always pop
2922+
the one with the shortest distance from the source. The [`heapq`][py-heapq]
2923+
module is exactly what we need. A `defaultdict` that returns `float('inf')`
2924+
(also provided by `math.inf`) as the default value is also useful to treat
2925+
not-yet-seen nodes as being infinitely distant (positive floating point infinity
2926+
compares greater than any integer).
29272927

29282928
The algorithm is pretty well-known and also well-explained in the Wikipedia page
29292929
I just linked above, so I'm not going into much detail about it, I'll just add
29302930
some comments to the code.
29312931

29322932
```python
2933+
import heapq
2934+
from collections import defaultdict
2935+
from math import inf as INFINITY
2936+
29332937
def dijkstra(grid):
29342938
h, w = len(grid), len(grid[0])
29352939
source = (0, 0)
@@ -2938,7 +2942,7 @@ def dijkstra(grid):
29382942
# Start with only the source in our queue of nodes to visit and in the
29392943
# mindist dictionary, with distance 0.
29402944
queue = [(0, source)]
2941-
mindist = defaultdict(lambda: float('inf'), {source: 0})
2945+
mindist = defaultdict(lambda: INFINITY, {source: 0})
29422946
visited = set()
29432947

29442948
while queue:
@@ -2957,8 +2961,11 @@ def dijkstra(grid):
29572961
visited.add(node)
29582962
r, c = node
29592963

2960-
# For each neighbor of this node:
2964+
# For each unvisited neighbor of this node...
29612965
for neighbor in neighbors4(r, c, h, w):
2966+
if neighbor in visited:
2967+
continue
2968+
29622969
# Calculate the total distance from the source to this neighbor
29632970
# passing through this node.
29642971
nr, nc = neighbor
@@ -2973,7 +2980,32 @@ def dijkstra(grid):
29732980

29742981
# If we ever empty the queue without entering the node == destination check
29752982
# in the above loop, there is no path from source to destination!
2976-
return float('inf')
2983+
return INFINITY
2984+
```
2985+
2986+
The `for` loop which iterates over the neighbors skipping already visited ones
2987+
can be simplified with a [`filter()`][py-builtin-filter] plus a lambda:
2988+
2989+
```python
2990+
# ...
2991+
for neighbor in filter(lambda n: n not in visited, neighbors4(r, c, h, w)):
2992+
nr, nc = neighbor
2993+
newdist = dist + grid[nr][nc]
2994+
# ...
2995+
```
2996+
2997+
Or using [`itertools.filterfalse()`][py-itertools-filterfalse], exploiting the
2998+
already existing `.__contains__()` built-in method of the `visited` set:
2999+
3000+
```python
3001+
from itertools import filterfalse
3002+
# ...
3003+
3004+
# ...
3005+
for neighbor in filterfalse(visited.__contains__, neighbors4(r, c, h, w)):
3006+
nr, nc = neighbor
3007+
newdist = dist + grid[nr][nc]
3008+
# ...
29773009
```
29783010

29793011
All that's left to do is call the function we just wrote on our grid:
@@ -3146,6 +3178,7 @@ get on the global leaderboard, this time for both parts (79th and 62nd), yay!
31463178
[py-heapq]: https://docs.python.org/3/library/heapq.html
31473179
[py-io-readline]: https://docs.python.org/3/library/io.html#io.IOBase.readline
31483180
[py-itertools-count]: https://docs.python.org/3/library/itertools.html#itertools.count
3181+
[py-itertools-filterfalse]: https://docs.python.org/3/library/itertools.html#itertools.filterfalse
31493182
[py-itertools-product]: https://docs.python.org/3/library/itertools.html#itertools.product
31503183
[py-itertools-repeat]: https://docs.python.org/3/library/itertools.html#itertools.repeat
31513184
[py-itertools-starmap]: https://docs.python.org/3/library/itertools.html#itertools.starmap
@@ -3179,6 +3212,7 @@ get on the global leaderboard, this time for both parts (79th and 62nd), yay!
31793212
[wiki-linked-list]: https://en.wikipedia.org/wiki/Linked_list
31803213
[wiki-median]: https://en.wikipedia.org/wiki/Median
31813214
[wiki-min-heap]: https://en.wikipedia.org/wiki/Binary_heap
3215+
[wiki-priority-queue]: https://en.wikipedia.org/wiki/Priority_queue
31823216
[wiki-pushdown-automata]: https://en.wikipedia.org/wiki/Pushdown_automaton
31833217
[wiki-queue]: https://en.wikipedia.org/wiki/Queue_(abstract_data_type)
31843218
[wiki-reflection]: https://en.wikipedia.org/wiki/Reflection_(mathematics)

0 commit comments

Comments
 (0)