I am using a d_ary_heap_indirect as a priority queue (to process items with the highest priority first) using a property map to store the priorities. However, when I change the values in the priority property map and push vertices that are already in the queue into the queue again, it results in kind of an invalid state where the vertex appears in the queue twice at different positions.
Here is a demo:
#include <iostream>
#include <iomanip>
#include <boost/graph/grid_graph.hpp>
#include <boost/graph/detail/d_ary_heap.hpp>
#include <boost/property_map/property_map.hpp>
#include <cstdlib>
template <typename TQueue>
static void OutputQueue(TQueue queue);
int main(int, char*[])
{
srand((unsigned int)time(NULL));
srand48((unsigned int)time(NULL));
boost::array<std::size_t, 2> lengths = { { 2,2 } };
typedef boost::grid_graph<2> GraphType;
GraphType graph(lengths);
typedef boost::graph_traits<GraphType>::vertex_descriptor Vertex;
typedef boost::property_map<GraphType, boost::vertex_index_t>::const_type GridIndexMapType;
GridIndexMapType gridIndexMap(get(boost::vertex_index, graph));
typedef boost::vector_property_map<std::size_t, GridIndexMapType> IndexInHeapMap;
IndexInHeapMap index_in_heap(gridIndexMap);
typedef boost::graph_traits<GraphType>::vertex_iterator VertexIteratorType;
typedef boost::vector_property_map<float, GridIndexMapType> PriorityMapType;
PriorityMapType priorityMap(gridIndexMap);
VertexIteratorType vertexIterator, vertexIteratorEnd;
typedef std::greater<float> ComparisonFunctor;
typedef boost::d_ary_heap_indirect<Vertex, 4, IndexInHeapMap, PriorityMapType, ComparisonFunctor > MutableQueueType;
ComparisonFunctor comparisonFunctor;
MutableQueueType mutableQueue(priorityMap, index_in_heap, comparisonFunctor);
std::cout << "There are " << mutableQueue.size() << " items in the queue." << std::endl;
// Add random values to the vertices and add them to the queue
for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
{
put(priorityMap, *vertexIterator, rand() % 1000);
}
for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
{
mutableQueue.push(*vertexIterator);
}
std::cout << "There are " << mutableQueue.size() << " items in the queue." << std::endl;
std::cout << "The priority queue is: " << std::endl;
OutputQueue(mutableQueue);
// Insert another set of random values for each vertex
for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
{
float newPriority = rand() % 1000;
std::cout << "New priority for " << vertexIterator->operator[](0) << ", " << vertexIterator->operator[](1) << " " << newPriority << std::endl;
put(priorityMap, *vertexIterator, newPriority);
}
for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
{
//mutableQueue.push(*vertexIterator); // This makes sense that the queue would not end up sorted
mutableQueue.push_or_update(*vertexIterator); // I thought this one should work
//mutableQueue.update(*vertexIterator); // This one actually seems to UNsort the queue?
}
std::cout << "There are " << mutableQueue.size() << " items in the queue." << std::endl;
std::cout << "The priority queue is: " << std::endl;
OutputQueue(mutableQueue);
std::cout << std::endl;
return 0;
}
template <typename TQueue>
static void OutputQueue(TQueue queue)
{
while( ! queue.empty() )
{
typename TQueue::value_type u = queue.top();
// These two lines are equivalent
std::cout << "vertex: " << u[0] << " " << u[1] << " priority: " << get(queue.keys(), u) << std::endl;
queue.pop();
}
}
And a demo output:
There are 0 items in the queue.
There are 4 items in the queue.
The priority queue is:
vertex: 1 1 priority: 445
vertex: 0 0 priority: 150
vertex: 0 1 priority: 84
vertex: 1 0 priority: 0
New priority for 0, 0 769
New priority for 1, 0 870
New priority for 0, 1 99
New priority for 1, 1 211
There are 8 items in the queue.
The priority queue is:
vertex: 0 0 priority: 769
vertex: 1 0 priority: 870
vertex: 1 0 priority: 870
vertex: 0 0 priority: 769
vertex: 1 1 priority: 211
vertex: 1 1 priority: 211
vertex: 0 1 priority: 99
vertex: 0 1 priority: 99
The demo simply sets random priority values for every vertex, and pushes them all into the queue. It then does exactly the same thing again. You can see in the output that some of the items appear in the queue at different positions (not back-to-back, as I would expect, since they reference the same priority value in the PriorityMap).
The problem is that item (0,0) (with new priority 769) appears above vertex (1,0) with priority 870. This would cause the items to be processed in the wrong order.
Is there a way to replace an item in the queue when it is pushed instead of adding a second one? (like an std::set instead of the current behavior which is like std::multiset)?
--------- Edit ------------
In the "// Insert another set of random values for each vertex" loop, I replaced the 'mutableQueue.push(*vertexIterator)' with :
mutableQueue.push_or_update(*vertexIterator);
Unfortunately it doesn't do what I'd expect - the output is now:
There are 0 items in the queue.
New priority for 0, 0 150
New priority for 1, 0 522
New priority for 0, 1 27
New priority for 1, 1 883
There are 4 items in the queue.
The priority queue is:
vertex: 1 1 priority: 883
vertex: 1 0 priority: 522
vertex: 0 0 priority: 150
vertex: 0 1 priority: 27
New priority for 0, 0 658
New priority for 1, 0 591
New priority for 0, 1 836
New priority for 1, 1 341
There are 7 items in the queue.
The priority queue is:
vertex: 0 1 priority: 836
vertex: 0 1 priority: 836
vertex: 0 0 priority: 658
vertex: 0 0 priority: 658
vertex: 1 0 priority: 591
vertex: 1 0 priority: 591
vertex: 1 1 priority: 341
Further, replacing the push() with just update() produces:
There are 0 items in the queue.
New priority for 0, 0 806
New priority for 1, 0 413
New priority for 0, 1 592
New priority for 1, 1 861
There are 4 items in the queue.
The priority queue is:
vertex: 1 1 priority: 861
vertex: 0 0 priority: 806
vertex: 0 1 priority: 592
vertex: 1 0 priority: 413
New priority for 0, 0 175
New priority for 1, 0 642
New priority for 0, 1 991
New priority for 1, 1 462
There are 4 items in the queue.
The priority queue is:
vertex: 1 1 priority: 462
vertex: 0 1 priority: 991
vertex: 1 0 priority: 642
vertex: 0 0 priority: 175
There are now only 4 items (like I would expect), but they are not sorted!
----------- Edit - more information --------------
I think there is something going wrong with the index_in_heap map. I added:
std::cout << "Index added: " << get(index_in_heap, v) << std::endl;
after this line:
put(index_in_heap, v, index);
in d_ary_heap_indirect::push(Value).
I also added
std::cout << "Index added caller: " << get(index_in_heap, v) << std::endl;
after the first round of adding values to the queue (after this line:
mutableQueue.push(*vertexIterator);
The output is:
Original priority for 0, 0 641
Index added: 0
Index added caller: 0
Original priority for 1, 0 40
Index added: 1
Index added caller: 1
Original priority for 0, 1 400
Index added: 2
Index added caller: 2
Original priority for 1, 1 664
Index added: 3
Index added caller: 0
I don't understand why this last index is 3 inside the push()
function, but 0 when I query it from the caller?
When I look at the same things inside the update() function, the
index_in_heap just seems to return garbage. That is, I look at the
value of size_type index = get(index_in_heap, v); in update(), and
when it is called with vertex (0,0), the value of 'index' is
4294967295 (when I would expect it to be in the range [0,3]).
Can anyone explain this? Perhaps I am setting up the index_in_heap map incorrectly?
The priority queue won't update its structure when you just change the priorities of the nodes. Once a node is inserted you need to consider its priority constant. If you need to update the priorities you need to tell the priority queue about this. To this end you need to tell it which node gets what new priority.
Unfortunately, tracking some sort of node identification and priority makes the priority queues slow: for a d-heap it is necessay to track where the node moved, making updates relatively expensive. For node-based heaps, e.g., Fibonacci-heaps, the node stays put but the tend to be more expensive to maintain (Fibonacci-heaps have interesting theoretical complexity which, however, only matters for impractically sized problems). I haven't come up with any middle-ground although I implemented all approaches to priority queues I could find described in books.
The d_ary_heap_indirect is designed to only allow priorities to increase. If in the update() and push_or_update() functions you change:
preserve_heap_property_up(index);
to
preserve_heap_property_up(index);
preserve_heap_property_down();
it seems to allow increasing or decreasing the priorities while keeping the queue sorted.
Related
I am currently developing a chess engine in C++, and I am in the process of debugging my move generator. For this purpose, I wrote a simple perft() function:
int32_t Engine::perft(GameState game_state, int32_t depth)
{
int32_t last_move_nodes = 0;
int32_t all_nodes = 0;
Timer timer;
timer.start();
int32_t output_depth = depth;
if (depth == 0)
{
return 1;
}
std::vector<Move> legal_moves = generator.generate_legal_moves(game_state);
for (Move move : legal_moves)
{
game_state.make_move(move);
last_move_nodes = perft_no_print(game_state, depth - 1);
all_nodes += last_move_nodes;
std::cout << index_to_square_name(move.get_from_index()) << index_to_square_name(move.get_to_index()) << ": " << last_move_nodes << "\n";
game_state.unmake_move(move);
}
std::cout << "\nDepth: " << output_depth << "\nTotal nodes: " << all_nodes << "\nTotal time: " << timer.get_milliseconds() << "ms/" << timer.get_milliseconds()/1000.0f << "s\n\n";
return all_nodes;
}
int32_t Engine::perft_no_print(GameState game_state, int32_t depth)
{
int32_t nodes = 0;
if (depth == 0)
{
return 1;
}
std::vector<Move> legal_moves = generator.generate_legal_moves(game_state);
for (Move move : legal_moves)
{
game_state.make_move(move);
nodes += perft_no_print(game_state, depth - 1);
game_state.unmake_move(move);
}
return nodes;
}
It's results for the initial chess position (FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1) for depths 1 and 2 match the results of stockfish's perft command, so I assume they are correct:
h2h3: 1
h2h4: 1
g2g3: 1
g2g4: 1
f2f3: 1
f2f4: 1
e2e3: 1
e2e4: 1
d2d3: 1
d2d4: 1
c2c3: 1
c2c4: 1
b2b3: 1
b2b4: 1
a2a3: 1
a2a4: 1
g1h3: 1
g1f3: 1
b1c3: 1
b1a3: 1
Depth: 1
Total nodes: 20
Total time: 1ms/0.001s
h2h3: 20
h2h4: 20
g2g3: 20
g2g4: 20
f2f3: 20
f2f4: 20
e2e3: 20
e2e4: 20
d2d3: 20
d2d4: 20
c2c3: 20
c2c4: 20
b2b3: 20
b2b4: 20
a2a3: 20
a2a4: 20
g1h3: 20
g1f3: 20
b1c3: 20
b1a3: 20
Depth: 2
Total nodes: 400
Total time: 1ms/0.001s
The results stop matching at depth 3, though:
Stockfish:
go perft 3
a2a3: 380
b2b3: 420
c2c3: 420
d2d3: 539
e2e3: 599
f2f3: 380
g2g3: 420
h2h3: 380
a2a4: 420
b2b4: 421
c2c4: 441
d2d4: 560
e2e4: 600
f2f4: 401
g2g4: 421
h2h4: 420
b1a3: 400
b1c3: 440
g1f3: 440
g1h3: 400
Nodes searched: 8902
My engine:
h2h3: 361
h2h4: 380
g2g3: 340
g2g4: 397
f2f3: 360
f2f4: 436
e2e3: 380
e2e4: 437
d2d3: 380
d2d4: 437
c2c3: 399
c2c4: 326
b2b3: 300
b2b4: 320
a2a3: 280
a2a4: 299
g1h3: 281
g1f3: 280
b1c3: 357
b1a3: 320
Depth: 3
Total nodes: 7070
Total time: 10ms/0.01s
I figured that my move generator was just buggy, and tried to track down the bugs by making a move the engine gives incorrect values for on the board and then calling perft() with depth = 2 on it to find out which moves are missing. But for all moves I tried this with, the engine suddenly starts to output the correct results I expected to get earlier!
Here is an example for the move a2a3:
When calling perft() on the initial position in stockfish, it calculates 380 subnodes for a2a3 at depth 3.
When calling perft() on the initial position in my engine, it calculates 280 subnodes for a2a3 at depth 3.
When calling perft() on the position you get after making the move a2a3 in the initial position in my engine, it calculates the correct number of total nodes at depth 2, 380:
h7h5: 19
h7h6: 19
g7g5: 19
g7g6: 19
f7f5: 19
f7f6: 19
e7e5: 19
e7e6: 19
d7d5: 19
d7d6: 19
c7c5: 19
c7c6: 19
b7b5: 19
b7b6: 19
a7a5: 19
a7a6: 19
g8h6: 19
g8f6: 19
b8c6: 19
b8a6: 19
Depth: 2
Total nodes: 380
Total time: 1ms/0.001s
If you have any idea what the problem could be here, please help me out. Thank you!
EDIT:
I discovered some interesting new facts that might help to solve the problem, but I don't know what to do with them:
For some reason, using std::sort() like this in perft():
std::sort(legal_moves.begin(), legal_moves.end(), [](auto first, auto second){ return first.get_from_index() % 8 > second.get_from_index() % 8; });
to sort the vector of legal moves causes the found number of total nodes for the initial position (for depth 3) to change from the wrong 7070 to the (also wrong) 7331.
When printing the game state after calling game_state.make_move() in perft(), it seems to have had no effect on the position bitboards (the other properties change like they are supposed to). This is very strange, because isolated, the make_move() method works just fine.
I'm unsure if you were able to pin down the issue but from the limited information available in the question, the best I can assume (and something I faced myself earlier) is that there is a problem in your unmake_move() function when it comes to captures since
Your perft fails only at level 3 - this is when the first legal capture is possible, move 1 and 2 can have no legal captures.
Your perft works fine when it's at depth 1 in the position after a2a3 rather than when it's searching at depth 3 from the start
This probably means that your unmake_move() fails at a depth greater than 1 where you need to restore some of the board's state that cannot be derived from just the move parameter you are passing in (e.g. enpassant, castling rights etc. before you made the move).
This is how you would like to debug your move generator using perft.
Given startpos as p1, generate perft(3) for your engine and sf. (you did that)
Now check any move that have different nodes, you pick a2a3. (you did that)
Given startpos + a2a3 as p2, generate perft(2) for your engine and sf. (you partially did this)
Now check any move that have different nodes in step 3. Let's say move x.
Given startpos + a2a3 + x as p3, generate perft(1) for your engine and sf.
Since that is only perft(1) by this time you will be able to figure out the wrong move or the missing move from your generator. Setup that last position or p3 on the board and see the wrong/missing moves from your engine compared to sf perft(1) result.
So I have been working on a Breadth First Search to get a path given a starting and ending node. However in some cases it seems to fail and not get the path, which I know is possible since a Depth First Search and visual inspection shows that it should exist.
I have an Adjacency Matrix:
1 2 3 4 5 6 7 8
1 0 20 25 20 0 0 0 0
2 20 0 5 0 30 0 0 0
3 25 5 0 13 8 21 0 0
4 20 0 13 0 0 17 0 0
5 0 30 8 0 0 33 0 0
6 0 0 21 17 33 0 0 0
7 0 0 0 0 0 0 0 10
8 0 0 0 0 0 0 10 0
Which has a graph as follows:
This is my function:
void Network::BFS(int src, int dest, vector<bool>& visited, vector<int>& path) {
// The Queue is the core for the BFS.
queue<int> Queue;
// Mark current node as visited.
visited[src] = true;
Queue.push(src);
// While queue is not empty.
while (!Queue.empty()) {
// Add node to path.
// Check if we have found the destination yet or not, if we have we do one last push to path and we're done!
if (Queue.front() == dest) {
return;
}
int top = Queue.front();
path.push_back(Queue.front());
// Pop off front.
Queue.pop();
// Iterate and process all none visited nodes.
for (int node = 0; node < amountOfNodes; node++) {
// Check if it is not visited already.
if (visited[node] == false && (adjMatrix[node * amountOfNodes + src] != 0)) {
Queue.push(node); // Add to end.
visited[node] = true;
}
}
}
}
Sample input and output:
(6, 3) -> Path is: 6
(1, 5) -> Path is: 1 2 3 4
As you can see, it does not compute the path properly at all. Where is my algorithm going wrong here, and how do I fix it?
BFS involves visiting adjacent nodes in a FIFO fashion. Once you reach a node, you put into the queue all its neighbours, unless they were already visited.
First off, there's a typo where you iterate over adjacent nodes. You want to traverse the top column, not the src one:
adjMatrix[node * amountOfNodes + top] != 0
// ~~^
Secondly, your current path implementation stores the visit order of nodes, not a path from the source to its destination. For the latter, you need to store the parent of each node, so that the final path can be restored by going from a child (destination) to its parent, grandparent, great-grandparent, ..., etc.
std::vector<int> parent(amountOfNodes, -1);
//...
if (visited[node] == false && (adjMatrix[node * amountOfNodes + top] != 0))
{
Queue.push(node); // Add to end.
visited[node] = true;
parent[node] = top;
}
Restoring the path is straightforward:
int u = dest;
do
{
std::cout << u << " ";
u = parent[u];
}
while (u != -1);
DEMO
If I set the value of a SparseMatrix entry in Eigen as follows:
sparse_matrix->coeffref(10, 10) = 0;
Would this actually shrink the storage required by the matrix or would it try and store a 0 and use up 4 bytes there (assuming integer type)?
if the answer is the latter, how can I set columns to 0, so that it does not use any extra space?
Also, what about something like this:
typedef Eigen::Triplet<double> TripletType;
std::vector<TripletType> t;
for (int i = 0; i < some_value; ++i) {
for (int j = 0; j < some_value; ++j) {
t->push_back(TripletType(i, j, 0);
}
}
sparse_matrix->setFromTriplets(t);
Would this result in explicit zeros in the sparse matrix?
After insertion with coeffRef you can prune the sparse matrix like:
Eigen::SparseMatrix<double, Eigen::ColMajor> A(5,5);
// fill A
A.insert(0,0)=9.;
A.insert(1,0)=3.0/2.0;
A.insert(0,1)=3.0/2.0;
A.insert(2,0)=6.0;
A.insert(0,2)=6.0;
A.insert(3,0)=3.0/4.0;
A.insert(0,3)=3.0/4.0;
A.insert(4,0)=3.0;
A.insert(0,4)=3.0;
A.insert(1,1)=1.0/2.0;
A.insert(2,2)=12.0;
A.insert(3,3)=5.0/8.0;
A.insert(4,4)=16.0;
std::cout << A << std::endl;
std::cout << A.data().size() << std::endl;
A.coeffRef(3,0) = 0;
A.prune(0,0); // Suppresses all nonzeros which are much smaller than reference under the tolerence epsilon
std::cout << A << std::endl;
std::cout << A.data().size() << std::endl;`
Output:
Nonzero entries:
(9,0) (1.5,1) (6,2) (0.75,3) (3,4) (_,_) (_,_) (_,_) (1.5,0) (0.5,1) (6,0) (12,2
) (0.75,0) (0.625,3) (3,0) (16,4)
Outer pointers:
0 8 10 12 14 $
Inner non zeros:
5 2 2 2 2 $
9 1.5 6 0.75 3
1.5 0.5 0 0 0
6 0 12 0 0
0.75 0 0 0.625 0
3 0 0 0 16
16
Nonzero entries:
(9,0) (1.5,1) (6,2) (3,4) (1.5,0) (0.5,1) (6,0) (12,2) (0.75,0) (0.625,3) (3,0)
(16,4)
Outer pointers:
0 4 6 8 10 $
9 1.5 6 0.75 3
1.5 0.5 0 0 0
6 0 12 0 0
0 0 0 0.625 0
3 0 0 0 16
12
You can see that the size has changed from 16 to 12, as also the three (_,_) are removed.
I didn't check with sizeof() if memory storage that is needed is really less.
I am bulkloading an R Tree with spatialindex (http://libspatialindex.github.com/) library:
string baseName = "streets";
size_t capacity = 10 * 1024 * 1024;
bool bWriteThrough = false;
indexIdentifier = 0;
IStorageManager *disk = StorageManager::createNewDiskStorageManager(baseName, 512);
fileInMem = StorageManager
::createNewRandomEvictionsBuffer(*disk, capacity, bWriteThrough);
// bulkLoads my tree
bulkLoadRTree();
cout << "tree info:" << endl;
cout << *tree << endl;
delete disk;
The following is output at the info about the built tree:
Dimension: 2
Fill factor: 0.7
Index capacity: 100
Leaf capacity: 100
Tight MBRs: enabled
Near minimum overlap factor: 32
Reinsert factor: 0.3
Split distribution factor: 0.4
Utilization: 69%
Reads: 1
Writes: 35980
Hits: 0
Misses: 0
Tree height: 4
Number of data: 2482376
Number of nodes: 35979
Level 0 pages: 35463
Level 1 pages: 507
Level 2 pages: 8
Level 3 pages: 1
Splits: 0
Adjustments: 0
Query results: 0
now I am trying to load what I have saved in the disk:
IStorageManager *ldisk = StorageManager::loadDiskStorageManager(baseName);
SpatialIndex::StorageManager::IBuffer* fileLoadBuffer = StorageManager
::createNewRandomEvictionsBuffer(*ldisk, capacity, bWriteThrough);
id_type id = 1;
tree = RTree::loadRTree(*fileLoadBuffer, id);
cout << *tree << endl;
and the tree has only one node (the output of the tree is:)
Dimension: 2
Fill factor: 0.7
Index capacity: 100
Leaf capacity: 100
Tight MBRs: enabled
Near minimum overlap factor: 32
Reinsert factor: 0.3
Split distribution factor: 0.4
Utilization: 0%
Reads: 0
Writes: 0
Hits: 0
Misses: 0
Tree height: 1
Number of data: 0
Number of nodes: 1
Level 0 pages: 1
Splits: 0
Adjustments: 0
Query results: 0
What do I do wrong? Why don't I load the whole tree from the disk?
Did you maybe not sync your changes to disc?
Plus, usually one would implement the tree on-disk, and not read it completely on the first access. So at this point, it cannot report accurate statistics.
Or maybe your bulkLoadRTree does not use fileInMem.
One has to delete the fileInMem so the pages are further sent back to disk and further sent back to delete *disk. This line needs to be added before delete disk:
delete fileInMem
Hello everywhere there is an explanation by drawings hot to create graph out of adj. matrix. However, i need simple pseudo code or algorithym for that .... I know how to draw it out of adj. matrix and dont know why nobody no where explains how to actually put it in code. I dont mean actual code but at least algorithm ... Many say .. 1 is if there is an edge i know that.. I have created the adj. matrix and dont know how to transfer it to graph. My vertices dont have names they are just indexes of the matrix. for example 1-9 are the "names of my matrix"
1 2 3 4 5 6 7 8 9
1 0 1 0 0 1 0 0 0 0
2 1 0 1 0 0 0 0 0 0
3 0 1 0 1 0 0 0 0 0
4 0 0 1 0 0 1 0 0 0
5 1 0 0 0 0 0 1 0 0
6 0 0 0 1 0 0 0 0 1
7 0 0 0 0 1 0 0 1 0
8 0 0 0 0 0 0 1 0 0
9 0 0 0 0 0 1 0 0 0
that was originaly a maze ... have to mark row1 col4 as start and row7 col8 end ...
Nobody ever told me how to implement graph out of matrix (without pen) :Pp
thanks
Nature of symmetry
Adjancency matrix is a representation of a graph. For undirected graph, its matrix is symmetrical. For instance, if there is an edge from vertex i to vertex j, there must also be an edge from vertex j to vertex i. That is the same edge actually.
*
*
* A'
A *
*
*
Algorithm
Noticing this nature, you can implement your algorithm as simple as:
void drawGraph(vertices[nRows][nCols])
{
for (unsigned int i = 0; i < nRows; ++i)
{
for (unsigned int j = i; j < nCols; ++j)
{
drawLine(i, j);
}
}
}
You can convert a graph from an adjacency matrix representation to a node-based representation like this:
#include <iostream>
#include <vector>
using namespace std;
const int adjmatrix[9][9] = {
{0,1,0,0,1,0,0,0,0},
{1,0,1,0,0,0,0,0,0},
{0,1,0,1,0,0,0,0,0},
{0,0,1,0,0,1,0,0,0},
{1,0,0,0,0,0,1,0,0},
{0,0,0,1,0,0,0,0,1},
{0,0,0,0,1,0,0,1,0},
{0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,1,0,0,0}
};
struct Node {
vector<Node*> neighbours;
/* optional additional node information */
};
int main (int argc, char const *argv[])
{
/* initialize nodes */
vector<Node> nodes(9);
/* add pointers to neighbouring nodes */
int i,j;
for (i=0;i<9;++i) {
for (j=0;j<9;++j) {
if (adjmatrix[i][j]==0) continue;
nodes[i].neighbours.push_back(&nodes[j]);
}
}
/* print number of neighbours */
for (i=0;i<9;++i) {
cout << "Node " << i
<< " has " << nodes[i].neighbours.size() <<" outbound edges." << endl;
}
return 0;
}
Here, the graph is represented as an array of nodes with pointers to reachable neighbouring nodes. After setting up the nodes and their neighbour pointers you use this data structure to perform the graph algorithms you want, in this (trivial) example print out the number of outbound directed edges each node has.