-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtrd.py
304 lines (283 loc) · 9.24 KB
/
trd.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#!/usr/bin/env python3
# Wishlist:
# 1. disjoint set operations
# 3. splay tree operations
# 4. all intersections
# 5. tarjan's strongly connected components
# Solving shortlist:
# 1. Dynamic programming (lru_cache)
# 2. Binary search on decision problem
def maxflow(edges, source, sink):
'''Dinic' algorithm, O(V^2 * E)
>>> edges = {1: {2: 3, 3: 3}, 2: {3: 2, 4: 3}, 3: {5: 2},
... 4: {5: 4, 6: 2}, 5: {6: 3}, 6: {}}
>>> maxflow(edges, 1, 6)
5
>>> for i in range(1, 7):
... print(*[edges[i].get(j, '-') for j in range(1, 7)])
- 0 1 - - -
3 - 2 0 - -
2 0 - - 0 -
- 3 - - 3 0
- - 2 1 - 0
- - - 2 3 -
'''
inf = 2 ** 64 # Faster than math.inf
def dfs(x, inflow):
if x == source:
return inflow
rem = inflow
for y in edges[x]:
cap = edges[y][x]
if cap > 0 and level[x] > level.get(y, inf):
used = dfs(y, min(cap, rem))
edges[x][y] += used
edges[y][x] -= used
rem -= used
if used < min(cap, rem):
del level[y]
if rem == 0:
return inflow
return inflow - rem
for x, ys in edges.items():
for y in ys:
edges[y].setdefault(x, 0)
while True:
level = {source: 0}
n = 1
todo = [source]
while todo:
if sink in todo:
break
newtodo = []
for x in todo:
for y, cap in edges[x].items():
if cap > 0 and y not in level:
level[y] = n
newtodo.append(y)
n += 1
todo = newtodo
if sink not in level:
return sum(edges[sink].values())
dfs(sink, inf)
def convexhull(points):
'''Andrew's monotone chain, O(n log n)
>>> convexhull([(1, 1, 'a'), (0, -3, 'b'), (-1, 1, 'c'), (-1, -1, 'd'),
... (3, 0, 'e'), (-3, 0, 'f'), (1, -1, 'g'), (0, 3, 'h')])
[(-3, 0, 'f'), (0, 3, 'h'), (3, 0, 'e'), (0, -3, 'b')]
>>> convexhull([(1, 1), (-1, 1), (1, -1), (0, 0), (-1, -1)])
[(-1, -1), (-1, 1), (1, 1), (1, -1)]
'''
def half(points):
res = []
for p in points:
while len(res) > 1 and \
(p[1] - res[-1][1]) * (res[-1][0] - res[-2][0]) > \
(p[0] - res[-1][0]) * (res[-1][1] - res[-2][1]):
res.pop()
res.append(p)
res.pop()
return res
points = sorted(points)
return half(points) + half(reversed(points))
def dfs(edges, todo):
'''Depth-first search, O(V + E)
>>> edges = {1: [2, 6], 2: [3, 5], 3: [4], 4: [2, 8],
... 5: [4, 7], 6: [5, 7], 7: [6], 8: [7]}
>>> list(dfs(edges, [1]))
[1, 2, 3, 4, 8, 7, 6, 5]
>>> list(dfs({1: [], 2: [1]}, [1]))
[1]
'''
done = set()
while todo:
x = todo.pop()
if x not in done:
done.add(x)
yield x
todo.extend(reversed(edges[x]))
def bfs(edges, todo, finish=[]):
'''Breadth-first search, O(V + E)
>>> edges = {1: [2, 6], 2: [3, 5], 3: [4], 4: [2, 8],
... 5: [4, 7], 6: [5, 7], 7: [6], 8: [7]}
>>> list(bfs(edges, [1]))
[[1], [2, 6], [3, 5, 7], [4], [8]]
>>> list(bfs(edges, [1], [3]))
[[1], [2, 6], [3, 5, 7]]
>>> list(bfs({1: [], 2: [1]}, [1]))
[[1]]
'''
done = set()
while todo:
yield todo
if any(x in finish for x in todo):
return
newtodo = []
for x in todo:
for y in edges[x]:
if y not in done:
done.add(y)
newtodo.append(y)
todo = newtodo
def minspantree2(dists):
'''Prim's algorithm on adjacency matrices, O(V^2)
>>> dists = [[0, 3, 7, 1], [3, 0, 7, 2], [7, 7, 0, 4], [1, 2, 4, 0]]
>>> minspantree2(dists)
{0: {3: 1}, 3: {0: 1, 1: 2, 2: 4}, 1: {3: 2}, 2: {3: 4}}
'''
mst = {0: dict()}
todo = [(d, t, 0) for t, d in enumerate(dists[0]) if t != 0]
while todo:
d, t, s = min(todo)
mst[t] = {s: d}
mst[s][t] = d
todo = [(od, ot, os) if od < dists[ot][t] else (dists[ot][t], ot, t)
for od, ot, os in todo if t != ot]
return mst
def minspantree(edges):
'''Prim's algorithm, O(E log V)
>>> edges = {1: {2: 3, 4: 1}, 2: {1: 3, 4: 2},
... 3: {4: 4}, 4: {1: 1, 2: 2, 3: 4}}
>>> minspantree(edges)
{1: {4: 1}, 4: {1: 1, 2: 2, 3: 4}, 2: {4: 2}, 3: {4: 4}}
'''
from heapq import heappush, heappop, heapify
start = next(iter(edges))
mst = {start: dict()}
frontier = [(d, v, start) for v, d in edges[start].items()]
heapify(frontier)
while frontier:
d, v, p = heappop(frontier)
if v in mst:
continue
mst[v] = {p: d}
mst[p][v] = d
for n, nd in edges[v].items():
heappush(frontier, (nd, n, v))
return mst
def shortestpaths(edges, start, finish=[], h=lambda v: 0):
'''A*, O(E log V)
Distances are only correct for nodes in the shortest path. Without a finish,
they are correct for all nodes. The heuristic function can be used to give
a lower bound of distance from node to goal to aid search.
>>> edges = {1: {2: 7, 3: 9, 6: 14}, 2: {1: 7, 3: 10, 4: 15},
... 3: {1: 9, 2: 10, 4: 11, 6: 2}, 4: {2: 15, 3: 11, 5: 6},
... 5: {4: 6, 6: 9}, 6: {1: 14, 3: 2, 5: 9}}
>>> dist, prev = shortestpaths(edges, [1], [6])
>>> list(reversed(list(getpath(prev, 6))))
[1, 3, 6]
>>> shortestpaths(edges, [1])
({1: 0, 2: 7, 3: 9, 6: 11, 4: 20, 5: 20}, {2: 1, 3: 1, 6: 3, 4: 3, 5: 6})
'''
from heapq import heappush, heappop, heapify
dist = {x: 0 for x in start}
prev = dict()
frontier = [(h(v), v, None) for v in start]
heapify(frontier)
while frontier:
_, v, p = heappop(frontier)
if v in prev:
continue
if p is not None:
prev[v] = p
if v in finish:
break
for u, dvu in edges[v].items():
du = dist[v] + dvu
if u not in dist or du < dist[u]:
dist[u] = du
heappush(frontier, (du + h(u), u, v))
return dist, prev
def getpath(to, node):
'''Get a path from a next-node relation.'''
yield node
while node in to:
node = to[node]
yield node
def primelist(n):
'''Sieve of Eratosthenes, O(n log n)
>>> primelist(23)
[2, 3, 5, 7, 11, 13, 17, 19]
'''
if n < 3:
return []
pl = [True] * (n // 2)
for k in range(3, int(n ** 0.5) + 1, 2):
if pl[k // 2]:
kk = (k * k) // 2
pl[kk::k] = [False] * len(range(kk, n // 2, k))
r = [i for i, x in zip(range(1, n, 2), pl) if x]
r[0] = 2
return r
# LINEAR ALGEBRA
def dist(x1, y1, x2, y2):
'''Distance from point x1, y1 to point x2, y2
>>> dist(1, 1, 4, 5)
5.0
'''
from math import sqrt # sqrt is twice as fast as **0.5
dx, dy = x2 - x1, y2 - y1
return sqrt(dx * dx + dy * dy)
def normalize(x1, y1, x2, y2):
'''Normalize vector to unit length
>>> normalize(1.0, 1.0, 5.0, 1.0)
(1.0, 1.0, 2.0, 1.0)
'''
f = 1 / dist(x1, y1, x2, y2)
return (x1, y1, x1 + (x2 - x1) * f, y1 + (y2 - y1) * f)
def param(x1, y1, x2, y2, x, y):
'''Value of projection of x, y on line if x1, y1 is 0 and x2, y2 is 1
>>> param(1, 1, 2, 3, 3.4, 0.8)
0.4
'''
d1x, d1y = x2 - x1, y2 - y1 # d1 = p2 - p1
d2x, d2y = x - x1, y - y1 # d2 = p - p1
return (d1x * d2x + d1y * d2y) / (d1x * d1x + d1y * d1y) # d1.d2 / d1.d1
def project(x1, y1, x2, y2, x, y):
'''Project point x, y on line defined by x1, y1 and x2, y2
>>> project(1, 1, 2, 3, 3.4, 0.8)
(1.4, 1.8)
'''
p = param(x1, y1, x2, y2, x, y)
return x1 + p * (x2 - x1), y1 + p * (y2 - y1)
def mirror(x1, y1, x2, y2, x, y):
'''Mirror point x, y over line defined by x1, y1 and x2, y2
>>> mirror(1, 1, 3, 2, 2, 4)
(4.0, 0.0)
'''
x3, y3 = project(x1, y1, x2, y2, x, y) # p3 = project(p1, p2, p)
return x3 * 2 - x, y3 * 2 - y # p3 * 2 - p
def linepointdist(x1, y1, x2, y2, x, y):
'''Distance from point x, y to line defined by x1, y1 and x2, y2
>>> linepointdist(1, 1, 4, 5, 5, -2)
5.0
'''
x3, y3 = project(x1, y1, x2, y2, x, y)
return dist(x3, y3, x, y)
def intersection(x1, y1, x2, y2, x3, y3, x4, y4):
'''Intersection point between two lines
>>> intersection(1, 1, 5, 3, 2, 3, 5, 0)
(3.0, 2.0)
'''
d1x, d1y = x1 - x2, y1 - y2
d2x, d2y = x3 - x4, y3 - y4
D = d1x * d2y - d1y * d2x
if D == 0:
return None
p1, p2 = x1 * y2 - y1 * x2, x3 * y4 - y3 * x4
return (p1 * d2x - d1x * p2) / D, (p1 * d2y - d1y * p2) / D
def hasintersection(x1, y1, x2, y2, x3, y3, x4, y4):
'''Wether the two given line segments intersect
>>> hasintersection(1, 1, 2, 3, 2, 1, 1, 4)
True
'''
r = intersection(x1, y1, x2, y2, x3, y3, x4, y4)
if r is None:
return False
x, y = r
p1 = param(x1, y1, x2, y2, x, y)
p2 = param(x3, y3, x4, y4, x, y)
return 0 <= p1 <= 1 and 0 <= p2 <= 1
if __name__ == '__main__':
import doctest
doctest.testmod()