I am trying to write an A* pathfinder to learn C++ and am struggling with a specific bug.
These are the features/assumptions of the program:
The path contains "walls" which cannot be passed
All paths cost the same value (1)
Diagonal movement is allowed
Only one goal
Here is my code:
#include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
#include <cmath>
#include <set>
using Graph = std::vector<std::vector<int>>;
using Position = std::tuple<int, int>;
class Node;
using Nodes = std::vector<Node>;
constexpr int WALL_BLOCK = 1;
constexpr int USER_BLOCK = 3;
constexpr int GOAL_BLOCK = 2;
class Node {
private:
int row;
int column;
Node* parent = nullptr;
int f = 0;
int g = 0;
int h = 0;
public:
Node() : row(-1), column(-1) {}
Node(int row, int column) : row(row), column(column) {}
Node(int row, int column, Node *parent) : row(row), column(column) {
if (this->parent != nullptr && *this->parent == *this) {
throw "Node cannot be parented to itself";
}
this->parent = parent;
}
~Node() {}
Node* get_parent() const { return this->parent; }
int get_f() const { return this->f; }
int get_g() const { return this->g; }
int get_h() const { return this->h; }
Position get_position() const { return std::make_tuple(row, column); }
void set_f(int f) { this->f = f; }
void set_g(int g) { this->g = g; }
void set_h(int h) { this->h = h; }
bool operator==(Node const &node) const {
return this->row == node.row && this->column == node.column;
}
bool operator<(Node const &node) const {
return this->row < node.row && this->column < node.column;
}
friend std::ostream& operator<<(std::ostream& os, Node const &node);
};
std::ostream& operator<<(std::ostream& os, Node const &node) {
auto row = node.row;
auto column = node.column;
os << "Node(("
<< row << ", "
<< column << "), "
<< node.get_g() << ", "
<< node.get_h() << ", "
;
Node* parent = node.get_parent();
if (parent != nullptr) {
os << parent;
} else {
os << "nullptr";
}
os << ")";
return os;
}
inline bool is_walkable(Node const &node, Graph const &graph) {
int column;
int row;
std::tie(row, column) = node.get_position();
return graph[row][column] != WALL_BLOCK;
}
Position get_first_index(Graph const &graph, int block);
Position get_end(Graph const &graph) {
return get_first_index(graph, GOAL_BLOCK);
}
Position get_first_index(Graph const &graph, int block) {
for (int row = 0, max = graph.size(); row < max; ++row) {
auto line = graph[row];
auto found = std::find(line.begin(), line.end(), block);
if (found != line.end()) {
return std::make_tuple(row, found - line.begin());
}
}
return {-1, -1};
}
inline int get_h(Node const &node, Node const &reference) {
auto node_position = node.get_position();
auto reference_position = reference.get_position();
auto node_position_row = std::get<0>(node_position);
auto node_position_column = std::get<1>(node_position);
auto reference_position_row = std::get<0>(reference_position);
auto reference_position_column = std::get<1>(reference_position);
return (
std::pow((node_position_row - reference_position_row), 2) +
std::pow((node_position_column - reference_position_column), 2)
);
}
Position get_start(Graph const &graph) {
return get_first_index(graph, USER_BLOCK);
}
Nodes get_children(Node &node, Graph const &graph) {
Nodes children;
int row;
int column;
std::tie(row, column) = node.get_position();
for (int row_offset = -1; row_offset < 2; ++row_offset) {
for (int column_offset = -1; column_offset < 2; ++column_offset) {
if (row_offset == 0 and column_offset == 0) {
// (0, 0) will always be `node`. We can't let `node` be a child
// of itself so we have to `continue` here
//
continue;
}
Graph::size_type node_row = row + row_offset;
Graph::size_type node_column = column + column_offset;
if (node_row >= graph.size()) {
continue;
}
if (node_column >= graph[node_row].size()) {
continue;
}
children.push_back({
static_cast<int>(node_row),
static_cast<int>(node_column),
&node
});
}
}
return children;
}
Nodes trace(Node const &node) {
Node* parent = node.get_parent();
Nodes path;
std::set<Node> seen;
while (parent != nullptr) {
auto parent_node = *parent;
if (std::find(seen.begin(), seen.end(), parent_node) != seen.end()) {
// If this happens, `parent` is already in `path`. To avoid
// a cyclic loop from happening, we will break out, instead.
//
break;
}
seen.insert(parent_node);
path.push_back(parent_node);
parent = parent->get_parent();
}
return path;
}
Nodes a_star(Graph const &graph, Node const &user, Node const &goal) {
Nodes open_list {user};
Nodes closed_list;
while (open_list.size() != 0) {
Node current_node = open_list[0];
unsigned int current_index = 0;
for (int index = 0, max = open_list.size(); index < max; ++index) {
auto node = open_list[index];
if (node.get_f() < current_node.get_f()) {
current_node = node;
current_index = index;
}
}
if (current_node == goal) {
auto path = trace(current_node);
std::reverse(path.begin(), path.end());
return path;
}
open_list.erase(open_list.begin() + current_index);
closed_list.push_back(current_node);
auto children = get_children(current_node, graph);
for (auto &child : children) {
if (std::find(closed_list.begin(), closed_list.end(), child) != closed_list.end()) {
continue;
}
if (!is_walkable(child, graph)) {
continue;
}
child.set_g(child.get_parent()->get_g() + 1);
child.set_h(get_h(child, goal));
child.set_f(child.get_g() + child.get_h());
bool add = true;
for (auto const &open : open_list) {
if (child == open && child.get_g() > open.get_g()) {
add = false;
break;
}
}
if (add) {
open_list.push_back(child);
}
}
}
return {};
}
int main() {
Graph graph = {
{0, 0, 0, 0, 1, 0, 0},
{0, 3, 0, 0, 1, 0, 2},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0},
};
int start_row = 0;
int start_column = 0;
std::tie(start_row, start_column) = get_start(graph);
int end_row = 0;
int end_column = 0;
std::tie(end_row, end_column) = get_end(graph);
auto user = Node(start_row, start_column);
auto goal = Node(end_row, end_column);
std::cout << user << std::endl;
std::cout << goal << std::endl;
auto nodes = a_star(graph, user, goal);
std::cout << "Path: [";
for (auto const &node : nodes) {
std::cout << "("
<< std::get<0>(node.get_position())
<< ", "
<< std::get<1>(node.get_position())
<< "), ";
}
std::cout << "]" << std::endl;
return 0;
}
When I run this on my computer, I get this output:
Node((1, 1), 0, 0, nullptr)
Node((1, 6), 0, 0, nullptr)
Path: [(1, 6), ]
Path: should have returned a full list starting from (1, 1) to the goal, (1, 6) But instead of [(1, 1), (1, 2), (2, 3), (3, 3), (4, 4), (3, 5), (2, 6), (1, 6), ]. we got [(1, 6), ]. I think the reason for this is because current_node contains itself as a parent. So trace breaks early to avoid a cyclic loop. And that's why there's only one in the output. It's weird that any Node would have itself as a parent because the constructor throws an exception if that happens.
In fact on my machine, every parent of every single node is the same address, 0x7fffffff9140, which shouldn't be happening.
My only guess to this is that current_node is being initialized in the while loop so maybe even though the Node position / f / g / h are changing, the pointer in memory never does. So all the nodes end up getting the same parent that way.
That may or may not be the issue but either way, I'm not sure how to fix it. Any advice would be helpful. Thank you!
This constructor body is totally messed up, you are reading from member this->parent which is always initialized to nullptr (by the brace-or-equal-initializer).
Node(int row, int column, Node *parent) : row(row), column(column)
{
if (this->parent != nullptr && *this->parent == *this) {
throw "Node cannot be parented to itself";
}
this->parent = parent;
}
Second major problem, this is passing a reference to an object current_node whose lifetime is insufficient:
auto children = get_children(current_node, graph);
By the time you read any of the "children" out of the list, the node pointed to by their parent member has already been destroyed.
The copy in closed_list would survive, but that can't be used to store a parent pointer either, because of the iterator invalidation rules for std::vector. If you change closed_list to std::list<Node> then the element addresses would be stable.
But then when a_star returns all the objects pointed to by instance parent pointers will die anyway. trace(current_node) results in a collection of nodes whose parent members point outside the collection.
Your best bet is probably to preallocate enough Node objects for the whole graph instance, use an index instead of a pointer as parent, and update the parent member instead of setting it during construction. Index values, unlike pointers, remain meaningful no matter how many times a data structure is copied.
(Also, string literals make bad exception objects)
Related
Feel free to point out my mistakes, I do not claim to be a good C++ programmer or even good w/ Data structures. I'm just looking to improve and learn upon my example.
This approach is not my own but the code is. I learned a concept and attempted to code it in C++.
The point here is to learn to build this without using libraries such as std::priority_queue. Thanks again!
#include <iostream>
#include <memory>
#include <vector>
#include <unordered_map>
#include <algorithm>
// directions of each node
enum
{
IS_LEFT = 1,
IS_RIGHT = 2
};
// (ADT) Priority Queue which uses a (DS) heap
using std::vector;
// Makes removal of any node anywhere within the heap O(log(n))
using std::unordered_map;
class BinaryStack
{
private:
// tracks the last node in heap
size_t last_index { 0 };
// the heap
vector<int> heap{ };
// the hashmap to track where any given node is at (required for node removal within a heap)
unordered_map<int, vector<size_t>> hashmap{ };
// swap any two values from two given vectors
void swapVectorValue(vector<size_t> &vec1, size_t val1, vector<size_t> &vec2, size_t val2)
{
vec1.erase(std::remove(vec1.begin(), vec1.end(), val1), vec1.end());
vec2.erase(std::remove(vec2.begin(), vec2.end(), val2), vec2.end());
vec1.push_back(val2);
vec2.push_back(val1);
}
// adds a value to hashmap | the keys are the node values & the values are their respective indexes
void addToHashMap(int val, size_t last_index)
{
auto search = hashmap.find(val);
if (search != hashmap.end())
hashmap[val].push_back(last_index);
else
hashmap[val] = vector<size_t> { last_index };
}
public:
// insert a node into the heap
void insert(int val)
{
heap.push_back(val);
// point last node to end of array
size_t last_index = heap.size()-1;
addToHashMap(val, last_index);
// only one node so lets just stop here
if (heap.size() == 1) return;
// determines if node is on left or right
short node_direction = (heap.size() % 2 == 0) ? IS_LEFT : IS_RIGHT;
size_t parent_index = (last_index - node_direction) / 2;
// keep going until bubbles to top
while (heap[parent_index] > heap[last_index])
{
//swaps indexes of swapped nodes
swapVectorValue(hashmap[heap[parent_index]], parent_index, hashmap[heap[last_index]], last_index);
// update value of parent and new node
int temp { heap[parent_index] };
heap[parent_index] = heap[last_index];
heap[last_index] = temp;
// leave if at top
if (parent_index == 0) break;
// find new parent
last_index = parent_index;
node_direction = (last_index % 2 == 0) ? IS_RIGHT : IS_LEFT;
parent_index = (last_index - node_direction) / 2;
}
}
// remove any node
void remove(int val)
{
size_t parent_index{ };
auto search = hashmap.find(val);
if (search != hashmap.end())
{
parent_index = hashmap[val].back();
hashmap[val].pop_back();
if (hashmap[val].size() == 0) hashmap.erase(val);
} else return;
last_index = heap.size()-1;
heap[parent_index] = heap[last_index];
heap.pop_back();
size_t left_child = (parent_index * 2) + 1;
size_t right_child = (parent_index * 2) + 2;
size_t smallest_child{ };
if (left_child > last_index) return;
if (left_child == last_index && heap[left_child] > heap[parent_index]) return;
if (right_child > last_index)
smallest_child = left_child;
else
smallest_child = (heap[left_child] < heap[right_child]) ? left_child : right_child;
while (heap[parent_index] > heap[smallest_child])
{
swapVectorValue(hashmap[heap[parent_index]], parent_index, hashmap[heap[smallest_child]], smallest_child);
// update value of parent and new node
int temp { heap[parent_index] };
heap[parent_index] = heap[smallest_child];
heap[smallest_child] = temp;
parent_index = smallest_child;
left_child = (parent_index * 2) + 1;
right_child = (parent_index * 2) + 2;
if (left_child > last_index) return;
if (left_child == last_index && heap[left_child] > heap[parent_index]) return;
if (right_child > last_index)
smallest_child = left_child;
else
smallest_child = (heap[left_child] < heap[right_child]) ? left_child : right_child;
}
}
// removes only the top node
int poll(int val)
{
// NOT PROGRAMMED YET
return 1;
}
void display()
{
// get parent node
for (const auto &n: heap)
{
std::cout << n << ' ';
}
std::cout << std::endl;
}
void hashmapDisplay()
{
for (const auto& r: hashmap)
{
std::cout << "value: " << r.first << " at indexes: ";
for (const auto& s: r.second)
{
std::cout << s << ' ';
}
std::cout << std::endl;
}
}
};
int main()
{
std::unique_ptr<BinaryStack> bstack(new BinaryStack());
bstack->insert(95);
bstack->insert(9);
bstack->insert(9);
bstack->insert(12);
bstack->insert(14);
bstack->insert(9);
bstack->insert(93);
bstack->remove(93);
bstack->remove(9);
// proves order of heap
bstack->display();
// just to prove the indexes are being tracked correctly
bstack->hashmapDisplay();
return 0;
}
I have been programming for a little while, but I am relativly new to c++. I am trying to implement the A* algorithm and have managed to produce the code below. The implementation producces the expected result, the shortest path from point A to B in a 2D grid, but I suspect i leave behind some memory leaks.
I tested this by overloading the new and delete operators to keep track of the amount of bytes allocated on the heap and it showed that a lot of memeory never got released. However I also tested the number of nodes that never got deleted but it showed that there were that all allocated nodes also got their destructor called. Note in the code that i only call new on Node, hence my confunsion. I have much confusion about this and would be happy for an explenation.
I have tried using smart pointers, but enden up with circular refrences which were difficult to resolve.
This is also my first post on Stack Overflow so please feel free to point how I can improve my questions. Thanks in advance.
#include<vector>
#include<array>
#include<cmath>
int mynodes = 0;
int memory_left = 0;
void* operator new(size_t size)
{
memory_left += size;
return malloc(size);
}
void operator delete(void* memory, size_t size)
{
memory_left -= size;
free(memory);
}
struct Node{
std::array<int, 2> position;
Node* parent = nullptr;
double h, g, f;
Node(const std::array<int, 2>& pos)
:position(pos){mynodes++;}
~Node(){ mynodes--; }
};
std::vector<std::array<int, 2>> find_children(const std::vector<std::vector<int>>& grid, const std::array<int, 2>& pos){
std::vector<std::array<int, 2>> children;
children.reserve(8);
for(int t = -1; t < 2; t++){
for(int q = -1; q < 2; q++){
if(t != 0 || q != 0){
if(abs(t) == abs(q)) continue;
std::array<int, 2> cur_pos = {pos[0]+q, pos[1]+t};
if(cur_pos[0] >= 0 && cur_pos[0] < grid[0].size() && cur_pos[1] >= 0 && cur_pos[1] < grid.size())
{
if(grid[cur_pos[1]][cur_pos[0]] == 0)
{
children.push_back(cur_pos);
}
}
}
}
}
return children;
}
bool search_vect(const std::vector<Node*>& set, const std::array<int, 2>& pos)
{
for(Node* node : set)
{
if(node->position[0] == pos[0] && node->position[1] == pos[1]) return true;
}
return false;
}
void releaseNodes(std::vector<Node*>& set)
{
for(auto& node : set)
{
delete node;
}
set.clear();
}
std::vector<std::array<int, 2>> find_path(const std::vector<std::vector<int>>& grid, std::array<int, 2> start, std::array<int, 2> end){
Node* cur_node = new Node(start);
std::vector<Node*> open_vect;
std::vector<Node*> closed_vect;
open_vect.push_back(cur_node);
while(cur_node->position != end){
double lowest_f = INFINITY;
size_t idx = 0;
for(size_t i = 0; i < open_vect.size(); i++)
{
if(open_vect[i]->f < lowest_f)
{
cur_node = open_vect[i];
lowest_f = cur_node->f;
idx = i;
}
}
open_vect.erase(open_vect.begin() + idx);
std::vector<std::array<int, 2>> children = find_children(grid, cur_node->position);
closed_vect.push_back(cur_node);
for(const auto& child_pos : children){
// if(closed_vect.find(child_pos) != closed_vect.end() || open_vect.find(child_pos) != open_vect.end()) continue;
if(search_vect(closed_vect, child_pos) || search_vect(open_vect, child_pos))
{
continue;
}
Node* new_node = new Node(child_pos);
new_node->g = cur_node->g + 1;
new_node->h = abs(end[0] - child_pos[0]) + abs(end[1] - child_pos[1]);
new_node->f = new_node->g + new_node->h;
new_node->parent = cur_node;
// double h = sqrt(pow(end[0] - child_pos[0], 2) + pow(end[1] - child_pos[1], 2));
open_vect.push_back(new_node);
}
}
std::vector<std::array<int, 2>> path;
while(cur_node != nullptr){
path.push_back(cur_node->position);
cur_node = cur_node->parent;
}
releaseNodes(open_vect);
releaseNodes(closed_vect);
return path;
}
int main()
{
std::vector<std::vector<int>> grid( 100 , std::vector<int> (100, 0));
{
auto path = find_path(grid, {1, 1}, {98, 98});
}
}
Here the minimal/easy way of getting rid of any explicit new/delete:
#include<vector>
#include<array>
#include<cmath>
struct Node{
std::array<int, 2> position;
Node* parent = nullptr;
double h{0.};
double g{0.};
double f{0.};
Node(const std::array<int, 2>& pos)
: position(pos) {}
};
std::vector<std::array<int, 2>> find_children(const std::vector<std::vector<int>>& grid, const std::array<int, 2>& pos){
std::vector<std::array<int, 2>> children;
children.reserve(8);
for(int t = -1; t < 2; t++){
for(int q = -1; q < 2; q++){
if(t != 0 || q != 0){
if(abs(t) == abs(q)) continue;
std::array<int, 2> cur_pos = {pos[0]+q, pos[1]+t};
if(cur_pos[0] >= 0 && cur_pos[0] < grid[0].size() && cur_pos[1] >= 0 && cur_pos[1] < grid.size())
{
if(grid[cur_pos[1]][cur_pos[0]] == 0)
{
children.push_back(cur_pos);
}
}
}
}
}
return children;
}
bool search_vect(const std::vector<Node*>& set, const std::array<int, 2>& pos)
{
for(Node* node : set)
{
if(node->position[0] == pos[0] && node->position[1] == pos[1]) return true;
}
return false;
}
std::vector<std::array<int, 2>> find_path(const std::vector<std::vector<int>>& grid, std::array<int, 2> start, std::array<int, 2> end){
std::vector<Node> nodes{};
nodes.emplace_back(start);
Node* cur_node = &nodes.back();
std::vector<Node*> open_vect;
std::vector<Node*> closed_vect;
open_vect.push_back(cur_node);
while(cur_node->position != end){
double lowest_f = INFINITY;
size_t idx = 0;
for(size_t i = 0; i < open_vect.size(); i++)
{
if(open_vect[i]->f < lowest_f)
{
cur_node = open_vect[i];
lowest_f = cur_node->f;
idx = i;
}
}
open_vect.erase(open_vect.begin() + idx);
std::vector<std::array<int, 2>> children = find_children(grid, cur_node->position);
closed_vect.push_back(cur_node);
for(const auto& child_pos : children){
// if(closed_vect.find(child_pos) != closed_vect.end() || open_vect.find(child_pos) != open_vect.end()) continue;
if(search_vect(closed_vect, child_pos) || search_vect(open_vect, child_pos))
{
continue;
}
nodes.emplace_back(child_pos);
Node* new_node = &nodes.back();
new_node->g = cur_node->g + 1;
new_node->h = abs(end[0] - child_pos[0]) + abs(end[1] - child_pos[1]);
new_node->f = new_node->g + new_node->h;
new_node->parent = cur_node;
// double h = sqrt(pow(end[0] - child_pos[0], 2) + pow(end[1] - child_pos[1], 2));
open_vect.push_back(new_node);
}
}
std::vector<std::array<int, 2>> path;
while(cur_node != nullptr){
path.push_back(cur_node->position);
cur_node = cur_node->parent;
}
return path;
}
int main()
{
std::vector<std::vector<int>> grid( 100 , std::vector<int> (100, 0));
{
auto path = find_path(grid, {1, 1}, {98, 98});
}
}
This is not perfect, but it easily demonstrates how easy it is to not cause any potential memory leaks. Instead of replacing the vectors of pointers with vectors of nodes, I just added an additional vector of nodes (the owner). Now the pointers are non-owning and nothing weird should happen anymore.
I have a list of {a,b} and i need all possible combinatations where say n=3.
so:
[a,b,a],
[b,a,b]
[a,a,a]
[b,b,b]
etc.
Is there a name of such a problem
My current solution just uses random sampling and is very inefficient:
void set_generator(const vector<int>& vec, int n){
map<string, vector<int>> imap;
int rcount = 0;
while(1){
string ms = "";
vector<int> mset;
for(int i=0; i<n; i++){
int sampled_int = vec[rand() % vec.size()];
ms += std::to_string(sampled_int);
mset.emplace_back(sampled_int);
}
if(rcount > 100)
break;
if(imap.count(ms)){
rcount += 1;
//cout << "*" << endl;
continue;
}
rcount = 0;
imap[ms] = mset;
cout << ms << endl;
}
}
set_generator({1,2},3);
Let us call b the size of the input vector.
The problem consists in generating all numbers from 0 to b^n - 1, in base b.
A simple solution increments the elements of an array one by one, each from 0 to b-1.
This is performed by the function increment in the code hereafter.
Output:
111
211
121
221
112
212
122
222
The code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
void set_generator_op (const std::vector<int>& vec, int n){
std::map<std::string, std::vector<int>> imap;
int rcount = 0;
while(1){
std::string ms = "";
std::vector<int> mset;
for(int i=0; i<n; i++){
int sampled_int = vec[rand() % vec.size()];
ms += std::to_string(sampled_int);
mset.emplace_back(sampled_int);
}
if(rcount > 100)
break;
if(imap.count(ms)){
rcount += 1;
//cout << "*" << endl;
continue;
}
rcount = 0;
imap[ms] = mset;
std::cout << ms << "\n";
}
}
// incrementation of a array of int, in base "base"
// return false if max is already attained
bool increment (std::vector<int>& cpt, int base) {
int n = cpt.size();
for (int i = 0; i < n; ++i) {
cpt[i]++;
if (cpt[i] != base) {
return true;
}
cpt[i] = 0;
}
return false;
}
void set_generator_new (const std::vector<int>& vec, int n){
int base = vec.size();
std::vector<int> cpt (n, 0);
while (true) {
std::string permut = "";
for (auto &k: cpt) {
permut += std::to_string (vec[k]);
}
std::cout << permut << "\n";
if (!increment(cpt, base)) return;
}
}
int main() {
set_generator_op ({1,2},3);
std::cout << "\n";
set_generator_new ({1,2},3);
}
Following advices of Jarod42, I have
suppressed the useless conversion to a string
used a more elegant do ... while instead of the while true
inversed the iterators for printing the result
Moreover, I have created a templated version of the program.
New output:
111
112
121
122
211
212
221
222
aaa
aab
aba
abb
baa
bab
bba
bbb
And the new code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
// incrementation of a array of int, in base "base"
// return false if max is already attained
bool increment (std::vector<int>& cpt, int base) {
int n = cpt.size();
for (int i = 0; i < n; ++i) {
cpt[i]++;
if (cpt[i] != base) {
return true;
}
cpt[i] = 0;
}
return false;
}
template <typename T>
void set_generator_new (const std::vector<T>& vec, int n){
int base = vec.size();
std::vector<int> cpt (n, 0);
do {
for (auto it = cpt.rbegin(); it != cpt.rend(); ++it) {
std::cout << vec[*it];
}
std::cout << "\n";
} while (increment(cpt, base));
}
int main() {
set_generator_new<int> ({1,2}, 3);
std::cout << "\n";
set_generator_new<char> ({'a','b'}, 3);
}
Besides the concrete answer for integer usage, I want to provide a generic way I needed during test case construction for scenarios with a wide spread of various parameter variations. Maybe it's helpful to you too, at least for similar scenarios.
#include <vector>
#include <memory>
class SingleParameterToVaryBase
{
public:
virtual bool varyNext() = 0;
virtual void reset() = 0;
};
template <typename _DataType, typename _ParamVariationContType>
class SingleParameterToVary : public SingleParameterToVaryBase
{
public:
SingleParameterToVary(
_DataType& param,
const _ParamVariationContType& valuesToVary) :
mParameter(param)
, mVariations(valuesToVary)
{
if (mVariations.empty())
throw std::logic_error("Empty variation container for parameter");
reset();
}
// Step to next parameter value, return false if end of value vector is reached
virtual bool varyNext() override
{
++mCurrentIt;
const bool finished = mCurrentIt == mVariations.cend();
if (finished)
{
return false;
}
else
{
mParameter = *mCurrentIt;
return true;
}
}
virtual void reset() override
{
mCurrentIt = mVariations.cbegin();
mParameter = *mCurrentIt;
}
private:
typedef typename _ParamVariationContType::const_iterator ConstIteratorType;
// Iterator to the actual values this parameter can yield
ConstIteratorType mCurrentIt;
_ParamVariationContType mVariations;
// Reference to the parameter itself
_DataType& mParameter;
};
class GenericParameterVariator
{
public:
GenericParameterVariator() : mFinished(false)
{
reset();
}
template <typename _ParameterType, typename _ParameterVariationsType>
void registerParameterToVary(
_ParameterType& param,
const _ParameterVariationsType& paramVariations)
{
mParametersToVary.push_back(
std::make_unique<SingleParameterToVary<_ParameterType, _ParameterVariationsType>>(
param, paramVariations));
}
const bool isFinished() const { return mFinished; }
void reset()
{
mFinished = false;
mNumTotalCombinationsVisited = 0;
for (const auto& upParameter : mParametersToVary)
upParameter->reset();
}
// Step into next state if possible
bool createNextParameterPermutation()
{
if (mFinished || mParametersToVary.empty())
return false;
auto itPToVary = mParametersToVary.begin();
while (itPToVary != mParametersToVary.end())
{
const auto& upParameter = *itPToVary;
// If we are the very first configuration at all, do not vary.
const bool variedSomething = mNumTotalCombinationsVisited == 0 ? true : upParameter->varyNext();
++mNumTotalCombinationsVisited;
if (!variedSomething)
{
// If we were not able to vary the last parameter in our list, we are finished.
if (std::next(itPToVary) == mParametersToVary.end())
{
mFinished = true;
return false;
}
++itPToVary;
continue;
}
else
{
if (itPToVary != mParametersToVary.begin())
{
// Reset all parameters before this one
auto itBackwd = itPToVary;
do
{
--itBackwd;
(*itBackwd)->reset();
} while (itBackwd != mParametersToVary.begin());
}
return true;
}
}
return true;
}
private:
// Linearized parameter set
std::vector<std::unique_ptr<SingleParameterToVaryBase>> mParametersToVary;
bool mFinished;
size_t mNumTotalCombinationsVisited;
};
Possible usage:
GenericParameterVariator paramVariator;
size_t param1;
int param2;
char param3;
paramVariator.registerParameterToVary(param1, std::vector<size_t>{ 1, 2 });
paramVariator.registerParameterToVary(param2, std::vector<int>{ -1, -2 });
paramVariator.registerParameterToVary(param3, std::vector<char>{ 'a', 'b' });
std::vector<std::tuple<size_t, int, char>> visitedCombinations;
while (paramVariator.createNextParameterPermutation())
visitedCombinations.push_back(std::make_tuple(param1, param2, param3));
Generates:
(1, -1, 'a')
(2, -1, 'a')
(1, -2, 'a')
(2, -2, 'a')
(1, -1, 'b')
(2, -1, 'b')
(1, -2, 'b')
(2, -2, 'b')
For sure, this can be further optimized/specialized. For instance you can simply add a hashing scheme and/or an avoid functor if you want to avoid effective repetitions. Also, since the parameters are held as references, one might consider to protect the generator from possible error-prone usage via deleting copy/assignement constructors and operators.
Time complexity is within the theoretical permutation complexity range.
I got this implementation for maximum matching off the net and is trying to give its input through main class. But I am getting zero for all the places in match. What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
using namespace std;
void add_edge(int u, int v);
void edmonds();
struct edge {
int v, nx;
};
const int MAXN = 1000, MAXE = 2000;
edge graph[MAXE];
int last[MAXN], match[MAXN], px[MAXN], base[MAXN], N, M, edges;
bool used[MAXN], blossom[MAXN], lused[MAXN];
int main ()
{
// return 0;
add_edge(1,4);
add_edge(1,5);
add_edge(1,6);
add_edge(2,5);
add_edge(2,7);
add_edge(3,4);
add_edge(4,1);
add_edge(4,3);
add_edge(5,1);
add_edge(5,2);
add_edge(6,1);
add_edge(7,2);
edmonds();
cout << match[0];
cout << match[1];
cout << match[2];
cout << match[3];
cout << match[4];
cout << match[5];
cout << match[6];
}
inline void add_edge(int u, int v) {
graph[edges] = (edge) {v, last[u]};
last[u] = edges++;
graph[edges] = (edge) {u, last[v]};
last[v] = edges++;
}
void mark_path(int v, int b, int children) {
while (base[v] != b) {
blossom[base[v]] = blossom[base[match[v]]] = true;
px[v] = children;
children = match[v];
v = px[match[v]];
}
}
int lca(int a, int b) {
memset(lused, 0, N);
while (1) {
lused[a = base[a]] = true;
if (match[a] == -1)
break;
a = px[match[a]];
}
while (1) {
b = base[b];
if (lused[b])
return b;
b = px[match[b]];
}
}
int find_path(int root) {
memset(used, 0, N);
memset(px, -1, sizeof(int) * N);
for (int i = 0; i < N; ++i)
base[i] = i;
used[root] = true;
queue<int> q;
q.push(root);
register int v, e, to, i;
while (!q.empty()) {
v = q.front(); q.pop();
for (e = last[v]; e >= 0; e = graph[e].nx) {
to = graph[e].v;
if (base[v] == base[to] || match[v] == to)
continue;
if (to == root || (match[to] != -1 && px[match[to]] != -1)) {
int curbase = lca(v, to);
memset(blossom, 0, N);
mark_path(v, curbase, to);
mark_path(to, curbase, v);
for (i = 0; i < N; ++i)
if (blossom[base[i]]) {
base[i] = curbase;
if (!used[i]) {
used[i] = true;
q.push(i);
}
}
} else if (px[to] == -1) {
px[to] = v;
if (match[to] == -1)
return to;
to = match[to];
used[to] = true;
q.push(to);
}
}
}
return -1;
}
void build_pre_matching() {
register int u, e, v;
for (u = 0; u < N; ++u)
if (match[u] == -1)
for (e = last[u]; e >= 0; e = graph[e].nx) {
v = graph[e].v;
if (match[v] == -1) {
match[u] = v;
match[v] = u;
break;
}
}
}
void edmonds() {
memset(match, 0xff, sizeof(int) * N);
build_pre_matching();
register int i, v, pv, ppv;
for (i = 0; i < N; ++i)
if (match[i] == -1) {
v = find_path(i);
while (v != -1) {
pv = px[v], ppv = match[pv];
match[v] = pv, match[pv] = v;
v = ppv;
}
}
}
You set elements of match in two locations: In build_pre_matching() and in edmonds(). In both of these cases, no change will happen if match[x] for some index x isn't -1. The only other place elements of match get a value is during static initialization where the values get zero initialized. Since the initial value is zero and the values are only ever changed if at least one of them happens to be -1, I would expect that the values retain the value 0.
You might want to use something like
std::fill(std::begin(match), std::end(match), -1);
at a strategic location since you seem to assume that the values are initially -1. Of course, you also should consider the idea of not using global variables because this doesn't scale and works really badly in a multi-threaded program.
I think my destructor is good now... but still not sure how to call print_set from within the operator() overload.
It outputs as it should, but I feel like there is an easy way to just call print_set from within the function call overload... can't seem to get things how I want them..
Header file:
/**
* disjoint_sets class declaration
*
*/
#include<ostream>
#include<string>
#include<list>
class disjoint_sets
{
public:
disjoint_sets(int n); //n is the maximum int that can be in a set, so arrays need to be of size n+1
~disjoint_sets();
bool make_set(int n);
int find_set(int u);
bool union_set(int u, int v);
void print_set(std::ostream& out, int u); //e.g. { 1, 2, 4, 8, 10, }
operator std::string(); //e.g. "{ { 1, 2, 4, 8, 10, }, { 3, 6, }, { 5, }, { 7, }, { 9, }, }"
private:
typedef std::list<int>* listptr;
int* p; //parent array
int* rank; //rank array
listptr* set; //array of pointers. Each set[i] is a pointer is to a list<int> containing the integers in the i'th set
int size; //the size i.e. maximum int that can be in a set
};
Implementation:
/**
* disjoint_sets class implementation
*
*/
#include <iostream>
#include <istream>
#include <sstream>
#include <string>
#include <list>
#include "disjoint.hpp"
/*********************
* CONSTRUCTOR
*********************/
disjoint_sets::disjoint_sets(int n)
{
size = n;
// initialize all arrays to same size (n+1)
p = new int[n+1];
rank = new int[n+1];
set = new listptr[n+1];
// loop through set (array of pointers) and initialize
// each element to an empty list
for (int i(1); i <= n; i++)
{
set[i] = new std::list<int>;
}
}
/*********************
* DESTRUCTOR
*********************/
disjoint_sets::~disjoint_sets()
{
delete[] p;
delete[] rank;
// delete each element in the set first, then delete the set.
for(int i(1); i<size+1; i++)
{
set[i]->clear();
}
delete[] set;
}
/*********************
* MAKE_SET
*********************/
bool disjoint_sets::make_set(int n)
{
// if parent already exists
if (p[n] != 0)
{
return false;
}
// else we need to make a set
else
{
// make itself the parent
p[n] = n;
// use push_front to add the int to front of set
set[n]->push_front(n);
return true;
}
}
/*********************
* FIND_SET
***********************/
int disjoint_sets::find_set(int u)
{
while (u != p[u])
{
u = p[u];
}
// returns parent
return u;
}
/*********************
* UNION_SET
*********************/
bool disjoint_sets::union_set(int u, int v)
{
// assign parents to u, v
u = find_set(u);
v = find_set(v);
// return false if parent is 0, list is empty
if (u == 0 or v == 0)
{
return false;
}
// if u's height is less than v's (not in same set)
if (rank[u] < rank[v])
{
// point u's parent to v (merge them)
p[u] = v;
// splice u out
(*set[v]).splice((*set[v]).end(), (*set[u]));
return true;
}
// u's height is greater than or equal to v's height
else
{
// set v's parent to u
p[v] = u;
// splice v out
(*set[u]).splice((*set[u]).end(), (*set[v]));
return true;
}
// if ranks are equal
if (rank[u] == rank[v])
{
// increment rank of u
rank[u]++;
return true;
}
}
/*********************
* PRINT_SET
*********************/
void disjoint_sets::print_set(std::ostream& out, int u)
{
// begin brace for set
out << "{ ";
// loop through with iter, seperate with comma
for (std::list<int>::iterator iter((*set[u]).begin()); iter != (*set[u]).end(); iter++)
{
out << *iter << ", ";
}
// end brace for set
out << "}";
}
/*********************
* STRING CONVERSION
*********************/
disjoint_sets::operator std::string()
{
// sstream variable
std::stringstream text;
// pointer to int array
int *test;
test = new int[size+1];
// opening paren
text << "{ ";
// start main loop through the array
for (int i(1); i <= (size + 1); i++)
{
// if the root is empty
if (test[find_set(i)] == 0)
{
// set to anything != 0?
test[find_set(i)] = 10;
// check if list is empty
if (set[i]->empty())
{
text << "";
}
else
{
// individual set opening parenthesis
text << "{ ";
// use iterator to loop through and load up the stringstream, separate w/ comma
for (std::list<int>::iterator iter((*set[i]).begin()); iter != (*set[i]).end(); iter++)
{
text << *iter << ", ";
}
// individual set closing parenthesis w/ proper punctuation
text << "}, ";
}
}
}
// closing parenthesis
text << "}";
delete[] test;
return text.str();
}
Driver:
#include<iostream>
#include "disjoint.hpp"
int main()
{
using namespace std;
disjoint_sets ds(12);
for( int i=1; i <= 10; ++i)
ds.make_set(i);
ds.union_set(1,2);
ds.union_set(2,4);
ds.union_set(1,8);
ds.union_set(3,6);
ds.union_set(2,10);
//{ { 1, 2, 4, 8, 10, }, { 3, 6, }, { 5, }, { 7, }, { 9, }, }
cout << string(ds) << endl;
}
Just focusing on the destructor:
// delete[] set; // need special conditions for this?
Yes, you need to delete each element in the set first and then delete the set. Use std::shared_ptr if you are not interested to do the memory management.
As a hint: You might get better answers if you ask concrete questions.