Link Graph Encyclopedia

Computing at scale

Brandes' algorithm

Exact betweenness centrality for every node in O(nm) time. One breadth-first search per source counts shortest paths, and one backward sweep hands each node its share.

Betweenness centrality asks, for every node, what fraction of the shortest paths between other pairs pass through it. Summing over all pairs naively costs O(n3)O(n^3) or more. In 2001 Ulrik Brandes showed how to compute every node’s betweenness in O(nm)O(nm) time on unweighted graphs, by processing one source at a time and accumulating each node’s share in a single backward sweep. Every major graph library implements his algorithm, and every approximate method for large graphs builds on it.

Counting shortest paths

From a source ss, a breadth-first search finds each node’s distance d(s,v)d(s, v) and the number of shortest paths σsv\sigma_{sv} from ss to vv. Paths are counted along the way: σss=1\sigma_{ss} = 1, and for every node vv,

σsv=∑u∈preds(v)σsu,\sigma_{sv} = \sum_{u \in \text{pred}_s(v)} \sigma_{su},

where preds(v)\text{pred}_s(v) holds the nodes uu with an arc u→vu \to v and d(s,u)+1=d(s,v)d(s, u) + 1 = d(s, v). The search also records the order in which nodes are reached.

Accumulating dependencies

The dependency of ss on vv is the share of shortest paths from ss, to all targets, that pass through vv:

δs(v)=∑t≠s,vσst(v)σst.\delta_s(v) = \sum_{t \ne s, v} \frac{\sigma_{st}(v)}{\sigma_{st}} .

Brandes’ key observation is a recurrence that computes all dependencies from the far end back:

δs(v)=∑w : v∈preds(w)σsvσsw(1+δs(w)).\delta_s(v) = \sum_{w \,:\, v \in \text{pred}_s(w)} \frac{\sigma_{sv}}{\sigma_{sw}} \bigl( 1 + \delta_s(w) \bigr) .

Each successor ww on a shortest path passes back to vv the fraction of its paths that come through vv, for the paths ending at ww itself (the 1) and for those continuing beyond it (δs(w)\delta_s(w)). Visiting nodes in reverse BFS order guarantees that δs(w)\delta_s(w) is final before vv needs it. The betweenness is the sum over sources:

b(v)=∑s≠vδs(v).b(v) = \sum_{s \ne v} \delta_s(v) .

Worked example

Take $s = $ E in the running example. Breadth-first search reaches C, D and F at distance 1, A and G at distance 2, and B at distance 3. A has two shortest paths, through C and through D, so σEA=2\sigma_{EA} = 2; B inherits them, σEB=2\sigma_{EB} = 2; every other node has one.

Sweeping back from the farthest node:

Source E thus gives C 2, A 1 and D 1. Repeating from every source and summing gives the final betweenness of the running example: C 6, A 3, D 1, and 0 for the rest. The lab on the betweenness centrality page runs exactly this code.

Cost

Each source costs one BFS, O(n+m)O(n + m), and one backward sweep over the same arcs, so all sources cost O(n(n+m))O(n(n + m)), written O(nm)O(nm) for connected graphs. Memory is O(n+m)O(n + m): one source’s distances, path counts, predecessor lists and dependencies at a time, plus the betweenness totals. On weighted graphs, Dijkstra’s algorithm replaces BFS and the cost becomes O(nm+n2log⁡n)O(nm + n^2 \log n).

Sources are independent, so the algorithm parallelises perfectly across sources: each thread processes its own sources and keeps its own totals, summed at the end.

Too slow for the web, so sample

O(nm)O(nm) is quadratic in practice. For Common Crawl’s domain graph it means about 3×10173 \times 10^{17} arc visits. The standard remedy samples:

References

Edit this entry on GitHub