In short, my naive code (in Ruby) looks like this:
# $seen is a hash to memoize previously seen sets # $sparse is a hash of usernames to a list of neighboring usernames # $set is the list of output clusters $seen = {} def subgraph(set, adj) hash = (set + adj).sort return if $seen[hash] $sets.push set.sort.join(", ") if adj.empty? and set.size > 2 adj.each {|node| subgraph(set + [node], $sparse[node] & adj)} $seen[hash] = true end $sparse.keys.each do |vertex| subgraph([vertex], $sparse[vertex]) end
And my implementation of Bron Kerbosch:
def bron_kerbosch(set, points, exclude) $sets.push set.sort.join(', ') if set.size > 2 and exclude.empty? and points.empty? points.each_with_index do |vertex, i| points[i] = nil bron_kerbosch(set + [vertex], points & $sparse[vertex], exclude & $sparse[vertex]) exclude.push vertex end end bron_kerbosch [], $sparse.keys, []
I also tweaked the rotation and degeneration, which reduced the runtime of bron_kerbosch, but not enough to overtake my original solution. That seems to be the case; What kind of algorithmic insight do I miss? Below is a writeup with more details if you need to see fully working code. I tested this on pseudo-random settings up to a million or so in size.
Vincent woo
source share