Skip to content

Commit

Permalink
Trac #33743: Faster random tree generator
Browse files Browse the repository at this point in the history
We use a heap to replace a loop searching for the vertex with smallest
index and count 0 with a pop operation. The change is safe as it does
not modify the order in which vertices are considered. This way, the
generator is no longer quadratic.

Before (with #33579)
{{{
sage: %time T = graphs.RandomTree(10, seed=0)
CPU times: user 380 µs, sys: 84 µs, total: 464 µs
Wall time: 448 µs
sage: %time T = graphs.RandomTree(100, seed=0)
CPU times: user 1.44 ms, sys: 118 µs, total: 1.56 ms
Wall time: 1.57 ms
sage: %time T = graphs.RandomTree(1000, seed=0)
CPU times: user 46.1 ms, sys: 2.34 ms, total: 48.5 ms
Wall time: 47.1 ms
sage: %time T = graphs.RandomTree(10000, seed=0)
CPU times: user 2.75 s, sys: 14.8 ms, total: 2.77 s
Wall time: 2.78 s
}}}
After
{{{
sage: %time T = graphs.RandomTree(10, seed=0)
CPU times: user 363 µs, sys: 60 µs, total: 423 µs
Wall time: 407 µs
sage: %time T = graphs.RandomTree(100, seed=0)
CPU times: user 1.19 ms, sys: 629 µs, total: 1.82 ms
Wall time: 1.26 ms
sage: %time T = graphs.RandomTree(1000, seed=0)
CPU times: user 6.52 ms, sys: 156 µs, total: 6.68 ms
Wall time: 6.68 ms
sage: %time T = graphs.RandomTree(10000, seed=0)
CPU times: user 59.3 ms, sys: 3.63 ms, total: 63 ms
Wall time: 62 ms
}}}

URL: https://trac.sagemath.org/33743
Reported by: dcoudert
Ticket author(s): David Coudert
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed May 22, 2022
2 parents 773ec37 + ada25e3 commit 8943dc0
Showing 1 changed file with 13 additions and 9 deletions.
22 changes: 13 additions & 9 deletions src/sage/graphs/generators/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -1380,29 +1380,33 @@ def RandomTree(n, seed=None):
set_random_seed(seed)

# create random Prufer code
code = [ randint(0,n-1) for i in range(n-2) ]
code = [randint(0, n - 1) for i in range(n - 2)]

# We count the number of symbols of each type.
# count[k] is the no. of times k appears in code
# count[k] is the number of times k appears in code
#
# (count[k] is set to -1 when the corresponding vertex is not
# available anymore)
count = [0] * n
for k in code:
count[k] += 1

for s in code:
for x in range(n):
if count[x] == 0:
break
# We use a heap to store vertices for which count[k] == 0 and get the vertex
# with smallest index
from heapq import heapify, heappop, heappush
zeros = [x for x in range(n) if not count[x]]
heapify(zeros)

for s in code:
x = heappop(zeros)
g.add_edge(x, s)
count[x] = -1
g.add_edge(x,s)
count[s] -= 1
if not count[s]:
heappush(zeros, s)

# Adding as an edge the last two available vertices
last_edge = [ v for v in range(n) if count[v] != -1 ]
g.add_edge(last_edge)
g.add_edge(zeros)

return g

Expand Down

0 comments on commit 8943dc0

Please sign in to comment.