beam: iterative Tarjan SCC (no recursion limit), verified vs recursive oracle + 200k cycle
This commit is contained in:
1 parent
0c4629b120
commit
12a48021e3
2 files changed
+98
No files matched your search
@@ -0,0 +1,55 @@
|
|||||||
|
"""Iterative Tarjan strongly connected components (no recursion limit)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, Iterable, List, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def scc(adj: Dict[int, Iterable[int]]) -> List[List[int]]:
|
||||||
|
index_counter = [0]
|
||||||
|
stack: List[int] = []
|
||||||
|
on_stack: set = set()
|
||||||
|
indices: Dict[int, int] = {}
|
||||||
|
lowlink: Dict[int, int] = {}
|
||||||
|
result: List[List[int]] = []
|
||||||
|
|
||||||
|
for u in list(adj.keys()):
|
||||||
|
if u in indices:
|
||||||
|
continue
|
||||||
|
call_stack: List[Tuple[int, iter]] = [(u, iter(adj.get(u, ())))]
|
||||||
|
indices[u] = lowlink[u] = index_counter[0]
|
||||||
|
index_counter[0] += 1
|
||||||
|
stack.append(u)
|
||||||
|
on_stack.add(u)
|
||||||
|
while call_stack:
|
||||||
|
v, it = call_stack[-1]
|
||||||
|
advanced = False
|
||||||
|
for w in it:
|
||||||
|
if w not in indices:
|
||||||
|
indices[w] = lowlink[w] = index_counter[0]
|
||||||
|
index_counter[0] += 1
|
||||||
|
stack.append(w)
|
||||||
|
on_stack.add(w)
|
||||||
|
call_stack.append((w, iter(adj.get(w, ()))))
|
||||||
|
advanced = True
|
||||||
|
break
|
||||||
|
elif w in on_stack:
|
||||||
|
if indices[w] < lowlink[v]:
|
||||||
|
lowlink[v] = indices[w]
|
||||||
|
call_stack[-1] = (v, it)
|
||||||
|
if advanced:
|
||||||
|
continue
|
||||||
|
call_stack.pop()
|
||||||
|
if call_stack:
|
||||||
|
parent = call_stack[-1][0]
|
||||||
|
if lowlink[v] < lowlink[parent]:
|
||||||
|
lowlink[parent] = lowlink[v]
|
||||||
|
if lowlink[v] == indices[v]:
|
||||||
|
comp = []
|
||||||
|
while True:
|
||||||
|
w = stack.pop()
|
||||||
|
on_stack.discard(w)
|
||||||
|
comp.append(w)
|
||||||
|
if w == v:
|
||||||
|
break
|
||||||
|
result.append(comp)
|
||||||
|
return result
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import sys
|
||||||
|
sys.path.insert(0, "/root/.hermes/wizard-outputs/beam")
|
||||||
|
from scc import scc
|
||||||
|
|
||||||
|
def tarjan_recursive(adj):
|
||||||
|
idx=[0]; stack=[]; on=set(); indices={}; low={}; res=[]
|
||||||
|
import sys as _s
|
||||||
|
_s.setrecursionlimit(100000)
|
||||||
|
def strong(v):
|
||||||
|
indices[v]=low[v]=idx[0]; idx[0]+=1; stack.append(v); on.add(v)
|
||||||
|
for w in adj.get(v,()):
|
||||||
|
if w not in indices:
|
||||||
|
strong(w); low[v]=min(low[v],low[w])
|
||||||
|
elif w in on: low[v]=min(low[v],indices[w])
|
||||||
|
if low[v]==indices[v]:
|
||||||
|
comp=[]
|
||||||
|
while True:
|
||||||
|
w=stack.pop(); on.discard(w); comp.append(w)
|
||||||
|
if w==v: break
|
||||||
|
res.append(comp)
|
||||||
|
for u in list(adj):
|
||||||
|
if u not in indices: strong(u)
|
||||||
|
return res
|
||||||
|
|
||||||
|
import random
|
||||||
|
random.seed(42)
|
||||||
|
for trial in range(200):
|
||||||
|
n=random.randint(1,40)
|
||||||
|
adj={i:[] for i in range(n)}
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(n):
|
||||||
|
if random.random()<0.25: adj[i].append(j)
|
||||||
|
a=sorted(sorted(c) for c in scc(adj))
|
||||||
|
b=sorted(sorted(c) for c in tarjan_recursive(adj))
|
||||||
|
assert a==b, (a,b)
|
||||||
|
print("ALL 200 RANDOM TRIALS PASSED")
|
||||||
|
# big graph no recursion
|
||||||
|
N=200000
|
||||||
|
adj={i:[i+1] for i in range(N-1)}
|
||||||
|
adj[N-1]=[0]
|
||||||
|
r=scc(adj)
|
||||||
|
assert len(r)==1 and len(r[0])==N
|
||||||
|
print("BIG CYCLE OK", len(r[0]))
|
||||||
Reference in new issue
Block a user