shortest path algorithm from text input - c++

I've been trying to do this shortest path problem and I realised that the way I was trying to it was almost completely wrong and that I have no idea to complete it.
The question requires you to find the shortest path from one point to another given a text file of input.
The input looks like this with the first value representing how many levels there are.
4
14 10 15
13 5 22
13 7 11
5
This would result in an answer of: 14+5+13+11+5=48
The question asks for the shortest path from the bottom left to the top right.
The way I have attempted to do this is to compare the values of either path possible and then add them to a sum. e.g the first step from the input I provided would compare 14 against 10 + 15. I ran into the problem that if both values are the same it will stuff up the rest of the working.
I hope this makes some sense.
Any suggestions on an algorithm to use or any sample code would be greatly appreciated.

Assume your data file is read into a 2D array of the form:
int weights[3][HEIGHT] = {
{14, 10, 15},
{13, 5, 22},
{13, 7, 11},
{X, 5, X}
};
where X can be anything, doesn't matter. For this I'm assuming positive weights and therefore there is never a need to consider a path that goes "down" a level.
In general you can say that the minimum cost is lesser of the following 2 costs:
1) The cost of rising a level: The cost of the path to the opposite side from 1 level below, plus the cost of coming up.
2) The cost of moving across a level : The cost of the path to the opposite from the same level, plus the cost of coming across.
int MinimumCost(int weight[3][HEIGHT]) {
int MinCosts[2][HEIGHT]; // MinCosts[0][Level] stores the minimum cost of reaching
// the left node of that level
// MinCosts[1][Level] stores the minimum cost of reaching
// the right node of that level
MinCosts[0][0] = 0; // cost nothing to get to the start
MinCosts[0][1] = weight[0][1]; // the cost of moving across the bottom
for (int level = 1; level < HEIGHT; level++) {
// cost of coming to left from below right
int LeftCostOneStep = MinCosts[1][level - 1] + weight[2][level - 1];
// cost of coming to left from below left then across
int LeftCostTwoStep = MinCosts[0][level - 1] + weight[0][level - 1] + weight[1][level];
MinCosts[0][level] = Min(LeftCostOneStep, LeftCostTwoStep);
// cost of coming to right from below left
int RightCostOneStep = MinCosts[0][level - 1] + weight[0][level - 1];
// cost of coming to right from below right then across
int RightCostTwoStep = MinCosts[1][level - 1] + weight[1][level - 1] + weight[1][level];
MinCosts[1][level] = Min(RightCostOneStep, RightCostTwoStep);
}
return MinCosts[1][HEIGHT - 1];
}
I haven't double checked the syntax, please only use it to get a general idea of how to solve the problem. You could also rewrite the algorithm so that MinCosts uses constant memory, MinCosts[2][2] and your whole algorithm could become a state machine.
You could also use dijkstra's algorithm to solve this, but that's a bit like killing a fly with a nuclear warhead.

My first idea was to represent the graph with a matrix and then run a DFS or Dijkstra to solve it. But for this given question, we can do better.
So, here is a possible solution of this problem that runs in O(n). 2*i means left node of level i and 2*i+1 means right node of level i. Read the comments in this solution for an explanation.
#include <stdio.h>
struct node {
int lup; // Cost to go to level up
int stay; // Cost to stay at this level
int dist; // Dist to top right node
};
int main() {
int N;
scanf("%d", &N);
struct node tab[2*N];
// Read input.
int i;
for (i = 0; i < N-1; i++) {
int v1, v2, v3;
scanf("%d %d %d", &v1, &v2, &v3);
tab[2*i].lup = v1;
tab[2*i].stay = tab[2*i+1].stay = v2;
tab[2*i+1].lup = v3;
}
int v;
scanf("%d", &v);
tab[2*i].stay = tab[2*i+1].stay = v;
// Now the solution:
// The last level is obvious:
tab[2*i+1].dist = 0;
tab[2*i].dist = v;
// Now, for each level, we compute the cost.
for (i = N - 2; i >= 0; i--) {
tab[2*i].dist = tab[2*i+3].dist + tab[2*i].lup;
tab[2*i+1].dist = tab[2*i+2].dist + tab[2*i+1].lup;
// Can we do better by staying at the same level ?
if (tab[2*i].dist > tab[2*i+1].dist + tab[2*i].stay) {
tab[2*i].dist = tab[2*i+1].dist + tab[2*i].stay;
}
if (tab[2*i+1].dist > tab[2*i].dist + tab[2*i+1].stay) {
tab[2*i+1].dist = tab[2*i].dist + tab[2*i+1].stay;
}
}
// Print result
printf("%d\n", tab[0].dist);
return 0;
}
(This code has been tested on the given example.)

Use a depth-first search and add only the minimum values. Then check which side is the shortest stair. If it's a graph problem look into a directed graph. For each stair you need 2 vertices. The cost from ladder to ladder can be something else.

The idea of a simple version of the algorithm is the following:
define a list of vertices (places where you can stay) and edges (walks you can do)
every vertex will have a list of edges connecting it to other vertices
for every edge store the walk length
for every vertex store a field with 1000000000 with the meaning "how long is the walk to here"
create a list of "active" vertices initialized with just the starting point
set the walk-distance field of starting vertex with 0 (you're here)
Now the search algorithm proceeds as
pick the (a) vertex from the "active list" with lowest walk_distance and remove it from the list
if the vertex is the destination you're done.
otherwise for each edge in that vertex compute the walk distance to the other_vertex as
new_dist = vertex.walk_distance + edge.length
check if the new distance is shorter than other_vertex.walk_distance and in this case update other_vertex.walk_distance to the new value and put that vertex in the "active list" if it's not already there.
repeat from 1
If you run out of nodes in the active list and never processed the destination vertex it means that there was no way to reach the destination vertex from the starting vertex.
For the data structure in C++ I'd use something like
struct Vertex {
double walk_distance;
std::vector<struct Edge *> edges;
...
};
struct Edge {
double length;
Vertex *a, *b;
...
void connect(Vertex *va, Vertex *vb) {
a = va; b = vb;
va->push_back(this); vb->push_back(this);
}
...
};
Then from the input I'd know that for n levels there are 2*n vertices needed (left and right side of each floor) and 2*(n-1) + n edges needed (one per each stair and one for each floor walk).
For each floor except the last you need to build three edges, for last floor only one.
I'd also allocate all edges and vertices in vectors first, fixing the pointers later (post-construction setup is an anti-pattern but here is to avoid problems with reallocations and still maintaining things very simple).
int n = number_of_levels;
std::vector<Vertex> vertices(2*n);
std::vector<Edge> edges(2*(n-1) + n);
for (int i=0; i<n-1; i++) {
Vertex& left = &vertices[i*2];
Vertex& right = &vertices[i*2 + 1];
Vertex& next_left = &vertices[(i+1)*2];
Vertex& next_right = &vertices[(i+1)*2 + 1];
Edge& dl_ur = &edges[i*3]; // down-left to up-right stair
Edge& dr_ul = &edges[i*3+1]; // down-right to up-left stair
Edge& floor = &edges[i*3+2];
dl_ur.connect(left, next_right);
dr_ul.connect(right, next_left);
floor.connect(left, right);
}
// Last floor
edges.back().connect(&vertex[2*n-2], &vertex[2*n-1]);
NOTE: untested code
EDIT
Of course this algorithm can solve a much more general problem where the set of vertices and edges is arbitrary (but lengths are non-negative).
For the very specific problem a much simpler algorithm is possible, that doesn't even need any data structure and that can instead compute the result on the fly while reading the input.
#include <iostream>
#include <algorithm>
int main(int argc, const char *argv[]) {
int n; std::cin >> n;
int l=0, r=1000000000;
while (--n > 0) {
int a, b, c; std::cin >> a >> b >> c;
int L = std::min(r+c, l+b+c);
int R = std::min(r+b+a, l+a);
l=L; r=R;
}
int b; std::cin >> b;
std::cout << std::min(r, l+b) << std::endl;
return 0;
}
The idea of this solution is quite simple:
l variable is the walk_distance for the left side of the floor
r variable is the walk_distance for the right side
Algorithm:
we initialize l=0 and r=1000000000 as we're on the left side
for all intermediate steps we read the three distances:
a is the length of the down-left to up-right stair
b is the length of the floor
c is the length of the down-right to up-left stair
we compute the walk_distance for left and right side of next floor
L is the minimum between r+c and l+b+c (either we go up starting from right side, or we go there first starting from left side)
R is the minimum betwen l+a and r+b+a (either we go up starting from left, or we start from right and cross the floor first)
for the last step we just need to chose what is the minimum between r and coming there from l by crossing the last floor

Related

Shortest path in Graph with time limit

Let's say I have a graph G with N vertices and M edges. Each edge has its length and time (let's say in minutes), which it takes to traverse that edge. I need to find the shortest path in the graph between the vertices 1 and N, which is performed in under T minutes time.
Since time is the more valuable resource and we care about traversing the graph in time, and only then with minimal length, I decided to use Dijkstra's algorithm, for which I considered the time of each edge as its weight. I added a vector to store the durations. Thus, the algorithm returns the least time, not the least length. A friend suggested this addition to my code:
int answer(int T) {
int l = 1;
int r = M; // a very big number
int answer = M;
while (l <= r) {
int mid = (l + r) / 2;
int time = dijkstra(mid); // the parameter mid serves as an upper bound for dijkstra and I relax the edge only if its length(not time) is less than mid
if (time <= T) {
answer = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
if (best == M) {
return -1; // what we return in case there is no path in the graph, which takes less than T minutes
}
return answer;
}
Here is the dijkstra method (part of class Graph with std::unordered_map<int, std::vector<Node>> adjacencyList member):
int dijkstra(int maxLength) {
std::priority_queue<Node, std::vector<Node>, NodeComparator> heap;//NodeComparator sorts by time of edge
std::vector<int> durations(this->numberOfVertices + 1, M);
std::set<int> visited;
// duration 1->1 is 0
durations[1] = 0;
heap.emplace(1, 0, 0);
while (!heap.empty()) {
int vertex = heap.top().endVertex;
heap.pop();
// to avoid repetition
if (visited.find(vertex) != visited.end()) {
continue;
}
for (Node node: adjacencyList[vertex]) {
// relaxation
if (node.length <= maxLength && durations[node.endVertex] > durations[vertex] + node.time) {
durations[node.endVertex] = durations[vertex] + node.time;
heap.emplace(node.endVertex, durations[node.endVertex], 0);
}
}
// mark as visited to avoid going through the same vertex again
visited.insert(vertex);
}
// return path time between 1 and N bounded by maxKilograms
return durations.back();
}
This seems to work but seems inefficient to me. To be frank, I don't understand his idea completely. It appears to me like randomly trying to find the best answer(because nobody said that the time of an edge is tied proportionally to its length). I tried searching for shortest path in graph with time limit but I found algorithms that find the fastest paths, not the shortest with a limit. Does an algorithm for this even exist? How can improve my solution?
What is this?
int time = dijkstra(mid);
It certainly isnt an implementation of the Dijkstra algorithm!
The Dijkstra algorithm requires the starting node and returns THE shortest path from the starting node to every other.
You are going to need a function that returns all the distinct paths between start and end nodes that take less than T. Then you can search them for the one that is cheapest.
Search graph for all distinct paths from start to end
Discard paths that take more then T
Select cheapest path.
Finding a resource constrained shortest path is NPHard. Most approaches to this problem employ a labelling scheme, which is a specialization of dynamic programming. You could use available libraries to accomplish this. See here for a boost implementation.

Tallest tower with stacked boxes in the given order

Given N boxes. How can i find the tallest tower made with them in the given order ? (Given order means that the first box must be at the base of the tower and so on). All boxes must be used to make a valid tower.
It is possible to rotate the box on any axis in a way that any of its 6 faces gets parallel to the ground, however the perimeter of such face must be completely restrained inside the perimeter of the superior face of the box below it. In the case of the first box it is possible to choose any face, because the ground is big enough.
To solve this problem i've tried the following:
- Firstly the code generates the rotations for each rectangle (just a permutation of the dimensions)
- secondly constructing a dynamic programming solution for each box and each possible rotation
- finally search for the highest tower made (in the dp table)
But my algorithm is taking wrong answer in unknown test cases. What is wrong with it ? Dynamic programming is the best approach to solve this problem ?
Here is my code:
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>
struct rectangle{
int coords[3];
rectangle(){ coords[0] = coords[1] = coords[2] = 0; }
rectangle(int a, int b, int c){coords[0] = a; coords[1] = b; coords[2] = c; }
};
bool canStack(rectangle &current_rectangle, rectangle &last_rectangle){
for (int i = 0; i < 2; ++i)
if(current_rectangle.coords[i] > last_rectangle.coords[i])
return false;
return true;
}
//six is the number of rotations for each rectangle
int dp(std::vector< std::vector<rectangle> > &v){
int memoization[6][v.size()];
memset(memoization, -1, sizeof(memoization));
//all rotations of the first rectangle can be used
for (int i = 0; i < 6; ++i) {
memoization[i][0] = v[0][i].coords[2];
}
//for each rectangle
for (int i = 1; i < v.size(); ++i) {
//for each possible permutation of the current rectangle
for (int j = 0; j < 6; ++j) {
//for each permutation of the previous rectangle
for (int k = 0; k < 6; ++k) {
rectangle &prev = v[i - 1][k];
rectangle &curr = v[i][j];
//is possible to put the current rectangle with the previous rectangle ?
if( canStack(curr, prev) ) {
memoization[j][i] = std::max(memoization[j][i], curr.coords[2] + memoization[k][i-1]);
}
}
}
}
//what is the best solution ?
int ret = -1;
for (int i = 0; i < 6; ++i) {
ret = std::max(memoization[i][v.size()-1], ret);
}
return ret;
}
int main ( void ) {
int n;
scanf("%d", &n);
std::vector< std::vector<rectangle> > v(n);
for (int i = 0; i < n; ++i) {
rectangle r;
scanf("%d %d %d", &r.coords[0], &r.coords[1], &r.coords[2]);
//generate all rotations with the given rectangle (all combinations of the coordinates)
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
if(j != k) //micro optimization disease
for (int l = 0; l < 3; ++l)
if(l != j && l != k)
v[i].push_back( rectangle(r.coords[j], r.coords[k], r.coords[l]) );
}
printf("%d\n", dp(v));
}
Input Description
A test case starts with an integer N, representing the number of boxes (1 ≤ N ≤ 10^5).
Following there will be N rows, each containing three integers, A, B and C, representing the dimensions of the boxes (1 ≤ A, B, C ≤ 10^4).
Output Description
Print one row containing one integer, representing the maximum height of the stack if it’s possible to pile all the N boxes, or -1 otherwise.
Sample Input
2
5 2 2
1 3 4
Sample Output
6
Sample image for the given input and output.
Usually you're given the test case that made you fail. Otherwise, finding the problem is a lot harder.
You can always approach it from a different angle! I'm going to leave out the boring parts that are easily replicated.
struct Box { unsigned int dim[3]; };
Box will store the dimensions of each... box. When it comes time to read the dimensions, it needs to be sorted so that dim[0] >= dim[1] >= dim[2].
The idea is to loop and read the next box each iteration. It then compares the second largest dimension of the new box with the second largest dimension of the last box, and same with the third largest. If in either case the newer box is larger, it adjusts the older box to compare the first largest and third largest dimension. If that fails too, then the first and second largest. This way, it always prefers using a larger dimension as the vertical one.
If it had to rotate a box, it goes to the next box down and checks that the rotation doesn't need to be adjusted there too. It continues until there are no more boxes or it didn't need to rotate the next box. If at any time, all three rotations for a box failed to make it large enough, it stops because there is no solution.
Once all the boxes are in place, it just sums up each one's vertical dimension.
int main()
{
unsigned int size; //num boxes
std::cin >> size;
std::vector<Box> boxes(size); //all boxes
std::vector<unsigned char> pos(size, 0); //index of vertical dimension
//gets the index of dimension that isn't vertical
//largest indicates if it should pick the larger or smaller one
auto get = [](unsigned char x, bool largest) { if (largest) return x == 0 ? 1 : 0; return x == 2 ? 1 : 2; };
//check will compare the dimensions of two boxes and return true if the smaller one is under the larger one
auto check = [&boxes, &pos, &get](unsigned int x, bool largest) { return boxes[x - 1].dim[get(pos[x - 1], largest)] < boxes[x].dim[get(pos[x], largest)]; };
unsigned int x = 0, y; //indexing variables
unsigned char change; //detects box rotation change
bool fail = false; //if it cannot be solved
for (x = 0; x < size && !fail; ++x)
{
//read in the next three dimensions
//make sure dim[0] >= dim[1] >= dim[2]
//simple enough to write
//mine was too ugly and I didn't want to be embarrassed
y = x;
while (y && !fail) //when y == 0, no more boxes to check
{
change = pos[y - 1];
while (check(y, true) || check(y, false)) //while invalid rotation
{
if (++pos[y - 1] == 3) //rotate, when pos == 3, no solution
{
fail = true;
break;
}
}
if (change != pos[y - 1]) //if rotated box
--y;
else
break;
}
}
if (fail)
{
std::cout << -1;
}
else
{
unsigned long long max = 0;
for (x = 0; x < size; ++x)
max += boxes[x].dim[pos[x]];
std::cout << max;
}
return 0;
}
It works for the test cases I've written, but given that I don't know what caused yours to fail, I can't tell you what mine does differently (assuming it also doesn't fail your test conditions).
If you are allowed, this problem might benefit from a tree data structure.
First, define the three possible cases of block:
1) Cube - there is only one possible option for orientation, since every orientation results in the same height (applied toward total height) and the same footprint (applied to the restriction that the footprint of each block is completely contained by the block below it).
2) Square Rectangle - there are three possible orientations for this rectangle with two equal dimensions (for examples, a 4x4x1 or a 4x4x7 would both fit this).
3) All Different Dimensions - there are six possible orientations for this shape, where each side is different from the rest.
For the first box, choose how many orientations its shape allows, and create corresponding nodes at the first level (a root node with zero height will allow using simple binary trees, rather than requiring a more complicated type of tree that allows multiple elements within each node). Then, for each orientation, choose how many orientations the next box allows but only create nodes for those that are valid for the given orientation of the current box. If no orientations are possible given the orientation of the current box, remove that entire unique branch of orientations (the first parent node with multiple valid orientations will have one orientation removed by this pruning, but that parent node and all of its ancestors will be preserved otherwise).
By doing this, you can check for sets of boxes that have no solution by checking whether there are any elements below the root node, since an empty tree indicates that all possible orientations have been pruned away by invalid combinations.
If the tree is not empty, then just walk the tree to find the highest sum of heights within each branch of the tree, recursively up the tree to the root - the sum value is your maximum height, such as the following pseudocode:
std::size_t maximum_height() const{
if(leftnode == nullptr || rightnode == nullptr)
return this_node_box_height;
else{
auto leftheight = leftnode->maximum_height() + this_node_box_height;
auto rightheight = rightnode->maximum_height() + this_node_box_height;
if(leftheight >= rightheight)
return leftheight;
else
return rightheight;
}
}
The benefits of using a tree data structure are
1) You will greatly reduce the number of possible combinations you have to store and check, because in a tree, the invalid orientations will be eliminated at the earliest possible point - for example, using your 2x2x5 first box, with three possible orientations (as a Square Rectangle), only two orientations are possible because there is no possible way to orient it on its 2x2 end and still fit the 4x3x1 block on it. If on average only two orientations are possible for each block, you will need a much smaller number of nodes than if you compute every possible orientation and then filter them as a second step.
2) Detecting sets of blocks where there is no solution is much easier, because the data structure will only contain valid combinations.
3) Working with the finished tree will be much easier - for example, to find the sequence of orientations of the highest, rather than just the actual height, you could pass an empty std::vector to a modified highest() implementation, and let it append the actual orientation of each highest node as it walks the tree, in addition to returning the height.

Dijkstra's Algorithm - Initializing Node Distances

I have an assignment to use Dijkstra's shortest path algorithm for a simple network simulation. There's one part of the coding implementation that I don't understand and it's giving me grief.
I searched around on stack overflow and found many helpful questions about Dijkstra's, but none with my specific question. I apologize if I didn't research thoroughly enough.
I'm using this pseudocode from Mark Allen Weiss's Data Structures and Algorithm Analysis in C++:
void Graph::dijkstra( Vertex s)
{
for each Vertex v
{
v.dist = INFINITY;
v.known = false;
}
s.dist = 0;
while( there is an unknown distance vertex )
{
Vertex v = smallest unknown distance vertex;
v.known = true;
for each Vertex w adjacent to v
{
if (!w.known)
{
int cvw = cost of edge from v to w;
if(v.dist + cvw < w.dist)
{
//update w
decrease(w.dist to v.dist + cvw);
w.path = v;
}
}
}
}
and my implementation seems to work aside from the last if statement.
if(v.dist + cvw < w.dist)
My code will never go into what's underneath because the distance for every node is initialized to (essentially) infinity and the algorithm never seems to change the distance. Therefore the left side of the comparison is never smaller than the right side. How am I misunderstanding this?
Here is my (messy) code:
class Vertex
{
private:
int id;
unordered_map < Vertex*, int > edges;
int load_factor;
int distance;
bool known;
public:
//getters and setters
};
void dijkstra(Vertex starting_vertex)
{
for (int i = 0; i < vertices.size(); i++)
{
//my program initially stores vertices in the vertex in spot (id - 1).
if (vertices[i].get_id() == starting_vertex.get_id())
{
vertices[i].set_distance(0);
vertices[i].set_known(true);
}
else
{
vertices[i].set_distance(10000000);
vertices[i].set_known(false);
}
}
for (int i = 0; i < vertices.size(); i++)
{
//while there is an unknown distance vertex
if (vertices[i].is_known() == false)
{
vertices[i].set_known(true);
//for every vertex adjacent to this vertex
for (pair<Vertex*, int> edge : vertices[i].get_edges())
{
//if the vertex isn't known
if (edge.first->is_known() == false)
{
//calculate the weight using Adam's note on dijkstra's algorithm
int weight = edge.second * edge.first->get_load_factor();
if (vertices[i].get_distance() + weight < edge.first->get_distance())
//this is my problem line. The left side is never smaller than the right.
{
edge.first->set_distance(vertices[i].get_distance() + weight);
path.add_vertex(edge.first);
}
}
}
}
}
}
Thank you!
You are missing out this step:
Vertex v = smallest unknown distance vertex;
and instead looping through all vertices.
The distance to the starting vertex is initialized to 0 so if you implement this part of the algorithm and pick the v with the smallest distance that is not "known" you will start with the starting vertex and the if should work.
Replace:
for (int i = 0; i < vertices.size(); i++)
{
//while there is an unknown distance vertex
if (vertices[i].is_known() == false)
{
...
}
}
With something like:
while(countNumberOfUnknownVertices(vertices) > 0)
{
Vertex& v = findUnknownVertexWithSmallestDistance(vertices);
...
}
You missed two important parts of Dijkstra's Algorithm.
In implementing
while( there is an unknown distance vertex )
{
Vertex v = smallest unknown distance vertex;
you set v to the first unknown vertex you come to. It's supposed to be, of all the unknown vertices, the one whose distance is least.
The other misstep is that, instead of making one pass over the vertices and doing some work on each unknown one you find, you need to search again after doing the work.
For example, if on one iteration you expand outward from vertex 5, that may make vertex 3 the new unknown vertex with least distance. You can't just continue the search from 5.
The search for the least-distance unknown vertex is going to be slow unless you develop some data structure (a Heap, perhaps) to make that search fast. Go ahead and do a linear search for now. Dijkstra's Algorithm will still work, but it'll take time O(N^2). You should be able to get it down to at least O(N log N).

Finding adjacent nodes in a tree

I'm developing a structure that is like a binary tree but generalized across dimensions so you can set whether it is a binary tree, quadtree, octree, etc by setting the dimension parameter during initialization.
Here is the definition of it:
template <uint Dimension, typename StateType>
class NDTree {
public:
std::array<NDTree*, cexp::pow(2, Dimension)> * nodes;
NDTree * parent;
StateType state;
char position; //position in parents node list
bool leaf;
NDTree const &operator[](const int i) const
{
return (*(*nodes)[i]);
}
NDTree &operator[](const int i)
{
return (*(*nodes)[i]);
}
}
So, to initialize it- I set a dimension and then subdivide. I am going for a quadtree of depth 2 for illustration here:
const uint Dimension = 2;
NDTree<Dimension, char> tree;
tree.subdivide();
for(int i=0; i<tree.size(); i++)
tree[i].subdivide();
for(int y=0; y<cexp::pow(2, Dimension); y++) {
for(int x=0; x<cexp::pow(2, Dimension); x++) {
tree[y][x].state = ((y)*10)+(x);
}
}
std::cout << tree << std::endl;
This will result in a quadtree, the state of each of the values are initialized to [0-4][0-4].
([{0}{1}{2}{3}][{10}{11}{12}{13}][{20}{21}{22}{23}][{30}{31}{32}{33}])
I am having trouble finding adjacent nodes from any piece. What it needs to do is take a direction and then (if necessary) traverse up the tree if the direction goes off of the edge of the nodes parent (e.g. if we were on the bottom right of the quadtree square and we needed to get the piece to the right of it). My algorithm returns bogus values.
Here is how the arrays are laid out:
And here are the structures necessary to know for it:
This just holds the direction for items.
enum orientation : signed int {LEFT = -1, CENTER = 0, RIGHT = 1};
This holds a direction and whether or not to go deeper.
template <uint Dimension>
struct TraversalHelper {
std::array<orientation, Dimension> way;
bool deeper;
};
node_orientation_table holds the orientations in the structure. So in 2d, 0 0 refers to the top left square (or left left square).
[[LEFT, LEFT], [RIGHT, LEFT], [LEFT, RIGHT], [RIGHT, RIGHT]]
And the function getPositionFromOrientation would take LEFT, LEFT and return 0. It is just basically the opposite of the node_orientation_table above.
TraversalHelper<Dimension> traverse(const std::array<orientation, Dimension> dir, const std::array<orientation, Dimension> cmp) const
{
TraversalHelper<Dimension> depth;
for(uint d=0; d < Dimension; ++d) {
switch(dir[d]) {
case CENTER:
depth.way[d] = CENTER;
goto cont;
case LEFT:
if(cmp[d] == RIGHT) {
depth.way[d] = LEFT;
} else {
depth.way[d] = RIGHT;
depth.deeper = true;
}
break;
case RIGHT:
if(cmp[d] == LEFT) {
depth.way[d] = RIGHT;
} else {
depth.way[d] = LEFT;
depth.deeper = true;
}
break;
}
cont:
continue;
}
return depth;
}
std::array<orientation, Dimension> uncenter(const std::array<orientation, Dimension> dir, const std::array<orientation, Dimension> cmp) const
{
std::array<orientation, Dimension> way;
for(uint d=0; d < Dimension; ++d)
way[d] = (dir[d] == CENTER) ? cmp[d] : dir[d];
return way;
}
NDTree * getAdjacentNode(const std::array<orientation, Dimension> direction) const
{
//our first traversal pass
TraversalHelper<Dimension> pass = traverse(direction, node_orientation_table[position]);
//if we are lucky the direction results in one of our siblings
if(!pass.deeper)
return (*(*parent).nodes)[getPositionFromOrientation<Dimension>(pass.way)];
std::vector<std::array<orientation, Dimension>> up; //holds our directions for going up the tree
std::vector<std::array<orientation, Dimension>> down; //holds our directions for going down
NDTree<Dimension, StateType> * tp = parent; //tp is our tree pointer
up.push_back(pass.way); //initialize with our first pass we did above
while(true) {
//continue going up as long as it takes, baby
pass = traverse(up.back(), node_orientation_table[tp->position]);
std::cout << pass.way << " :: " << uncenter(pass.way, node_orientation_table[tp->position]) << std::endl;
if(!pass.deeper) //we've reached necessary top
break;
up.push_back(pass.way);
//if we don't have any parent we must explode upwards
if(tp->parent == nullptr)
tp->reverseBirth(tp->position);
tp = tp->parent;
}
//line break ups and downs
std::cout << std::endl;
//traverse upwards combining the matrices to get our actual position in cube
tp = const_cast<NDTree *>(this);
for(int i=1; i<up.size(); i++) {
std::cout << up[i] << " :: " << uncenter(up[i], node_orientation_table[tp->position]) << std::endl;
down.push_back(uncenter(up[i], node_orientation_table[tp->parent->position]));
tp = tp->parent;
}
//make our way back down (tp is still set to upmost parent from above)
for(const auto & i : down) {
int pos = 0; //we need to get the position from an orientation list
for(int d=0; d<i.size(); d++)
if(i[d] == RIGHT)
pos += cexp::pow(2, d); //consider left as 0 and right as 1 << dimension
//grab the child of treepointer via position we just calculated
tp = (*(*tp).nodes)[pos];
}
return tp;
}
For an example of this:
std::array<orientation, Dimension> direction;
direction[0] = LEFT; //x
direction[1] = CENTER; //y
NDTree<Dimension> * result = tree[3][0]->getAdjacentNode(direction);
This should grab the top right square within bottom left square, e.g. tree[2][1] which would have a value of 21 if we read its state. Which works since my last edit (algorithm is modified). Still, however, many queries do not return correct results.
//Should return tree[3][1], instead it gives back tree[2][3]
NDTree<Dimension, char> * result = tree[1][2].getAdjacentNode({ RIGHT, RIGHT });
//Should return tree[1][3], instead it gives back tree[0][3]
NDTree<Dimension, char> * result = tree[3][0].getAdjacentNode({ RIGHT, LEFT });
There are more examples of incorrect behavior such as tree[0][0](LEFT, LEFT), but many others work correctly.
Here is the folder of the git repo I am working from with this. Just run g++ -std=c++11 main.cpp from that directory if it is necessary.
here is one property you can try to exploit:
consider just the 4 nodes:
00 01
10 11
Any node can have up to 4 neighbor nodes; two will exist in the same structure (larger square) and you have to look for the other two in neighboring structures.
Let's focus on identifying the neighbors which are in the same structure: the neighbors for 00 are 01 and 10; the neighbors for 11 are 01 and 10. Notice that only one bit differs between neighbor nodes and that neighbors can be classified in horizontal and vertical. SO
00 - 01 00 - 01 //horizontal neighbors
| |
10 11 //vertical neighbors
Notice how flipping the MSB gets the vertical neighbor and flipping the LSB gets the horizontal node? Let's have a close look:
MSB: 0 -> 1 gets the node directly below
1 -> 0 sets the node directly above
LSB: 0 -> 1 gets the node to the right
1 -> 0 gets the node to the left
So now we can determine the node's in each direction assuming they exist in the same substructure. What about the node to the left of 00 or above 10?? According to the logic so far if you want a horizontal neighbor you should flip the LSB; but flipping it would retrieve 10 ( the node to the right). So let's add a new rule, for impossible operations:
you can't go left for x0 ,
you can't go right for x1,
you can't go up for 0x,
you can't go down for 1x
*impossible operations refers to operations within the same structure.
Let's look at the bigger picture which are the up and left neighbors for 00? if we go left for 00 of strucutre 0 (S0), we should end up with 01 of(S1), and if we go up we end up with node 10 of S(2). Notice that they are basically the same horizontal/ veritical neighbor values form S(0) only they are in different structures. So basically if we figure out how to jump from one structure to another we have an algorithm.
Let's go back to our example: going up from node 00 (S0). We should end up in S2; so again 00->10 flipping the MSB. So if we apply the same algorithm we use within the structure we should be fine.
Bottom line:
valid transition within a strucutres
MSB 0, go down
1, go up
LSB 0, go right
1, go left
for invalid transitions (like MSB 0, go up)
determine the neighbor structure by flipping the MSB for vertical and LSB for vertical
and get the neighbor you are looking for by transforming a illegal move in structure A
into a legal one in strucutre B-> S0: MSB 0 up, becomes S2:MSB 0 down.
I hope this idea is explicit enough
Check out this answer for neighbor search in octrees: https://stackoverflow.com/a/21513573/3146587. Basically, you need to record in the nodes the traversal from the root to the node and manipulate this information to generate the required traversal to reach the adjacent nodes.
The simplest answer I can think of is to get back your node from the root of your tree.
Each cell can be assigned a coordinate mapping to the deepest nodes of your tree. In your example, the (x,y) coordinates would range from 0 to 2dimension-1 i.e. 0 to 3.
First, compute the coordinate of the neighbour with whatever algorithm you like (for instance, decide if a right move off the edge should wrap to the 1st cell of the same row, go down to the next row or stay in place).
Then, feed the new coordinates to your regular search function. It will return the neighbour cell in dimension steps.
You can optimize that by looking at the binary value of the coordinates. Basically, the rank of the most significant bit of difference tells you how many levels up you should go.
For instance, let's take a quadtree of depth 4. Coordinates range from 0 to 15.
Assume we go left from cell number 5 (0101b). The new coordinate is 4 (0100b). The most significant bit changed is bit 0, which means you can find the neighbour in the current block.
Now if you go right, the new coordinate is 6 (0110b), so the change is affecting bit 1, which means you have to go up one level to access your cell.
All this being said, the computation time and volume of code needed to use such tricks seems hardly worth the effort to me.

Creating random undirected graph in C++

The issue is I need to create a random undirected graph to test the benchmark of Dijkstra's algorithm using an array and heap to store vertices. AFAIK a heap implementation shall be faster than an array when running on sparse and average graphs, however when it comes to dense graphs, the heap should became less efficient than an array.
I tried to write code that will produce a graph based on the input - number of vertices and total number of edges (maximum number of edges in undirected graph is n(n-1)/2).
On the entrance I divide the total number of edges by the number of vertices so that I have a const number of edges coming out from every single vertex. The graph is represented by an adjacency list. Here is what I came up with:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <list>
#include <set>
#define MAX 1000
#define MIN 1
class Vertex
{
public:
int Number;
int Distance;
Vertex(void);
Vertex(int, int);
~Vertex(void);
};
Vertex::Vertex(void)
{
Number = 0;
Distance = 0;
}
Vertex::Vertex(int C, int D)
{
Number = C;
Distance = D;
}
Vertex::~Vertex(void)
{
}
int main()
{
int VertexNumber, EdgeNumber;
while(scanf("%d %d", &VertexNumber, &EdgeNumber) > 0)
{
int EdgesFromVertex = (EdgeNumber/VertexNumber);
std::list<Vertex>* Graph = new std::list<Vertex> [VertexNumber];
srand(time(NULL));
int Distance, Neighbour;
bool Exist, First;
std::set<std::pair<int, int>> Added;
for(int i = 0; i < VertexNumber; i++)
{
for(int j = 0; j < EdgesFromVertex; j++)
{
First = true;
Exist = true;
while(First || Exist)
{
Neighbour = rand() % (VertexNumber - 1) + 0;
if(!Added.count(std::pair<int, int>(i, Neighbour)))
{
Added.insert(std::pair<int, int>(i, Neighbour));
Exist = false;
}
First = false;
}
}
First = true;
std::set<std::pair<int, int>>::iterator next = Added.begin();
for(std::set<std::pair<int, int>>::iterator it = Added.begin(); it != Added.end();)
{
if(!First)
Added.erase(next);
Distance = rand() % MAX + MIN;
Graph[it->first].push_back(Vertex(it->second, Distance));
Graph[it->second].push_back(Vertex(it->first, Distance));
std::set<std::pair<int, int>>::iterator next = it;
First = false;
}
}
// Dijkstra's implementation
}
return 0;
}
I get an error:
set iterator not dereferencable" when trying to create graph from set data.
I know it has something to do with erasing set elements on the fly, however I need to erase them asap to diminish memory usage.
Maybe there's a better way to create some undirectioned graph? Mine is pretty raw, but that's the best I came up with. I was thinking about making a directed graph which is easier task, but it doesn't ensure that every two vertices will be connected.
I would be grateful for any tips and solutions!
Piotry had basically the same idea I did, but he left off a step.
Only read half the matrix, and ignore you diagonal for writing values to. If you always want a node to have an edge to itself, add a one at the diagonal. If you always do not want a node to have an edge to itself, leave it as a zero.
You can read the other half of your matrix for a second graph for testing your implementation.
Look at the description of std::set::erase :
Iterator validity
Iterators, pointers and references referring to elements removed by
the function are invalidated.
All other iterators, pointers and
references keep their validity.
In your code, if next is equal to it, and you erase element of std::set by next, you can't use it. In this case you must (at least) change it and only after this keep using of it.