flightgear-0001: addSegment/addParking std::find on m_nodes vector O(S*N), 50x flightgear-0002: Dijkstra findShortestRoute vector unvisited O(V^2), 13.6x flightgear-0003: A* airway search linear findInOpen O(E*V), 4x
147 lines
4 KiB
Python
147 lines
4 KiB
Python
"""
|
|
flightgear-0002: FGGroundNetwork::findShortestRoute Dijkstra O(V^2)
|
|
DEFECT: vector as unvisited set, linear min-scan + std::remove per iteration
|
|
FIX: priority queue O((V+E) log V)
|
|
|
|
Simulates airport ground network Dijkstra pathfinding.
|
|
Large airports have 200-500 taxiway nodes.
|
|
"""
|
|
import time
|
|
import heapq
|
|
import random
|
|
import math
|
|
|
|
|
|
def build_ground_network(n_nodes):
|
|
"""Build a random ground network graph (adjacency list).
|
|
Each node connects to 2-4 neighbors, simulating taxiway topology.
|
|
"""
|
|
adj = {i: [] for i in range(n_nodes)}
|
|
for i in range(n_nodes):
|
|
for offset in [1, 3, 7]:
|
|
j = (i + offset) % n_nodes
|
|
w = random.uniform(50.0, 500.0) # edge weight (meters)
|
|
adj[i].append((j, w))
|
|
adj[j].append((i, w))
|
|
return adj
|
|
|
|
|
|
def dijkstra_unpatched(adj, start, end):
|
|
"""Original: vector unvisited + linear min-scan + linear remove."""
|
|
INF = float('inf')
|
|
n = len(adj)
|
|
dist_map = {i: INF for i in range(n)}
|
|
prev_map = {i: None for i in range(n)}
|
|
unvisited = list(range(n))
|
|
|
|
dist_map[start] = 0.0
|
|
|
|
ops = 0
|
|
while unvisited:
|
|
# Linear scan to find minimum (O(V) per iteration)
|
|
best = unvisited[0]
|
|
for node in unvisited:
|
|
ops += 1
|
|
if dist_map[node] < dist_map[best]:
|
|
best = node
|
|
|
|
# Linear remove (O(V) per iteration)
|
|
unvisited.remove(best)
|
|
ops += len(unvisited)
|
|
|
|
if best == end:
|
|
break
|
|
|
|
if dist_map[best] == INF:
|
|
break
|
|
|
|
for neighbor, weight in adj[best]:
|
|
alt = dist_map[best] + weight
|
|
if alt < dist_map[neighbor]:
|
|
dist_map[neighbor] = alt
|
|
prev_map[neighbor] = best
|
|
|
|
return dist_map[end], ops
|
|
|
|
|
|
def dijkstra_patched(adj, start, end):
|
|
"""Patched: priority queue + visited set."""
|
|
INF = float('inf')
|
|
n = len(adj)
|
|
dist_map = {i: INF for i in range(n)}
|
|
visited = set()
|
|
|
|
dist_map[start] = 0.0
|
|
pq = [(0.0, start)]
|
|
|
|
ops = 0
|
|
while pq:
|
|
d, best = heapq.heappop(pq)
|
|
ops += 1
|
|
|
|
if best in visited:
|
|
continue
|
|
visited.add(best)
|
|
|
|
if best == end:
|
|
break
|
|
|
|
for neighbor, weight in adj[best]:
|
|
alt = d + weight
|
|
ops += 1
|
|
if alt < dist_map[neighbor]:
|
|
dist_map[neighbor] = alt
|
|
heapq.heappush(pq, (alt, neighbor))
|
|
|
|
return dist_map[end], ops
|
|
|
|
|
|
def test_correctness():
|
|
"""Verify both produce same shortest distance."""
|
|
random.seed(42)
|
|
adj = build_ground_network(100)
|
|
d_unpatched, _ = dijkstra_unpatched(adj, 0, 50)
|
|
d_patched, _ = dijkstra_patched(adj, 0, 50)
|
|
assert abs(d_unpatched - d_patched) < 1e-6, \
|
|
f"Distances differ: {d_unpatched} vs {d_patched}"
|
|
print(f"PASS correctness: distance={d_unpatched:.2f}")
|
|
|
|
|
|
def test_performance():
|
|
"""Benchmark at V=400 (large airport ground network)."""
|
|
random.seed(42)
|
|
n = 400
|
|
adj = build_ground_network(n)
|
|
|
|
iters = 10
|
|
pairs = [(random.randint(0, n-1), random.randint(0, n-1)) for _ in range(iters)]
|
|
|
|
t0 = time.perf_counter()
|
|
ops_u = 0
|
|
for s, e in pairs:
|
|
_, o = dijkstra_unpatched(adj, s, e)
|
|
ops_u += o
|
|
t_unpatched = time.perf_counter() - t0
|
|
|
|
t0 = time.perf_counter()
|
|
ops_p = 0
|
|
for s, e in pairs:
|
|
_, o = dijkstra_patched(adj, s, e)
|
|
ops_p += o
|
|
t_patched = time.perf_counter() - t0
|
|
|
|
ratio = t_unpatched / t_patched if t_patched > 0 else float('inf')
|
|
op_ratio = ops_u / ops_p if ops_p > 0 else float('inf')
|
|
print(f"V={n}, {iters} pathfinds")
|
|
print(f"Unpatched: {t_unpatched*1e3:.1f} ms, {ops_u} ops")
|
|
print(f"Patched: {t_patched*1e3:.1f} ms, {ops_p} ops")
|
|
print(f"Time ratio: {ratio:.1f}x")
|
|
print(f"Ops ratio: {op_ratio:.1f}x")
|
|
|
|
assert ratio > 2.0, f"Expected >2x speedup, got {ratio:.1f}x"
|
|
print(f"PASS performance: {ratio:.1f}x speedup")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_correctness()
|
|
test_performance()
|