-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpath.py
134 lines (108 loc) · 2.69 KB
/
path.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
Code for working with Dyck paths.
There are two useful representations:
Dyck paths are represented as numbers of missing boxes in
each downward row. For example, the following Dyck paths:
/\
/\/\
/\/\/\
(0, 0, 0)
/\/\
/\/\/\
(0, 0, 1)
/\
/\/\/\
(0, 0, 2)
/\
/\/\/\
(0, 1, 1)
/\/\/\
(0, 1, 2)
To run some tests for this module, use the command:
$ python path.py
For more verbose output, use:
$ python path.py -v
"""
__all__ = [
'boxes_under_path',
'is_path',
'iter_path',
]
# ---------------------------------------------------------
import itertools as it
# ---------------------------------------------------------
def is_path(p):
r"""
Check whether `p` is a valid Dyck path.
The entries of `p` give the number of boxes above the path in each row.
>>> is_path((0, 0, 0))
True
>>> is_path((0, 0, 1))
True
>>> is_path((0, 0, 2))
True
>>> is_path((0, 1, 1))
True
>>> is_path((0, 1, 2))
True
>>> is_path((0, 2, 1))
False
"""
return (isinstance(p, tuple) and
sorted(p) == list(p) and
all(p[i] in range(i+1)
for i in range(len(p))))
def iter_path(n):
r"""
Return an iterator over all valid Dyck paths of length `n`.
"""
if n == 0:
yield ()
return
elif n == 1:
yield (0,)
return
elif n >= 2:
for head in iter_path(n-1):
for tail in range(head[-1], n):
yield head + (tail,)
_boxes_cache = {}
def boxes_under_path(p):
try:
return _boxes_cache[p]
except KeyError:
result = _boxes_compute(p)
_boxes_cache[p] = result
return result
def _boxes_compute(p):
r"""
Return the set of boxes `(i, j)` below the path `p`.
>>> sorted(boxes_under_path((0, 0, 0)))
[(0, 1), (0, 2), (1, 2)]
>>> sorted(boxes_under_path((0, 0, 1)))
[(0, 1), (1, 2)]
>>> sorted(boxes_under_path((0, 0, 2)))
[(0, 1)]
>>> sorted(boxes_under_path((0, 1, 1)))
[(1, 2)]
>>> sorted(boxes_under_path((0, 1, 2)))
[]
"""
return {(i, j) for j, k in enumerate(p) for i in range(k, j)}
# ---------------------------------------------------------
def test_is_path(below=7):
r"""
Test that `is_path` corresponds to `iter_path`.
>>> test_is_path()
"""
for n in range(below):
expected = list(iter_path(n))
actual = filter(is_path, it.product(range(n), repeat=n))
assert expected == actual
# ---------------------------------------------------------
if __name__ == '__main__':
import doctest
doctest.testmod()
# ---------------------------------------------------------