Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 87c92e6

Browse files
refactor: unify random walk methods to use IndexedGraph traversal
hittingTime() and returnTime() still used raw Collection<V> neighbor iteration (graph.getNeighbors + Collection indexing), while the later- optimized hittingTimesFrom() and coverTime() already used the int[][] IndexedGraph for cache-friendly, boxing-free traversal. Refactored both methods to build an IndexedGraph upfront and walk via int[] adjacency arrays. This eliminates: - Per-step Collection.size() + iterator/index overhead in hot loops - Object equality checks (V.equals) replaced by int == comparison - Inconsistent traversal strategies across the same class Also removed the now-unused simulateWalkToTarget() and simulateReturnWalk() helper methods (~25 lines of dead code).
1 parent a7445ff commit 87c92e6

1 file changed

Lines changed: 40 additions & 37 deletions

File tree

Gvisual/src/gvisual/RandomWalkAnalyzer.java

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,31 @@ public <V, E> double hittingTime(Graph<V, E> graph, V source, V target) {
4545
validateNode(graph, target, "target");
4646
if (source.equals(target)) return 0.0;
4747

48+
// Use IndexedGraph for cache-friendly int[][] adjacency traversal,
49+
// consistent with hittingTimesFrom/coverTime (was previously using
50+
// raw Collection<V> neighbor iteration via simulateWalkToTarget).
51+
IndexedGraph<V> ig = buildIndexedGraph(graph, graph.getVertices());
52+
int sourceIdx = ig.indexOf(source);
53+
int targetIdx = ig.indexOf(target);
54+
int maxSteps = ig.size * ig.size * 10;
55+
4856
long totalSteps = 0;
4957
int reached = 0;
50-
int maxSteps = graph.getVertexCount() * graph.getVertexCount() * 10;
5158

5259
for (int sim = 0; sim < defaultSimulations; sim++) {
53-
int steps = simulateWalkToTarget(graph, source, target, maxSteps);
54-
if (steps >= 0) { totalSteps += steps; reached++; }
60+
int currentIdx = sourceIdx;
61+
boolean found = false;
62+
for (int step = 1; step <= maxSteps; step++) {
63+
int[] nbrs = ig.adj[currentIdx];
64+
if (nbrs.length == 0) break;
65+
currentIdx = nbrs[rng.nextInt(nbrs.length)];
66+
if (currentIdx == targetIdx) {
67+
totalSteps += step;
68+
reached++;
69+
found = true;
70+
break;
71+
}
72+
}
5573
}
5674
return reached == 0 ? Double.POSITIVE_INFINITY : (double) totalSteps / reached;
5775
}
@@ -192,10 +210,27 @@ public <V, E> double returnTime(Graph<V, E> graph, V node) {
192210
validateGraph(graph);
193211
validateNode(graph, node, "node");
194212
if (graph.degree(node) == 0) return Double.POSITIVE_INFINITY;
213+
214+
// Use IndexedGraph for cache-friendly int[][] adjacency traversal,
215+
// consistent with hittingTimesFrom/coverTime (was previously using
216+
// raw Collection<V> neighbor iteration via simulateReturnWalk).
217+
IndexedGraph<V> ig = buildIndexedGraph(graph, graph.getVertices());
218+
int nodeIdx = ig.indexOf(node);
219+
int maxSteps = ig.size * ig.size * 10;
220+
195221
long totalSteps = 0;
196-
int maxSteps = graph.getVertexCount() * graph.getVertexCount() * 10;
197222
for (int sim = 0; sim < defaultSimulations; sim++) {
198-
totalSteps += simulateReturnWalk(graph, node, maxSteps);
223+
int[] nbrs = ig.adj[nodeIdx];
224+
if (nbrs.length == 0) { totalSteps += maxSteps; continue; }
225+
int currentIdx = nbrs[rng.nextInt(nbrs.length)];
226+
int steps = maxSteps;
227+
for (int step = 2; step <= maxSteps; step++) {
228+
if (currentIdx == nodeIdx) { steps = step; break; }
229+
int[] curNbrs = ig.adj[currentIdx];
230+
if (curNbrs.length == 0) break;
231+
currentIdx = curNbrs[rng.nextInt(curNbrs.length)];
232+
}
233+
totalSteps += steps;
199234
}
200235
return (double) totalSteps / defaultSimulations;
201236
}
@@ -341,25 +376,6 @@ public WalkSummary(int nc, int ec, Map<V, Double> sd, V mv, double mvp,
341376

342377
// ── Private Helpers ────────────────────────────────────────────────
343378

344-
private <V, E> int simulateWalkToTarget(Graph<V, E> graph, V source, V target, int maxSteps) {
345-
V current = source;
346-
for (int step = 1; step <= maxSteps; step++) {
347-
Collection<V> nbrs = graph.getNeighbors(current);
348-
if (nbrs == null || nbrs.isEmpty()) return -1;
349-
// Skip to a random neighbor without copying the full collection.
350-
int idx = rng.nextInt(nbrs.size());
351-
V next = null;
352-
if (nbrs instanceof List) {
353-
next = ((List<V>) nbrs).get(idx);
354-
} else {
355-
for (V v : nbrs) { if (idx-- == 0) { next = v; break; } }
356-
}
357-
current = next;
358-
if (current.equals(target)) return step;
359-
}
360-
return -1;
361-
}
362-
363379
/** BFS to find all vertices reachable from {@code source}. */
364380
private <V, E> Set<V> bfsReachable(Graph<V, E> graph, V source) {
365381
Set<V> reachable = new HashSet<>();
@@ -375,19 +391,6 @@ private <V, E> Set<V> bfsReachable(Graph<V, E> graph, V source) {
375391
return reachable;
376392
}
377393

378-
private <V, E> long simulateReturnWalk(Graph<V, E> graph, V node, int maxSteps) {
379-
Collection<V> nbrs = graph.getNeighbors(node);
380-
if (nbrs == null || nbrs.isEmpty()) return maxSteps;
381-
V current = pickRandom(nbrs);
382-
for (int step = 2; step <= maxSteps; step++) {
383-
if (current.equals(node)) return step;
384-
nbrs = graph.getNeighbors(current);
385-
if (nbrs == null || nbrs.isEmpty()) return maxSteps;
386-
current = pickRandom(nbrs);
387-
}
388-
return maxSteps;
389-
}
390-
391394
/** Pick a random element from a collection without copying it. */
392395
private <V> V pickRandom(Collection<V> coll) {
393396
int idx = rng.nextInt(coll.size());

0 commit comments

Comments
 (0)