Comments on algorithm
Your current algorithm is O(N^3) This can definitely be turned into O(N^2)
From reading the code you want to build a list of all sub-graphs that exist in your graph.
I think you are doing t wrong and building several of the subsets repeatedly.
There is a simple technique for this.
Think of each node in the graph as represented by a bit in a very large integer where the integer has one bit for every node in the graph. Node 0 is bit 0 and Node 5 is bit 5 etc. Thus the full graph is represented by the integer with all the bits set to one.
Any sub set of the graph is then represented by an integer with some bits off and some bits on. Thus you can generate all the different sub-sets of the graph by looping through all bit patterns in the integer and this can be done simply by starting at 1 and incrementing the integer until all the bits have been set (Each increment represents a new set).
Assuming you have more nodes than will fit into a normal integer you should probably use the boost::dynamic_bitset(If you have 64 or fewer nodes you can use a normal integer (assuming you have 64 bit integer type)).
/*
* This function adds 1 to the bitset.
*
* Since the bitset does not natively support addition we do it manually.
* If XOR a bit with 1 leaves it as one then we did not overflow so we can break out
* otherwise the bit is zero meaning it was previously one which means we have a bit
* overflow which must be added 1 to the next bit etc.
*/
void increment(boost::dynamic_bitset<>& bitset)
{
for(int loop = 0;loop < bitset.count(); ++loop)
{
if ((bitset[loop] ^= 0x1) == 0x1)
{ break;
}
}
}
int cliqueSize(graph &g, std::vector<VertexID> const& nodes, std::vector<EdgeID> const& edges)
{
static boost::dynamic_bitset empty;
empty.resize(nodes.size());
int cliquesCount = 0;
boost::dynamic_bitset<> set(nodes.size());
set[0] = 1;
while(set != empty()) // break when all bits are zero.
{
// Build the next potential clique
// Every bit that is a 1 in set means that that nodes is in the clique.
std::vector<VertexID> cliques;
for(int loop = 0;loop < nodes.size(); ++loop)
{
if (set[loop])
{
cliques.push_back(nodes[loop]);
}
}
// If this is a clique increment the count.
if (isClique(g, cliques, edges))
{
++cliquesCount;
}
// Increment the set bits by 1 each loop.
// This effectively iterates through all different bit patterns.
increment(set)
}
return cliquesCount;
}
Comment on code
You should prefer to pass parameters by const reference if you can.
int cliqueSize(graph &g, std::vector<VertexID> const& nodes, std::vector<EdgeID> const& edges)
^^^^^^^^ ^^^^^^^^
std::vector<std::vector<VertexID> > previous_cliques;
The following can be simplified a bit:
std::vector<VertexID> tmp;
tmp.push_back(nodes[i]);
previous_cliques.push_back(tmp);
// Easier to read like this:
previous_cliques.push_back(std::vector<VertexID>(1, nodes[i]));
// If you have C++11 (emplace_back uses move semantics to put the object in the vector)
previous_cliques.emplace_back(std::vector<VertexID>(1, nodes[i]));
Prefer to use pre-increment for loop variables.
for (unsigned clique_size = 1; clique_size < nodes.size(); ++clique_size)
^^
Though it makes no difference for POD types. It gets you in the habit of always using the pre-increment version. This will help you out when the type is not an integer type as generally speaking the pre-increment is more efficient. Also if you change the type from integer (to say iterator) it will automatically be the most efficient version.
You are returning the wrong value:
return nodes.size();
// I think you mean:
return new_cliques.size();