|
Boost : |
From: Magnus Ekdahl (maguno_at_[hidden])
Date: 2006-08-24 14:47:23
Douglas Gregor wrote:
>On Aug 24, 2006, at 4:49 AM, Magnus Ekdahl wrote:
>
>
>>First let me argue that there is room for improvement. I have made a
>>test program that constructs 1000 random complete graphs with 1000
>>nodes
>>and the execution time is as follows
>>
>>actually implementing jarniks algorithm
>>prim_minimum_spanning_tree 47.585s
>>
>>current prim (djikstra wrapper) 3m2638s (338% slower)
>>
>>kruskal_minimum_spanning_tree 40m57s (5063% slower)
>>
>>
>
>Those times seem... horrible. What compiler are you using?
>
g++ (GCC) 4.0.4 20060630 (prerelease) (Debian 4.0.3-5)
>With which
>optimization flags?
>
g++ -O3 -g0 -mtune=pentium4
>Can you post the sample code, so we can see what
>is going wrong?
>
>
>
attached :)
>>I also want to take the opportunity to ask about the meaning of the
>>minimum spanning tree algorithm as such, since efficient
>>implementations
>>in the future could be helped a lot by knowing exactly what the
>>problem
>>that should be solved is.
>>
>>
>
>Wikipedia has a decent definition:
>
> http://en.wikipedia.org/wiki/Minimum_spanning_tree
>
>
Nice, the key here is undirected graph, which in the boost context would
mean no parallel edges and self loops I guess.
>
>
>>Given the hardness to understand the actual generality of the MST
>>problem the current implementation tries to solve, I find it difficult
>>to see how efficient algorithms can be heuristically selected. I.e.
>>can
>>you use num_edges(g) as an indicator of sparseness?
>>
>>
>
>We would need to run some benchmarks to determine what heuristic to
>use to dynamically select the most efficient algorithm. We haven't
>really done this yet for the BGL, but we should. MST is one good
>example; all-pairs shortest-paths is another good example, because,
>e.g., the Floyd-Warshall algorithm is much better for dense graphs
>while Johnson's algorithm is much better for sparse graphs. I imagine
>that we could use some kind of threshold for num_edges(g)/
>(num_vertices(g)*num_vertices(g)) to decide whether a graph is sparse
>or dense.
>
>
I'll take this into account. For MST the relaxed_heap is surprisingly
fast though, so it will be an issue when something faster is found.
My original intension was to submit an O(|E|) array implementation for
the complete graph case. But it is for current test cases slower than
prim/relaxed so it have to be improved first (if at all possible).
-- Magnus Ekdahl
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <iostream>
#include <cstdlib>
#include<string>
#include<list>
#include <iostream>
#include "mst.h"
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <iostream>
#include <vector>
#include <boost/graph/random.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <algorithm>
#include <boost/pending/fibonacci_heap.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/pending/relaxed_heap.hpp>
using namespace std;
using namespace boost;
typedef adjacency_list < vecS, vecS, undirectedS, no_property, property < edge_weight_t, double > > Graph;
typedef graph_traits < Graph >::edge_descriptor Edge;
typedef graph_traits < Graph >::vertex_descriptor Vertex;
typedef std::pair<int, int> E;
void initGraph(Graph& g,const unsigned size)
{
for(unsigned i=0;i<size;i++)
{
for(unsigned j=i+1;j<size;j++)
add_edge(i, j, g);
}
}
void randGraph(Graph& g, const unsigned size)
{
property_map<Graph, edge_weight_t>::type weightmap = get(edge_weight, g);
graph_traits<Graph>::edge_iterator ei, eiend;
for (boost::tie(ei, eiend) = edges(g); ei != eiend; ++ei)
{
weightmap[*ei] = rand();
}
}
int main(int argc, char* argv[])
{
srand(0);
const unsigned NR_TESTS=1000;
const unsigned size=1000;
vector < Edge > spanning_tree_mwst;
Graph g(size);
std::vector < graph_traits < Graph >::vertex_descriptor > p(num_vertices(g));
initGraph(g,size);
cout << "Generating randum graphs with weights from 0 to "<< RAND_MAX << " of size "<< size <<"\n";
for(unsigned i=0;i<NR_TESTS;i++)
{
randGraph(g,size);
//prim_minimum_spanning_tree2(g,std::back_inserter(spanning_tree_mwst));
prim_minimum_spanning_tree(g,&p[0]);
spanning_tree_mwst.clear();
}
}
/***************************************************************************
* Copyright (C) 2006 by Magnus Ekdahl *
* maguno_at_[hidden] *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
#ifndef __MST_HH__
#define __MST_HH__
#include <map>
#include <functional>
#include <boost/graph/adjacency_list.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/pending/relaxed_heap.hpp>
#include <iostream>
namespace boost
{
namespace detail
{
// This is Prim's algorithm to calculate the Minimum Spanning Tree
// for an undirected graph with weighted edges.
template <class Graph, class OutputIterator, class Weight>
inline void
prim_mst_impl(const Graph& G,
OutputIterator spanning_tree_edges,Weight weight)
{
function_requires<VertexListGraphConcept<Graph> >();
typedef typename graph_traits<Graph>::edge_descriptor Edge;
function_requires<ReadablePropertyMapConcept<Weight, Edge> >();
typedef typename property_traits<Weight>::value_type W_value;
function_requires<ComparableConcept<W_value> >();
if (num_vertices(G) <= 1) return; // Nothing to do in this case
typedef typename graph_traits<Graph>::vertices_size_type size_type;
typedef typename boost::property_map<Graph, vertex_index_t>::type IndexMap;
IndexMap index = get(vertex_index, G);
const size_type size = num_vertices(G);
W_value* the_weights = new W_value[size];
Edge* the_edges = new Edge[size];
bool* vertices_in_mwst = new bool[size];
bool* discovered_vertices = new bool[size];
typedef indirect_cmp<W_value*,std::less<W_value> > ICmp;
ICmp cmp(&the_weights[0], std::less<W_value>());
relaxed_heap<size_type, ICmp> Q(size, cmp);
Q.push(0);
while(Q.empty()!=true)
{
const size_type u=Q.top();
Q.pop();
vertices_in_mwst[u]=true;
typename graph_traits<Graph>::out_edge_iterator ei, eiend;
for (boost::tie(ei,eiend)=out_edges(u,G); ei != eiend; ++ei)
{
const size_type v = index[target(*ei,G)];
if (vertices_in_mwst[v]==false)
{
W_value w = get(edge_weight, G,*ei);
if(discovered_vertices[v]==false)
{
discovered_vertices[v]=true;
the_weights[v]=w;
the_edges[v]=*ei;
Q.push(v);
}
else if(w<the_weights[v])
{
the_weights[v]=w;
the_edges[v]=*ei;
Q.update(v);
}
}
}
}
for(size_type i=1;i<size;++i)
*spanning_tree_edges++ = the_edges[i];
delete the_weights;
delete the_edges;
delete vertices_in_mwst;
delete discovered_vertices;
}
}
template <class VertexListGraph, class PredecessorMap>
inline void prim_minimum_spanning_tree2
(const VertexListGraph& g, PredecessorMap p_map)
{
detail::prim_mst_impl(g, p_map,get(edge_weight, g));
}
}
#endif
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk