I am attempting an online coding challenge wherein I am to implement a pathfinding algorithm that finds the shortest path between two points on a 2D grid. The code that is submitted is tested against a number of test cases that I, unfortunately, am unable to see, but it will however tell me if my answer for shortest distance is correct or not. My implementation of the A* algorithm returns a correct answer on 2/3 test cases and I cannot seem to figure out what scenario might create an incorrect answer on the third?
I have tried several of my own test cases and have gotten correct answers for all of those and at this point am feeling a little bit lost. There must be something small in my code that I am not seeing that is causing this third case to fail.
More details
The grid is w by h and contains only 1's (passable) and 0's (impassable) with every edge having a cost of 1 and the pathway cannot move diagonally
It all starts with the FindPath function which is to return the length of the shortest path, or -1 if no path is available
pOutBuffer is used to contain the path taken from beginning to end (excluding the starting point). If multiple paths are available then any will be accepted. So it isnt looking for one path in particular
I know the issue is not the result of time or memory inefficiency. I has to be either the distance returned is incorrect, or the values in pOutBuffer are incorrect.
Any help would be greatly appreciated as I am just about out of ideas as to what could possibly be wrong here. Thank you.
#include <set>
#include <vector>
#include <tuple>
#include <queue>
#include <unordered_map>
inline int PositionToIndex(const int x, const int y, const int w, const int h)
{
return x >= 0 && y >= 0 && x < w && y < h? x + y * w : -1;
}
inline std::pair<int, int> IndexToPosition(const int i, const int w)
{
return std::make_pair<int, int>(i % w, i / w);
}
inline int Heuristic(const int xa, const int ya, const int xb, const int yb)
{
return std::abs(xa - xb) + std::abs(ya - yb);
}
class Map
{
public:
const unsigned char* mapData;
int width, height;
const std::vector<std::pair<int, int>> directions = { {1,0}, {0,1}, {-1,0}, {0,-1} };
Map(const unsigned char* pMap, const int nMapWidth, const int nMapHeight)
{
mapData = pMap;
width = nMapWidth;
height = nMapHeight;
}
inline bool IsWithinBounds(const int x, const int y)
{
return x >= 0 && y >= 0 && x < width && y < height;
}
inline bool IsPassable(const int i)
{
return mapData[i] == char(1);
}
std::vector<int> GetNeighbours(const int i)
{
std::vector<int> ret;
int x, y, neighbourIndex;
std::tie(x, y) = IndexToPosition(i, width);
for (auto pair : directions)
{
neighbourIndex = PositionToIndex(x + pair.first, y + pair.second, width, height);
if (neighbourIndex >= 0 && IsWithinBounds(x + pair.first, y + pair.second) && IsPassable(neighbourIndex))
ret.push_back(neighbourIndex);
}
return ret;
}
};
int FindPath(const int nStartX, const int nStartY,
const int nTargetX, const int nTargetY,
const unsigned char* pMap, const int nMapWidth, const int nMapHeight,
int* pOutBuffer, const int nOutBufferSize)
{
int ret = -1;
// create the map
Map map(pMap, nMapWidth, nMapHeight);
// get start and end indecies
int targetIndex = PositionToIndex(nTargetX, nTargetY, nMapWidth, nMapHeight);
int startIndex = PositionToIndex(nStartX, nStartY, nMapWidth, nMapHeight);
// if start and end are same exit
if (targetIndex == startIndex) return 0;
std::unordered_map<int, int> pathway = { {startIndex, startIndex} };
std::unordered_map<int, int> distances = { {startIndex, 0} };
// queue for indecies to process
typedef std::pair<int, int> WeightedLocation;
std::priority_queue<WeightedLocation, std::vector<WeightedLocation>, std::greater<WeightedLocation>> queue;
queue.emplace(0, startIndex);
while (!queue.empty())
{
int currentWeight, currentIndex;
std::tie(currentWeight, currentIndex) = queue.top();
queue.pop();
if (currentIndex == targetIndex)
break;
int newDistance = distances[currentIndex] + 1;
for (int n : map.GetNeighbours(currentIndex))
{
if (distances.find(n) == distances.end() || newDistance < distances[n])
{
distances[n] = newDistance;
int weight = newDistance + Heuristic(n % nMapWidth, n / nMapWidth, nTargetX, nTargetY);
queue.emplace(weight, n);
pathway[n] = currentIndex;
}
}
}
if (pathway.find(targetIndex) != pathway.end())
{
int current = targetIndex;
while (current != startIndex)
{
int outIndex = distances[current] - 1;
pOutBuffer[distances[current] - 1] = current;
current = pathway[current];
}
ret = distances[targetIndex];
}
return ret;
}
Related
I'm learning C++ and am doing something I'm comfortable with in java to start out. Particle simulation and flocking using a quadtree to cheaply find particles in a region. Everything is working but when I use the quadtree to get the particles from a region it's really slow (about 1s for 5000 calls).
I tried replacing the vector with an array and measuring the execution time of various parts of the function.
Am I making any rooky mistakes like unnecessarily copying objects etc.? I'm using 5000 particles, I can't imagine 1fps is the fastest it can go.
Full code for minimal reproducable example as per request:
main.cpp
#include <string>
#include <iostream>
#include <random>
#include <chrono>
#include <thread>
#include <cmath>
#include "Particle.h"
#include "Quadtree.h"
// Clock
using namespace std::chrono;
using namespace std::this_thread;
// Global constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int desiredFPS = 30;
const int frameTimeMS = int(1000 / (double)desiredFPS);
const int numberOfParticles = 5000;
// Random number generation
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_real_distribution<> dist(0, 1);
Particle particles[numberOfParticles];
Quadtree quadtree = Quadtree(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
int main(int argc, char* args[])
{
for (int i = 0; i < numberOfParticles; i++)
{
particles[i] = Particle(dist(rng) * SCREEN_WIDTH, dist(rng) * SCREEN_HEIGHT);
}
// Clock for making all frames equally long and achieving the desired framerate when possible
auto lapStartTime = system_clock::now();
// Main loop
for (int i = 0; i < 1; i++)
{
// Insert the particles into the quadtree
quadtree = Quadtree(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
for (int i = 0; i < numberOfParticles; i++)
{
quadtree.insert(&particles[i]);
}
double neighbourhoodRadius = 40;
for (int i = 0; i < numberOfParticles; i++)
{
// THIS IS THE PART THAT IS SLOW
std::vector<Particle*> neighbours = quadtree.getCircle(
particles[i].x,
particles[i].y,
neighbourhoodRadius
);
}
// Update clocks
auto nextFrameTime = lapStartTime + milliseconds(frameTimeMS);
sleep_until(nextFrameTime);
lapStartTime = nextFrameTime;
}
return 0;
}
Quadtree.h
#pragma once
#include <vector>
#include "Particle.h"
#include "Rect.h"
class Quadtree
{
public:
const static int capacity = 10; // Capacity of any section
Quadtree(double px, double py, double width, double height);
Quadtree(Rect r);
bool insert(Particle* p); // Add a particle to the tree
std::vector<Particle*> getCircle(double px, double py, double r);
int numberOfItems(); // Total amount in the quadtree
private:
std::vector<Particle*> particles; // Particles stored by this section
std::vector<Quadtree> sections; // Branches (only if split)
Rect area; // Region occupied by the quadtree
bool isSplit() { return sections.size() > 0; }
void split(); // Split the quadtree into 4 branches
};
Quadtree.cpp
#include <iostream>
#include "Quadtree.h"
Quadtree::Quadtree(double px, double py, double width, double height)
{
area = Rect(px, py, width, height);
sections = {};
particles = {};
}
Quadtree::Quadtree(Rect r)
{
area = r;
sections = {};
particles = {};
}
bool Quadtree::insert(Particle* p)
{
if (area.intersectPoint(p->x, p->y))
{
if (!isSplit() && particles.size() < capacity)
{
particles.push_back(p);
}
else
{
if (!isSplit()) // Capacity is reached and tree is not split yet
{
split();
}
// That this is a reference is very important!
// Otherwise a copy of the tree will be modified
for (Quadtree& s : sections)
{
if (s.insert(p))
{
return true;
}
}
}
return true;
}
else
{
return false;
}
}
std::vector<Particle*> Quadtree::getCircle(double px, double py, double r)
{
std::vector<Particle*> selection = {};
if (!isSplit())
{
// Add all particles from this section that lie within the circle
for (Particle* p : particles)
{
double a = px - p->x;
double b = py - p->y;
if (a * a + b * b <= r * r)
{
selection.push_back(p);
}
}
}
else
{
// The section is split so add all the particles from the
// branches together
for (Quadtree& s : sections)
{
// Check if the branch and the circle even have any intersection
if (s.area.intersectRect(Rect(px - r, py - r, 2 * r, 2 * r)))
{
// Get the particles from the branch and add them to selection
std::vector<Particle*> branchSelection = s.getCircle(px, py, r);
selection.insert(selection.end(), branchSelection.begin(), branchSelection.end());
}
}
}
return selection;
}
void Quadtree::split()
{
sections.push_back(Quadtree(area.getSection(2, 2, 0, 0)));
sections.push_back(Quadtree(area.getSection(2, 2, 0, 1)));
sections.push_back(Quadtree(area.getSection(2, 2, 1, 0)));
sections.push_back(Quadtree(area.getSection(2, 2, 1, 1)));
std::vector<Particle*> oldParticles{ particles };
particles.clear();
for (Particle* p : oldParticles)
{
bool success = insert(p);
}
}
int Quadtree::numberOfItems()
{
if (!isSplit())
{
return particles.size();
}
else
{
int result = 0;
for (Quadtree& q : sections)
{
result += q.numberOfItems();
}
return result;
}
}
Particle.h
#pragma once
class Particle {
public:
double x;
double y;
Particle(double px, double py) : x(px), y(py) {}
Particle() = default;
};
Rect.h
#pragma once
class Rect
{
public:
double x;
double y;
double w;
double h;
Rect(double px, double py, double width, double height);
Rect() : x(0), y(0), w(0), h(0) {}
bool intersectPoint(double px, double py);
bool intersectRect(Rect r);
Rect getSection(int rows, int cols, int ix, int iy);
};
Rect.cpp
#include "Rect.h"
Rect::Rect(double px, double py, double width, double height)
{
x = px;
y = py;
w = width;
h = height;
}
bool Rect::intersectPoint(double px, double py)
{
return px >= x && px < x + w && py >= y && py < y + h;
}
bool Rect::intersectRect(Rect r)
{
return x + w >= r.x && y + h >= r.y && x <= r.x + r.w && y <= r.y + r.w;
}
Rect Rect::getSection(int cols, int rows, int ix, int iy)
{
return Rect(x + ix * w / cols, y + iy * h / rows, w / cols, h / rows);
}
So... In the original code creating the quadtree takes about 0.001s (relatively insignificant), and the neighbor search takes about 0.06s - here is our culprit (as mentioned by the OP).
Passing the std::vector<Particle*> neighbours as a reference to the getCircle function, gets rid of the insert call at the end of the function as well as new vector allocations (hi to everyone saying "oh, it will be optimized away automatically"). The time is reduced to 0.011s.
The nieghbours vector can be taken out of the main loop, and cleared after use, so that it only resizes on the first frame.
I do not see any more immediately obvious targets (without doing a complete rewrite). Maybe I will add something later.
I decided to approach this more systematically: I added an #if switch for every change I made and actually recorded some statistics, instead of eyeballing it. (Evey change is added incrementally, times include tree construction).
original
by reference
out of loop
min time:
0.0638s
0.0127s
0.0094s
avg time:
0.0664s
0.0136s
0.0104s
max time:
0.0713s
0.0157s
0.0137s
All measurements were done on my machine, with optimized build, using QueryPerfoemanceCounter.
I did end up rewriting the whole thing...
Got rid of vectors.
The Quadtree::particles is now Particle* particles[capacity] with a count.
sections is a pointer; isSplit just checks if sections is 0.
Since the total (or maximum) number of particles is known, the number of particles that can be returned by getCircle can't be more than that. So I allocate that much outside of the main loop to store neighbours. Adding another result involves just bumping a pointer (without even a check in release). And resetting it after use is done by setting the count to 0 (see arena or bump allocator).
The maximum number of quadtree nodes can be inferred from the number of particles. So, similarly, splitting just bumps the pointer by 4.
Trying to precompute the Rect in getCircle, or put px, py, r (and/or that rect as well) in a struct (passed as value or reference) does not yield any improvement (or is detremental). (was suggested by Goswin von Brederlow).
Then I flipped the recursion (was suggested by Ted Lyngmo). The temporary stack is, again, preallocated. And then I did the same thing for insert.
rewrite
non-recursive
insert as well
min_time:
0.0077
0.0069
0.0068
avg_time:
0.0089
0.0073
0.0070
max_time:
0.0084
0.0078
0.0074
So in the end the most impactful thing was the very first - not inserting and not creating unnecessary vectors every call, but instead passing the same one by reference.
One last thing - might want to store the quadtree particles separately, since most of the time getCircle is traversing nodes, where particles are not stored.
Otherwise, I do not see how to improve this any more. At this point it would require someone actually smart or crazy...
EDIT: Reposting; tried the problem a bit more and reposting. I'm genuinely lost at this point.
Have this small graph problem to do for today, was wondering if anyone had any possible solutions/insight for it.
Given two containers, one of which can accommodate a liters of water and the other b liters of water, determine the number of steps required to obtain exactly target liters of water in one of the containers, or -1 if it cannot be done.
At the beginning both containers are empty. The following operations are counted as 'steps':
Empty a contain er
Fill a container
Pour water from one container to the other, without spilling, until one of the containers is either full or empty
To get you started, we provide you a Node and a Graph class. Your job is to implement the function Graph createGraph(int capacity_a, int capacity_b), which builds a graph that contains all the possible container states given the capacity of two containers, and int findSolution(Graph g, int target), which does the graph traversal and returns the minimum steps.
You can modify the structures if you like, or build your own solution without using the provided structures. We will only test your int waterPouring(int a, int b, int target) function.
Hint: Which algorithm guarantees a minimun number of steps to reach a target?
My initial guess was Dijkstra's for the findSolution(), and attempted to translate some pseudo-code, but that didn't work out. I (think) I implemented createGraph correctly. Quite unsure as to where to go/if there's a better way to do this. Here's the code:
Thank you!
waterPouring.cpp:
#include <unordered_map>
#include <queue>
#include <vector>
using namespace std;
#define EMPTY 0
class Node {
public:
int a;
int b;
vector<Node *> neighbors;
Node () : a(EMPTY), b(EMPTY), neighbors() {}
Node (const int &a_, const int &b_) : a(a_), b(b_), neighbors() {}
Node (const int &a_, const int &b_, const vector<Node *> &neighbors_) : a(a_), b(b_), neighbors(neighbors_) {}
Node (const Node &tmpNode) : a(tmpNode.a), b(tmpNode.b), neighbors() {}
bool operator==(const Node & b_node)
{
return a == b_node.a && b == b_node.b;
}
Node &operator=(const Node & b_node) {
// WARNING: This operator does not copy the vector
a = b_node.a;
b = b_node.b;
return *this;
}
};
struct Graph {
vector<Node *> nodes;
};
Graph createGraph(int capacity_a, int capacity_b) {
// TODO
Graph g;
Node * capacityNode = new Node(capacity_a, capacity_b);
for (int i = 0; i < g.nodes.size(); i++) {
g.nodes.push_back(capacityNode);
}
return g;
}
int findSolution(Graph g, int target) {
// TODO: returns minimum number of steps to reach target liters of
water (or -1)
for (int& node : g) {
// not sure
}
return -1;
}
int waterPouring(int a, int b, int target) {
// Call createGraph
// Call findSolution
Graph stateMachineGraph = createGraph(a, b);
int steps = findSolution(stateMachineGraph, target);
for (Node *graphNode : stateMachineGraph.nodes)
{
delete graphNode;
}
return steps;
}
If you are ok with a solution without using a graph (provided task description allows it), you can do the following:
Suppose you have two containers with capacities a and b and you need to get c liters in the end.
Suppose you have two containers with capacities a and b and you need to get c liters in the end.
First observe, that every operation you are allowed to perform moves x * a + y * b liters of water. E.g. if you are pouring water from the full second to the full first container you are pouring 1 * b - 1 * a. You can continue to persuade yourself that this is true. This gives us the following equation:
x * a + y * b = c
This is a diophantine equation and it has a solution if gcd(a, b) divides c (see Bézout's identity). You can solve it using extended Euclidean algorithm. If c is less than max(a, b) then either x or y less than zero. Suppose x > 0. Then you need to full a-container x times, pour water from it to b-container emptying it y times.
Example: a = 9, b = 5, c = 6. We have
-1 * 9 + 3 * 5 = 6
So, we need to
0 5 // Full the second (1)
5 0 // Pour to the first
5 5 // Full the second (2)
9 1 // Pour to the first
0 1 // Empty the first (-1)
1 0 // Pour to the first
1 5 // Full the second (3)
6 0 // Pour to the first
But if you really want to use graph, then
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <queue>
struct Node { int a, b; };
class Graph {
public:
std::vector<std::pair<Node, std::vector<int>>> nodes;
static Graph Create(int a, int b) {
auto index = [a,b](int i, int j) {
return i * (b + 1) + j;
};
Graph g;
for (int i = 0; i <= a; ++i) {
for (int j = 0; j <= b; ++j) {
std::vector<int> adj;
if (i < a) adj.push_back(index(a, j));
if (i > 0) adj.push_back(index(0, j));
if (j < b) adj.push_back(index(i, b));
if (j > 0) adj.push_back(index(i, 0));
int da = std::min(a - i, j);
int db = std::min(b - j, i);
adj.push_back(index(i + da, j - da));
adj.push_back(index(i - db, j + db));
std::sort(adj.begin(), adj.end());
adj.erase(std::unique(adj.begin(), adj.end()), adj.end());
g.nodes.push_back({ { i,j }, adj });
}
}
return g;
}
// Breadth-first search
std::pair<int, std::vector<int>> Shortest(int target) const {
std::vector<bool> visited(nodes.size(), 0);
std::vector<int> dist(nodes.size(), std::numeric_limits<int>::max());
std::vector<int> prev(nodes.size(), -1);
std::queue<int> q;
int cur_dist = 0;
q.push(0); visited[0] = true; dist[0] = 0;
while (q.size() > 0) {
int index = q.front(); q.pop();
for (auto i : nodes[index].second) {
if (nodes[i].first.a == target || nodes[i].first.b == target) {
int j = index;
std::vector<int> path = { i, index };
while (prev[j] != -1) {
path.push_back(j = prev[j]);
}
return { dist[index] + 1, path };
}
if (!visited[i]) {
q.push(i); visited[i] = true; dist[i] = dist[index] + 1; prev[i] = index;
}
}
}
return { -1, {} };
}
};
int main()
{
const auto g = Graph::Create(9, 5);
const auto p = g.Shortest(6);
for (int i = (int)p.second.size() - 1; i >= 0; --i) {
std::cout << g.nodes[p.second[i]].first.a << " " << g.nodes[p.second[i]].first.b << std::endl;
}
std::cout << std::endl << p.first << std::endl;
return 0;
}
The output is the same:
0 0
0 5
5 0
5 5
9 1
0 1
1 0
1 5
6 0
8
I'm looking for some help finding all clusters of chars in the string in C++. The exact task is:
Given the following “2D string” (in C++ expression):
string text =
"#################aa##a###c######\n" +
"####bbbbaaaabbbbbaaaaa###ccc##cc\n" +
"#o##bbbbaaaabbbbbaaaaa###c#c##cc\n" +
"#oo#bbbbaeeabbbbbbbbaa##cc#ccccc\n" +
"#o##bbbbaeeabbbbbaaaaaa#cc#####c\n" +
"#o##bbbbaaaabbbbbaaaaaa#cc#####c\n";
Write a program computing the area of each contiguous region of the same >symbols. Two equal symbols belong to the same area if they are neighbors either >in a row or in a column. Don’t count the newline (\n) symbols, they are just to >form the 2D string.
The main function should be recursive.
Hint: use an extra 2D array to mark each symbol in the 2D string if it is >already counted or not. Scan the array row-wise until a not counted yet symbol >is found. Then, run the recursive area-computing function starting from this >symbol. Continue until all symbols are marked as counted.
The program output should look (more or less) like:
Region of symbols #, area …
Region of symbols a, area …
Region of symbols #, area …
Region of symbols c, area …
My current code looks like this:
#include <iostream>
#include <string>
using namespace std;
int cords (string str, int x, int y) {
int length, i, position, lines = 0, x_max, y_max;
char symbol;
length = str.length();
for (i = 0; i < length; i++) {
symbol = str[i];
if (symbol == '\n')
lines++;
}
length -= lines;
x_max = length / lines;
y_max = length / x_max;
position = x - 1 + (y - 1) * x_max + y - 1;
if (x <= x_max && y <= y_max)
return position;
}
int clusterMiner (char symbol, string str, int x, int y, int counter, int last) {
if (x > 32 || y > 6) {
return counter;
} else {
if (str[cords(str, x++, y)] == symbol) {
counter++;
return clusterMiner(symbol, str, x++, y, counter, x);
} else if (str[cords(str, 1, y++)] == symbol) {
return clusterMiner(symbol, str, 1, y++, counter, x);
}
}
}
int main () {
int length, lines, i, j, k, l, counter;
string text = // 32 elements per line
"#################aa##a###c######\n" // 32
"####bbbbaaaabbbbbaaaaa###ccc##cc\n" // 64
"#o##bbbbaaaabbbbbaaaaa###c#c##cc\n" // 96
"#oo#bbbbaeeabbbbbbbbaa##cc#ccccc\n" // 128
"#o##bbbbaeeabbbbbaaaaaa#cc#####c\n" // 160
"#o##bbbbaaaabbbbbaaaaaa#cc#####c\n"; // 192
counter = clusterMiner('#', text, 1, 1, 0, 0);
cout << counter;
return 0;
}
Cords function is just for easier interaction with the two dimensions of the string.
I'm not sure what to do next. Right now the program counts only some of the symbols as it stops at the first different one ignoring these which are connected to further nodes.
Thanks!
First, do not calculate x_max and y_max all the time newly, just do it once and store it in a variable. Then, you will have to iterate over the whole field:
char get(int x, int y)
{
// + 1: the newline!!!
return field[x + y * (x_max + 1)];
}
void countAll()
{
calculateMaxima();
// created your visited array now
for(unsigned int y = 0; y <= y_max; ++y)
{
for(int x = 0; x <= x_max; ++x)
{
if(!visited[x, y])
{
count = 0;
search(get(x, y), x, y);
// output count here...
}
}
}
}
Each time we hit a character not yet visited, i. e. a new one, we start a new search. For each search, we have to consider four neighbours for each current position {x, y}:{x +/- 1, y} and {x, y +/- (x_max + 1} (apart from the positions at the edges, which have less). So your search might look like this:
void visit(char symbol, int x, int y)
{
if(!visited[x][y] && get(x, y) == symbol)
{
++count;
++visited[x][y] = true;
}
search(symbol, x, y);
}
void search(char symbol, int x, int y)
{
if(x > 0)
visit(x - 1, y);
if(x < max_x)
visit(x + 1, y);
if(y > 0)
visit(x, y - 1);
if(y < max_y)
visit(x, y + 1);
}
For now, I am assuming count, visited and x/y_max being some global variables. Cleaner, as we are C++, would be writing a separate class for this purpose:
class ClusterMiner
{
unsigned int count;
std::string field;
// ...
void visit(char symbol, int x, int y);
void search(char symbol, int x, int y);
// ...
public:
void countAll();
};
Code is untested and incomplete, it shall only give you the necessary hints to find your way...
Side note: If you have unconnected regions of the same character, these will be detected as such. If this is not desired, you might sum up the results e. g. in a std::map<char, unsigned int> and iterate over this one after you finished counting...
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
So I was practising for an upcoming programming contest on algorithms, and I stumbled upon a problem from the previous year.
I pretty much solved it(in C++), but i was getting some timeouts, so i took a look at the official solution and it was written in Dlang.
I then tried to imitate what the official answer did in D, but I was still getting timeouts( > 4 seconds on a single input). Afaik, C++ is supposed to be faster than D, but D solves the same input in a split second and C++ takes more than 5 seconds for it
Here is the D answer code
import std.stdio;
import std.algorithm;
struct edge {
int src, des, w, o;
int opCmp (ref const edge e) const {
if(w != e.w) return w - e.w;
else return o - e.o;
}
};
const int MAXN = 100004, MAXM = 200004;
int N, M, D, ee, weight, days;
int[MAXN] ds;
edge[] edges;
void init() {
for(int i=1;i<=N;i++) ds[i] = i;
}
int find(int x) {
return ds[x] = (x == ds[x] ? x: find(ds[x]));
}
bool connected(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
int xr = find(x), yr = find(y);
if(xr ^ yr) {
ds[xr] = yr;
return 1;
}
return 0;
}
void main() {
scanf("%d%d%d", &N, &M, &D);
for(int i=1, a, b, c;i<=M;i++) {
scanf("%d%d%d", &a, &b, &c);
if(i < N)
edges ~= edge(a, b, c, 0);
else
edges ~= edge(a, b, c, 1);
}
edges.sort();
init();
int i, maxe=0;
for(i=0;i<edges.length;i++) {
auto e = edges[i];
if(merge(e.src, e.des)) {
if(e.o)
days ++;
}
}
printf("%d", days);
}
And then here is what I wrote in C++ as the answer code
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
struct Edge{
long long source, end, weight, old;
Edge(long long _s, long long _e, long long _w, long long _o):source(_s), end(_e), weight(_w), old(_o){}
};
int parents[100004];
vector<Edge>edges;
bool inc(Edge a, Edge b)
{
if(a.weight == b.weight)return a.old > b.old;
return a.weight < b.weight;
}
long long find(long long node)
{
if(parents[node] == node)return node;
else return find(parents[node]);
}
void init(long long M)
{
for(long long i = 0; i < M; ++i)parents[i] = i;
}
bool connect(long long x, long long y)
{
long long fx = find(x);
long long fy = find(y);
if(fx == fy)return false;
parents[fx] = fy;
return true;
}
long long noOfDays()
{
long long days = 0;
for(auto edge : edges){
if(connect(edge.source, edge.end)){
if(!edge.old)++days;
}
}
return days;
}
int main()
{
ios::sync_with_stdio(false);
long long N, M , D;
cin >> N >> M >> D;
N--;
for(long long i = 0; i < M; ++i){
long long a,b,c;
cin >> a >> b >> c;
if(i < N){
edges.push_back(Edge(a,b,c,1));
}else{
edges.push_back(Edge(a,b,c,0));
}
}
sort(edges.begin(), edges.end(), inc);
init(N+2);
cout << noOfDays() << endl;
}
The input which takes more than 5 seconds on C++, and a split second on D can be found here "http://ddl3.data.hu/get/356808/10699419/s4.24.in"
Here is the question I was actually trying to solve "https://dmoj.ca/problem/ccc17s4"(I am only doing the 11 point part).
Is there any way I can make my C++ code as fast as the D code? and why exactly isn't my C++ code running as fast as the D code?
EDIT: For all clarifications, g++ was used for the C++ without any optimizations, and 'dmd' for the Dlang, without any optimizations either
find() seems to be heavily used and they are very different in D and C++ implementations:
int find(int x) {
return ds[x] = (x == ds[x] ? x: find(ds[x]));
}
vs:
long long find(long long node)
{
if(parents[node] == node)return node;
else return find(parents[node]);
}
find() in D modifies array (looks like some kind of dynamic programming, were you cash previous result) while in C++ you always do full lookup. You should compare apples to apples, especially this code could be written exactly the same way in C++.
Out of curiosity, I tried running OPs code, and also the version below, which I created by minimally tweaking the 'D' code so that it would compile under C++. OPs C++ version took around 12 seconds to run. The version below took around 0.25 seconds to run.
My conclusion, in answer to the question is that the difference in run time seen by the OP is likely due to differences in implementation as described in some of the other answers, as opposed to poor performance of C++.
#include <cstdio>
#include <vector>
#include <algorithm>
struct edge {
edge(int src, int des, int w, int o) : src(src), des(des), w(w), o(o) {}
int src, des, w, o;
int opCmp(const edge& e) const {
if (w != e.w) return w - e.w;
else return o - e.o;
}
};
const int MAXN = 100004, MAXM = 200004;
int N, M, D, ee, weight, days;
int ds[MAXN];
std::vector<edge> edges;
void init() {
for (int i = 1; i <= N; i++) ds[i] = i;
}
int find(int x) {
return ds[x] = (x == ds[x] ? x : find(ds[x]));
}
bool connected(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
int xr = find(x), yr = find(y);
if (xr ^ yr) {
ds[xr] = yr;
return 1;
}
return 0;
}
void main() {
std::scanf("%d%d%d", &N, &M, &D);
for (int i = 1, a, b, c; i <= M; i++) {
scanf("%d%d%d", &a, &b, &c);
if (i < N)
edges.push_back(edge(a, b, c, 0));
else
edges.push_back(edge(a, b, c, 1));
}
std::sort(edges.begin(), edges.end(), [](const edge& lhs, const edge& rhs) { return lhs.opCmp(rhs) < 0; });
init();
int i, maxe = 0;
for (i = 0; i<edges.size(); i++) {
auto e = edges[i];
if (merge(e.src, e.des)) {
if (e.o)
days++;
}
}
printf("%d", days);
}
One possible contributor to the slow performance of the C++ version is the 'inc' function. It receives 2 'Edge' structs by value, which in C++ will mean copying of the structs for every comparison during the sort call towards the end of main().
Try changing the signature of 'inc' to accept 'const Edge&' instead of 'Edge'. This will cause the struct values to be passed by reference and so avoid the extra copying.
Also, if you run a profiler you should be able to find where the majority of the time is being spent. This is the 'right' way to approach optimization: measure to find where you have a performance bottleneck, address the bottleneck and measure again to confirm you have indeed improved the performance.
Could anybody help me to understand what is wrong with my A* search implementation?
I implemented a basic A* search based on this incredible helpful site: http://www.redblobgames.com/pathfinding/a-star/implementation.html#cplusplus
(big thanks here to the author, Amit!).
I am using a grid, eight directions and the Octile distance as heuristic.
Unfortunately my path, going from start(0,h/2) to end(w-1,h/2), is not the expected straight line but looks like this:
My code (should be compilable as is but requires OpenCv):
struct PriorityQueue
{
typedef pair<int, cv::Point> PQElement;
struct SortPairPoints
{
bool operator()(const PQElement & l, const PQElement & r)
{
return (l.first > r.first);
}
};
priority_queue<PQElement, vector<PQElement>, SortPairPoints> elements;
inline bool empty() { return elements.empty(); }
inline void put(int priority,cv::Point item)
{
elements.emplace(priority, item);
}
inline cv::Point get()
{
cv::Point best_item = elements.top().second;
elements.pop();
return best_item;
}
};
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
namespace std
{
template <>
struct hash<cv::Point>
{
size_t operator()(const cv::Point & p) const
{
size_t seed = 0;
hash_combine(seed,p.x);
hash_combine(seed,p.y);
return seed;
}
};
}
int heuristic(cv::Point next, cv::Point goal)
{
// int D = 1;
// int dx = abs(next.x - goal.x);
// int dy = abs(next.y - goal.y);
// return D * (dx + dy);
// return sqrt(dx * dx + dy * dy);
// int D = 1;
// int D2 = 1;
int D = 1;
int D2 = sqrt(2);
int dx = abs(next.x - goal.x);
int dy = abs(next.y - goal.y);
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy);
}
int w = 250;
int h = 250;
std::vector<cv::Point> pathDirs({cv::Point(1, 0),cv::Point(0, -1),cv::Point(0, 1),cv::Point(-1, 0), cv::Point(1, 1), cv::Point(-1, 1),cv::Point(-1, -1),cv::Point(1, -1)});
//std::vector<cv::Point> pathDirs({cv::Point(1, 0),cv::Point(0, -1),cv::Point(-1, 0),cv::Point(0, 1)});
cv::Rect scenebox(0,0,w,h);
void search(
cv::Mat map,
cv::Point start,
cv::Point goal,
unordered_map<cv::Point, cv::Point>& came_from,
unordered_map<cv::Point, int>& cost_so_far
)
{
PriorityQueue frontier;
frontier.put(0,start);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty()) {
auto current = frontier.get();
if (current == goal) {
break;
}
for (auto dir : pathDirs)
{
cv::Point next(current.x + dir.x, current.y + dir.y);
if (scenebox.contains(next) && (map.at<uchar>(next) == 255))
{
int new_cost = cost_so_far[current] + 1;
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
int priority = new_cost + heuristic(next, goal);
frontier.put(priority,next);
came_from[next] = current;
}
}
}
}
}
vector<cv::Point> reconstruct_path(
cv::Point start,
cv::Point goal,
unordered_map<cv::Point, cv::Point>& came_from
)
{
vector<cv::Point> path;
cv::Point current = goal;
path.push_back(current);
while (current != start) {
current = came_from[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
return path;
}
int main(int argc, const char * argv[])
{
cv::Mat tracemap = cv::Mat(w,h, CV_8UC1, cvScalar(255) );
cv::Point start(0,h/2);
cv::Point end(w-1,h/2);
// cv::Point start(0,0);
// cv::Point end(w-1,h-1);
// cv::line(tracemap,
// cv::Point (75,125),
// cv::Point (125,75),
// cvScalar(150),50);
unordered_map<cv::Point, cv::Point> came_from;
unordered_map<cv::Point, int> cost_so_far;
search(tracemap, start, end, came_from, cost_so_far);
vector<cv::Point> path = reconstruct_path(start, end, came_from);
for(int i = 0; i < path.size(); i++)
{
tracemap.at<uchar>(path[i]) = 0;
}
imshow("tracemap", tracemap);
cv::waitKey();
return 0;
}
Any insights or tips on how to get to the root of the problem are highly appreciated!
UPDATE: With Amit's suggestions I get the following paths now:
FOLLOW-UP (highly related that is why I am adding it here and don't open a new post):
If I use only four directions with a Manhattan distance as heuristic and a movement cost of 1 for all four steps, I get a jittery diagonal. Of course the algorithm has to take 'stairs" like this, but I would still expect something more straight - am I missing something obvious?
Your movement cost for diagonals is the same as for orthogonal steps.
A path going southeast, southeast, northeast, northeast is just as short as a path going east, east, east, east. Both have cost 4.
When there are multiple shortest paths, A* is giving you one of them, but it's not the one you want.
If you set diagonals to have a higher movement cost (sqrt(2) is what your heuristic states), then A* would prefer east, east, east, east. Change
int new_cost = cost_so_far[current] + 1;
to either use 1 or sqrt(2) depending on whether it's an orthogonal or diagonal step. You'll also need to make the costs into floats/doubles instead of ints, and make the priority queue do the same. (Alternatively, if you want to keep using ints, some people will use 14 and 10 as the costs, and scale the heuristic up to use 14 and 10 for D2 and D.)