A star algorithm without diagonal movement - c++

Situation:
I'm trying to translate the A* algoritm into c++ code where there is no diagonal movement allowed but I'm having some strange behaviour.
My question: Is it needed to also take into account the diagonal cost even when there is no diagonal movement possible.
When I do the calculations without diagonal cost (with a 'heuristic' factor 10), I always have the same fscore of 80 (you can see this in the second picture where I calculate fscore, gscore and hscore) and this seems really strange to me. Because of this (almost all the nodes having a fscore of 80) the algorimth doesn't seem to work because to many nodes have the same smallest fscore of 80.
Or is this normal behaviour and will a A* star implementation without diagonal have to perform a lot more work (because more nodes have the same minimum fscore)?.
I don't think this question has anything to do with my code than with my reasoning but I'm posting it anyway:
pathFind.cpp
#include "pathFind.h"
#include <queue>
#include "node.h"
#include <QString>
#include <QDebug>
#include <iostream>
/** Pathfinding (A* algo) using Manhatten heuristics and assuming a monotonic, consistent
* heuristic (the enemies do not change position)
*
* TODO: add variable terrain cost
**/
//dimensions
const int horizontalSize = 20;
const int verticalSize = 20;
//nodes sets
static int closedNodes[horizontalSize][verticalSize]; //the set of nodes already evaluated
static int openNodes[horizontalSize][verticalSize]; // the set of nodes to be evaluated; initialy only containing start node
static int dir_map[horizontalSize][verticalSize]; //map of directions (contains parents-children connection)
//directions
const int dir=4;
static int dirX[dir]={1,0,-1,0};
static int dirY[dir]={0,1,0,-1};
//test class
static int map[horizontalSize][verticalSize]; //map of navigated nodes
pathFind::pathFind(){
//test
srand(time(NULL));
//create empty map
for (int y=0;y<verticalSize;y++){
for (int x=0;x<horizontalSize;x++){
map[x][y]=0;
}
}
//fillout matrix
for (int x=horizontalSize/8;x<horizontalSize*7/8;x++){
map[x][verticalSize/2]=1;
}
for (int y=verticalSize/8;y<verticalSize*7/8;y++){
map[horizontalSize/2][y]=1;
}
//randomly select start and finish locations
int xA,yA,xB,yB;
int n=horizontalSize;
int m=verticalSize;
xA=6;
yA=5;
xB = 14;
yB = 12;
qDebug() <<"Map Size (X,Y): "<<n<<","<<m<<endl;
qDebug()<<"Start: "<<xA<<","<<yA<<endl;
qDebug()<<"Finish: "<<xB<<","<<yB<<endl;
// get the route
clock_t start = clock();
QString route=calculatePath(xA, yA, xB, yB);
if(route=="") qDebug() <<"An empty route generated!"<<endl;
clock_t end = clock();
double time_elapsed = double(end - start);
qDebug()<<"Time to calculate the route (ms): "<<time_elapsed<<endl;
qDebug()<<"Route:"<<endl;
qDebug()<<route<<endl<<endl;
}
QString pathFind::calculatePath(const int & xStart, const int & yStart,const int & xFinish, const int & yFinish){
/** why do we maintain a priority queue?
* it's for maintaining the open list: everytime we acces the open list we need to find the node with the lowest
* fscore. A priority queue is a sorted list so we simply have to grab (pop) the first item of the list everytime
* we need a lower fscore.
*
* A priority queue is a data structure in which only the largest element can be retrieved (popped).
* It's problem is that finding an node in the queue is a slow operation.
**/
std::priority_queue<node> pq[2]; //we define 2 priority list which is needed for our priority change of a node 'algo'
static int index; //static and global variables are initialized to 0
static node *currentNode;
static node *neighborNode;
//first reset maps
resetMaps();
//create start node
static node * startNode;
startNode= new node(xStart,yStart,0,0);
startNode->updatePriority(xFinish, yFinish);
// push it into the priority queue
pq[index].push(*startNode);
//add it to the list of open nodes
openNodes[0][0] = startNode->getPriority();
//A* search
//while priority queue is not empty; continue
while(!pq[index].empty()){
//get current node with the higest priority from the priority list
//in first instance this is the start node
currentNode = new node(pq[index].top().getxPos(),
pq[index].top().getyPos(),
pq[index].top().getDistance(),
pq[index].top().getPriority());
//remove node from priority queue
pq[index].pop();
openNodes[currentNode->getxPos()][currentNode->getyPos()]=0; //remove node from open list
closedNodes[currentNode->getxPos()][currentNode->getyPos()]=1; //add current to closed list
//if current node = goal => finished => retrace route back using parents nodes
if (currentNode->getxPos()==xFinish && currentNode->getyPos()==yFinish){
//quit searching if goal is reached
//return generated path from finish to start
QString path="";
int x,y,direction; //in cpp you don't need to declare variables at the top compared to c
//currentNode is now the goalNode
x=currentNode->getxPos();
y=currentNode->getyPos();
while (!(x==xStart && y==yStart)){
/** We start at goal and work backwards moving from node to parent
* which will take us to the starting node
**/
direction=dir_map[x][y];
path =(direction+dir/2)%dir+path; //we work our way back
//iterate trough our dir_map using our defined directions
x+=dirX[direction];
y+=dirY[direction];
}
//garbage collection; the memory allocated with 'new' should be freed to avoid memory leaks
delete currentNode;
while (!pq[index].empty()){
pq[index].pop();
}
return path;
//return path;
} else {
//add all possible neighbors to the openlist + define parents for later tracing back
//(four directions (int dir): up, down, left & right); but we want to make it
//as extendable as possible
for (int i=0; i<dir; i++){
//define new x & y coord for neighbor
//we do this because we want to preform some checks before making this neighbore node
int neighborX = currentNode->getxPos()+dirX[i];
int neighborY = currentNode->getyPos()+dirY[i];
//check boundaries
//ignore if on closed list (was already evaluted) or if unwalkable (currently not implemented)
if (!(neighborX <0 || neighborY <0 || neighborX > horizontalSize || neighborY > verticalSize ||
closedNodes[neighborX][neighborY]==1)){
//ok -> generate neighbor node
neighborNode = new node (neighborX,neighborY,currentNode->getDistance(),currentNode->getPriority());
//calculate the fscore of the node
neighborNode->updatePriority(xFinish,yFinish);
neighborNode->updateDistance();
//if neighbor not in openset => add it
if(openNodes[neighborX][neighborY]==0){
//add it to open set
openNodes[neighborX][neighborY]=neighborNode->getPriority();
//add it to the priority queue (by dereferencing our neighborNode object
//pq is of type node; push inserts a new element;
//the content is initialized as a copy
pq[index].push(*neighborNode);
//set the parent to the node we are currently looking at
dir_map[neighborX][neighborY]=(i+dir/2)%dir;
//if neighbor is already on open set
//check if path to that node is a better one (=lower gscore) if we use the current node to get there
} else if(openNodes[neighborX][neighborY]>neighborNode->getPriority()) {
/** lower gscore: change parent of the neighbore node to the select square
* recalculate fscore
* the value of the fscore should also be changed inside the node which resides inside our priority queue
* however as mentioned before this is a very slow operation (is going to be the bottleneck of this implemention probably)
* we will have to manuall scan for the right node and than change it.
*
* this check is very much needed, but the number of times this if condition is true is limited
**/
//update fscore inside the open list
openNodes[neighborX][neighborY]=neighborNode->getPriority();
//update the parent node
dir_map[neighborX][neighborY]=(i+dir/2)%dir;
//we copy the nodes from one queue to the other
//except the -old-current node will be ignored
while (!((pq[index].top().getxPos()==neighborX) && (pq[index].top().getyPos()==neighborY))){
pq[1-index].push(pq[index].top());
pq[index].pop();
}
pq[index].pop(); //remove the -old-current node
/** ?? **/
if(pq[index].size()>pq[1-index].size()){ //??? is this extra check necessary?
index=1-index; //index switch 1->0 or 0->1
}
while(!pq[index].empty()){
pq[1-index].push(pq[index].top());
pq[index].pop();
}
/** ?? **/
index=1-index; //index switch 1->0 or 0->1
pq[index].push(*neighborNode); //and the -new-current node will be pushed in instead
} else delete neighborNode;
}
}
delete currentNode;
}
} return ""; //no path found
}
void pathFind::resetMaps(){
for (int y=0;y<horizontalSize;y++){
for (int x=0;x<verticalSize;x++){
closedNodes[x][y]=0;
openNodes[x][y]=0;
}
}
}
node.cpp
#include "node.h"
#include <stdio.h>
#include <stdlib.h>
/** constructor **/
node::node(int x,int y, int d,int p)
{
xPos=x;
yPos=y;
distance=d;
priority=p;
}
/** getters for variables **/
//current node xPosition
int node::getxPos() const {
return xPos;
}
//current node yPosition
int node::getyPos() const {
return yPos;
}
//gscore
int node::getDistance() const {
return distance;
}
//fscore
int node::getPriority() const {
return priority;
}
/** Updates the priority; the lower the fscore the higer the priority
* the fscore is the sum of:
* -path-cost (gscore) (which is the distance from the starting node to the current node)
* -heuristic estimate (hscore) (which is an estimate of the distance from the current node to the destination node)
*
**/
void node::updatePriority(const int & xDest, const int & yDest){
priority = distance + estimateDistance(xDest,yDest)*10;
}
void node::updateDistance(){//const int & direction
distance +=10;
}
/** Estimate function for the remaining distance to the goal
* here it's based on the Manhattan distance;
* which is the distance between two points in a grid based on a strictly horizontal & veritcal path;
* => sum of vertical & horizontal components
**/
const int & node::estimateDistance(const int & xDest, const int & yDest) const{
static int xDistance,yDistance,totalDistance;
xDistance=xDest-xPos;
yDistance=yDest-yPos;
totalDistance=abs(xDistance)+abs(yDistance);
return (totalDistance);
}
/** class functor (I think) to compare elements using:
* operator overloading: "<" gets overloaded which we are going to use in our priority queue
* to determine priority of a node in our queue;
* returns true if node a has a lower fscore compared to node b
*
* there is an ambiguity here: < -- >; better to make both >
* also prototype is now friend which could probably be replaced with this for the first
* argument; it advantage is that because of the friend function the operand order can be reversed
* this doesn't really looks to favor our application; so should I use it or not?
**/
bool operator<(const node & a, const node & b){
return a.getPriority() > b.getPriority();
}

I think your algorithm have no problem, and if there is no diagonal movements available, it won't be necessary to take it into account.
Manhattan distance is a simple heuristic and as you become closer to destination node the H function gives smaller numbers though the G function(distance from first node till here) gets bigger. As the result, many nodes have the same f score.

A* is full generic- the graph you give it can be any shape at all. It only cares about whether you can go from X to Y, not why. If diagonal movement is not allowed, it does not care. And it is quite fine and legitimate for many nodes to have the same f score- it is, in fact, expected.

i think it doesn't matter how many squares have the same f score Bcz you choose one among them. As said in the main algorithm of A*, "For the purposes of speed, it can be faster to choose the last one you added to the open list".
I hope it wouldn't be a probelm if you don't consider diagonal costs. Seems interesting how it give results.
I hope you already read this article. If not have a look at it clearly.

Related

Error reconstructing path in a grid using BFS

The problem I am facing is the following:
I have a function based on the BFS search algorithm that I use in a NxM grid, the mission of this function is to return the following Direction from a set of possible Directions = {Up, Down, Left , Right} (No diagonal moves!)to which a player has to move, so that in each "round / frame" where there is a type of item of a game (For example, in this specific case, a bazooka) is closer to the item.
To address the problem, I have created a Map class made of vector <vector <Cell> > where vector is from the standard library and Cell is what the grid is made of and has some consulting methods on what is in one of the NxM cells (if there is a building, an enemy, a Bazooka, etc.)
So, for implementing a solution for this, I made a struct TrackingBFS to reconstruct the path of the BFS search:
struct TrackingBFS {
pair <int,int> anterior;
bool visited;
};
And this is the BFS search implementation:
//Pre: origen is a valid position on the grid where the player is
//Post:Returns a pair of bool and a direction to the closest bazooka. If we have access to a bazooka, then we will return a pair (true,Dir) where Dir is a direction to take to be closer to the bazooka else a pair (false, Dir) where dir is just the same direction as origen.
pair <bool,Dir> direction_to_closest_gun (const Pos& origen) {
//R = board_rows() C = board_cols()
//m = mapa
//sr,sc = origin positions
int sr = origen.i;
int sc = origen.j;
//Variables para mantener tracking de los pasos cogidos
queue <int> rq; //Queue of x-row coordinates
queue <int> cq; //Queue of y-row coordinates
int move_count = 0; //Number of steps
int nodes_left_in_layer = 1; //How many nodes we need to de-queue before doing a step
int nodes_in_next_layer = 0; //How many nodes we add to the expansio of the BFS so we can upgrade nodes_left_in_layer in the next iteration
bool arma_mejor_encontrada = false;
//Visited is a MxN matrix of TrackingBFS that for every i,j stores the following information:
//If we visited the cell at position visited [i][j], then the TrackingBFS struct will be visited = true and last_node = (k,l) where k and l are the components of the LAST cell on the grid we visited in the BFS search.
//Else if we didn't visited the cell at [i][j], the TrackingBFS struct will be visited = true and last_node (-1,-1).
TrackingBFS aux;
aux.last_node = make_pair(-1,-1);
aux.visited = false;
//We create a Matrix of TrackingBFS named visited of NxM filled with the unvisited cells
vector < vector <TrackingBFS> > visited (board_rows(), vector <TrackingBFS> (board_cols(),aux));
//--------------------------------SOLVE---------------------------------
rq.push(sr);
cq.push(sc);
visited[sr][sc].visited = true;
visited[sr][sc].last_node = make_pair(sr,sc);
int xfound;
int yfound;
while (!rq.empty()) {
int r = rq.front();
int c = cq.front();
rq.pop();
cq.pop();
if (mapa[r][c].weapon == Bazooka) {
arma_mejor_encontrada = true;
xfound = r;
yfound = c;
break;
}
//Explore neighbours
Pos p (r,c);
for (Dir d : dirs) {
Pos searching = p + d;
int rr = searching.i;
int cc = searching.j;
//If the position we are searching is out of range or it's been visited before or there is a obstacle then continue
if (!pos_ok(searching) or visited[rr][cc].visited or mapa[rr][cc].type == Building or mapa[rr][cc].resistance != -1 or mapa[rr][cc].id != -1) {
//NADA
}
//Else we add the visited node to the queue, and fill the information on visited[rr][cc] with the node we are coming and mark it as visited
else {
rq.push(rr);
cq.push(cc);
visited[rr][cc].visited = true;
visited[rr][cc].last_node = make_pair (r,c);
nodes_in_next_layer++;
}
}
nodes_left_in_layer--;
if (nodes_left_in_layer == 0) {
nodes_left_in_layer = nodes_in_next_layer;
nodes_in_next_layer = 0;
move_count++;
}
}
//If we found the Bazooka
if (arma_mejor_encontrada) {
//Return the pair (true,next direction of the player at position (sr,sc) to approach the bazooka)
return make_pair(arma_mejor_encontrada, reconstruir_camino(visited,xfound,yfound,sr,sc));
}
else {
//If there is no bazooka we return (false,Up (this second component is meaningless))
return make_pair(arma_mejor_encontrada, Up);
}
}
The reconstruir_camino (recosntruct_path in english) implementation:
//This function is made to reconstruct the path from where we found de bazooka (x,y) to where our player is (ox,oy), whe are only interested in the next position of because this is run each frame, so we are constantly refreshing the state of the map.
Dir reconstruir_camino(const vector < vector <TrackingBFS> >& visited, const int& x, const int& y, const int& ox, const int& oy) {
//In v we save the pair of coordinates of our path, this was done only for debuging and in debug_matriz_tracking(visited) is also only for debuging
vector <pair<int,int>> path;
debug_matriz_tracking(visited);
//
int i = visited[x][y].last_node.first;
int j = visited[x][y].last_node.second;
while (not (i == ox and j == oy)) { //While the next node is not iqual as the original node we started de search (The one where our player is)
path.push_back(make_pair(i,j)); //Just for debuging
i = visited[i][j].last_node.first;
j = visited[i][j].last_node.second;
}
//So now in path we have the start and end nodes of every edge on the path
int siguiente_x = path.back().first;
int siguiente_y = path.back().second;
debug_camino(path);
return direccion_contiguos(ox,oy,siguiente_x,siguiente_y);
}
And direccion_contiguos (contiguous direction / relative direction in english) implementation:
//Returns the RELATIVE direction to go if we want to go from (ox, oy) to (dx, dy) being these two contiguous positions, that is, (dx, dy) is in Up, Down, Left or Right with respect to (ox, oy) IT WORKS FINE, NOTHING WRONG WITH THIS DON'T TOUCH
Dir direccion_contiguos (const int& ox, const int& oy, const int& dx, const int& dy) {
Pos o (ox,oy);
Pos d (dx,dy);
if (o + Up == d) {
return Up;
}
else if (o + Down == d){
return Down;
}
else if (o + Left == d) {
return Left;
}
else if (o + Right == d) {
return Right;
}
return Down;
}
So now in visited, we have the information to reconstruct the path, in fact I debuged it (it's kinda messy i know, sorry), in a visual way so this is what I got for a Player in origen = (7,10) and bazooka at position = (4,11):
[Imgur link of the Visual representation of the Matrix for reconstructing the path from origin to bazooka][1]
To read this image, at the top and left there are the coordinates of every cell of the visited matrix, the ones with green font, are the ones that have been visited, and they store THE NEXT cell/vertex of the path, and the ones with (-1,-1) are the ones that have not been visited by the BFS algorithm and thus they don't have any previous node and are in white.
So, NICE! It seems to work, at least the visited matrix.
My problem is when I debug the vector of edges/directions of the graph/grid, this is what I used in the example of the image:
void debug_camino(const vector <pair<int,int>>& v) {
cerr << "----------------------CAMINO-------------------- DEBUG_CAMINO" << endl;
for (long unsigned int i = 0; i < v.size(); ++i) {
cerr << "(" << v[i].first << "," << v[i].second << "),";
}
cerr << endl;
}
And when I executed the program, this is the path that I got with debug_camino():
If you see the image attached you can see that that's almost the path but not quite yet.
(4,12),(4,13),(4,14),(3,15),(3,16),(4,16),(5,16),(6,16),(7,15),(7,14),(7,13),(7,12),(7,11)
These ones bolded are not real (even valid because they are diagonal moves) reconstructions of the path and I don't really know WHY this is happening, but it's provoking my player to not following right path, and I want to fix the error and I'm kinda desperate because I don't really know where the error is and I've been trying for days :( ! I hope somebody can help me with this. Thanks for reading all this and sorry if the code is in some parts in Spanish or if it's not all that readable.
[1]: https://i.stack.imgur.com/vZ2Go.png
Okay, I actually managed to fix this error, i was overwriting the i variable so that was causing the error.
//This function is made to reconstruct the path from where we found de bazooka (x,y) to where our player is (ox,oy), whe are only interested in the next position of because this is run each frame, so we are constantly refreshing the state of the map.
Dir reconstruir_camino(const vector < vector <TrackingBFS> >& visited, const int& x, const int& y, const int& ox, const int& oy) {
//In v we save the pair of coordinates of our path, this was done only for debuging and in debug_matriz_tracking(visited) is also only for debuging
vector <pair<int,int>> path;
debug_matriz_tracking(visited);
//
int i = visited[x][y].last_node.first;
int j = visited[x][y].last_node.second;
while (not (i == ox and j == oy)) { //While the next node is not iqual as the original node we started de search (The one where our player is)
path.push_back(make_pair(i,j)); //Just for debuging
**i** = visited[i][j].last_node.first;
j = visited[**i**][j].last_node.second;
}
//So now in path we have the start and end nodes of every edge on the path
int siguiente_x = path.back().first;
int siguiente_y = path.back().second;
debug_camino(path);
return direccion_contiguos(ox,oy,siguiente_x,siguiente_y);
}
It's already fixed

Kd-Tree flawed K nearest neighbor

Disclaimer: There are some bad practices in this following code
Hello, I just had a few questions on how to correctly format my KD tree K nearest neighbor search. Here is an example of my function.
void nearest_neighbor(Node *T, int K) {
if (T == NULL) return;
nearest_neighbor(T->left, K);
//do stuff find dist etc
if(?)nearest_neighbor(T->right, K);
}
This code is confusing so I will try to explain it. My function only takes the k value and a Node T. What I am trying to do is find the distance between the current node and every other value in the structure. These all work, the issue I'm having is understanding when and how to call the recursive calls nearest_neighbor(T->left/T->right,K) I know I am meant to prune the calls to the right side but I'm not sure how to do this. This is an multidimensional KD Tree by the way. Any guidance to better examples would be very appreciated.
I would advise you to implement like Wikipedia says, where for your specific question, this:
Starting with the root node, the algorithm moves down the tree
recursively, in the same way that it would if the search point were
being inserted (i.e. it goes left or right depending on whether the
point is lesser than or greater than the current node in the split
dimension).
answers the question. Of course you can have this image in mind:
where if you have more two dimensions like in the example, you simply split in the first dimension, then in the second, then in the third, then in the forth and so on, and then you follow a cyclic policy, so that when you reach the final dimension, you start from the first dimension again.
The general idea is to keep a global point closest to the target, updating with newly discovered points and never descending into an n-gon that can't possibly contain a point closer than the nearest to the target already found. I'll show it in C rather than C++. You can easily translate to object-oriented form.
#define N_DIM <k for the k-D tree>
typedef float COORD;
typedef struct point_s {
COORD x[N_DIM];
} POINT;
typedef struct node_s {
struct node_s *lft, *rgt;
POINT p[1];
} NODE;
POINT target[1]; // target for nearest search
POINT nearest[1]; // nearest found so far
POINT b0[1], b1[1]; // search bounding box
bool prune_search() {
// Return true if no point in the bounding box [b0..b1] is closer
// to the target than than the current value of nearest.
}
void search(NODE *node, int dim);
void search_lft(NODE *node, int dim) {
if (!node->lft) return;
COORD save = b1->p->x[dim];
b1->p->x[dim] = node->p->x[dim];
if (!prune_search()) search(node->lft, (dim + 1) % N_DIM);
b1->p->x[dim] = save;
}
void search_rgt(NODE *node, int dim) {
if (!node->rgt) return;
COORD save = b0->p->x[dim];
b0->p->x[dim] = node->p->x[dim];
if (!prune_search()) search(node->rgt, (dim + 1) % N_DIM);
b0->p->x[dim] = save;
}
void search(NODE *node, int dim) {
if (dist(node->p, target) < dist(nearest, target)) *nearest = *node->p;
if (target->p->x[dim] < node->p->x[dim]) {
search_lft(node, dim);
search_rgt(node, dim);
} else {
search_rgt(node, dim);
search_lft(node, dim);
}
}
/** Set *nst to the point in the given kd-tree nearest to tgt. */
void get_nearest(POINT *nst, POINT *tgt, NODE *root) {
*b0 = POINT_AT_NEGATIVE_INFINITY;
*b1 = POINT_AT_POSITIVE_INFINITY;
*target = *tgt;
*nearest = *root->p;
search(root, 0);
*nst = *nearest;
}
Note this is not the most economical implementation. It does some unnecessary nearest updates and pruning comparisons for simplicity. But its asymptotic performance is as expected for kd-tree NN. After you get this one working, you can use it as a base implementation to squeeze out the extra comparisons.

Connected Components with Periodic Boundary Conditions

Normally to find the connected components on a set of points, I build a graph from the points that are placed in 2 dimensional Euclidean space where the edges are determined with thresholding. Namely, if the distance between two points is closer than a predetermined cut-off radius, then I consider them as neighbors. Then I do a depth-first search on this graph to determine the connected components.
The problem with this approach is I have to use thresholding to build the graph in the first place. I am not a computer scientist, so I never took an algorithms class. I would like to know if there is an algorithm with which I can find nearest neighbors or connected components without building the edges with thresholding? The main issue that makes thresholding so preferable is the fact that this box is periodic. That's why googling alone didn't help me much.
My code for this look like this:
// +++++
// Graph
// +++++
// ( Note that edges are lines connecting nodes or vertices )
class Graph
{
public:
Graph() {}
~Graph() {}
void setNumNodes( int nds );
int getNumNodes() { return numNodes; }
void addEdge( int nd_idx, set<int> dest );
map<int,set<int> > edges; // [node, destination]
void setThreshold( double cutoff, double carpan );
double getThreshold() { return threshold; }
private:
int numNodes;
double threshold;
};
void Graph::setNumNodes( int nds ){
numNodes = nds;
}
void Graph::addEdge( int nd_idx, set<int> dest ){
edges.insert( pair<int,set<int> >( nd_idx, dest ) );
}
void Graph::setThreshold( double cutoff, double carpan ){
threshold = 2*R + carpan*cutoff;
}
// +++++
// Function for depth-first search from a starting node
int dfsFromNode( Graph& graph, int i, stack<int>& S, vector<bool>& visited ){
int connected_comp = 0;
// Add the node to the stack
S.push( i );
// While there are nodes that are not visited
// (so as long as stack is not empty ..)
while( !S.empty() ){
// Remove the top of the stack (backtracking process)
S.pop();
if( !visited[i] ){
visited[i] = true;
connected_comp++;
set<int> neighbors;
neighbors = graph.edges[i];
for( auto j: neighbors ){
i = j;
S.push( i );
}
} // if the node is visited before, get out
} // while loop to check if the stack is empty or not
return connected_comp;
}
edit:
To reiterate the question, how can I find the nearest neighbors or connected components without doing thresholding in periodic boundary settings?
For finding connected components, you can use kd-trees. A k-d tree, (abbrev. k-dimensional tree) is an algorithm in which you split your data points into two alternating between orthogonal directions in each degrees of freedom. I find the following link quite useful for explanation.
Specifically, in the case of periodic boundary conditions, you can just ghost/image particles outside of the primary box and build the kd-tree including these particles.

Finding nearest neighbor(s) in a KD Tree

Warning: Fairly long question, perhaps too long. If so, I apologize.
I'm working on a program involving a nearest neighbor(s) search of a kd tree (in this example, it is an 11 dimensional tree with 3961 individual points). We've only just learned about them, and while I have a good grasp of what the tree does, I get very confused when it comes to the nearest neighbor search.
I've set up a 2D array of points, each containing a quality and a location, which looks like this.
struct point{
double quality;
double location;
}
// in main
point **parray;
// later points to an array of [3961][11] points
I then translated the data so it has zero mean, and rescaled it for unit variance. I won't post the code as it's not important to my questions. Afterwards, I built the points into the tree in random order like this:
struct Node {
point *key;
Node *left;
Node *right;
Node (point *k) { key = k; left = right = NULL; }
};
Node *kd = NULL;
// Build the data into a kd-tree
random_shuffle(parray, &parray[n]);
for(int val=0; val<n; val++) {
for(int dim=1; dim<D+1; dim++) {
kd = insert(kd, &parray[val][dim], dim);
}
}
Pretty standard stuff. If I've used random_shuffle() incorrectly, or if anything is inherently wrong about the structure of my tree, please let me know. It should shuffle the first dimension of the parray, while leaving the 11 dimensions of each in order and untouched.
Now I'm on to the neighbor() function, and here's where I've gotten confused.
The neighbor() function (last half is pseudocode, where I frankly have no idea where to start):
Node *neighbor (Node *root, point *pn, int d,
Node *best, double bestdist) {
double dist = 0;
// Recursively move down tree, ignore the node we are comparing to
if(!root || root->key == pn) return NULL;
// Dist = SQRT of the SUMS of SQUARED DIFFERENCES of qualities
for(int dim=1; dim<D+1; dim++)
dist += pow(pn[d].quality - root->key->quality, 2);
dist = sqrt(dist);
// If T is better than current best, current best = T
if(!best || dist<bestdist) {
bestdist = dist;
best = root;
}
// If the dist doesn't reach a plane, prune search, walk back up tree
// Else traverse down that tree
// Process root node, return
}
Here's the call to neighbor in main(), mostly uncompleted. I'm not sure what should be in main() and what should be in the neighbor() function:
// Nearest neighbor(s) search
double avgdist = 0.0;
// For each neighbor
for(int i=0; i<n; i++) {
// Should this be an array/tree of x best neighbors to keep track of them?
Node *best;
double bestdist = 1000000000;
// Find nearest neighbor(s)?
for(int i=0; i<nbrs; i++) {
neighbor(kd, parray[n], 1, best, &bestdist);
}
// Determine "distance" between the two?
// Add to total dist?
avgdist += bestdist;
}
// Average the total dist
// avgdist /= n;
As you can see, I'm stuck on these last two sections of code. I've been wracking my brain over this for a few days now, and I'm still stuck. It's due very soon, so of course any and all help is appreciated. Thanks in advance.
The kd-tree does not involve shuffling.
In fact, you will want to use sorting (or better, quickselect) to build the tree.
First solve it for the nearest neighbor (1NN). It should be fairly clear how to find the kNN once you have this part working, by keeping a heap of the top candidates, and using the kth point for pruning.

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.