I have tasks and I want to calculate the most profitable order to arrange them.
Instead of checking every permutation and doing n*n! calculations, I want to build a tree of permutations, that is, the number of children at each level decreases by 1, and at each node the sub-permutation that has already been calculated will be saved and not recalculated.
For example, if I have 4 tasks, the tree will look like this:
My attached code is missing. I don't know how to build the tree and the give nodes the indexes as in the figure. I know how to deal with a binary tree, but not with a tree where the number of children is different at each lavel.
(The value of each task depends on its location.
I know how to do that, so I didn't include it in the question).
int n = 4;
struct node
{
int task_index = -1;
double value;
struct node **next;
};
void build_tree(node *current_node, int current_level = 0)
{
if (current_level < 1 || current_level >= n)
return;
// current_node->task_index = ? ;
current_node->next = new node *[n - current_level];
for (int i = 0; i < n - current_level; i++)
{
build_tree(current_node->next[i], current_level + 1);
}
}
void print_tree(node *current_node, int current_level = 0)
{
// print indexes
}
void delete_tree(node *current_node, int current_level = 0)
{
// delete nodes
}
int main()
{
struct node *root = new node;
build_tree(root);
print_tree(root);
delete_tree(root);
delete root;
return 0;
}
void build_tree(node *current_node, int current_level = 0)
{
if (current_level < 1 || current_level >= n)
return;
// current_node->task_index = ? ;
current_node->next = new node *[n - current_level];
for (int i = 0; i < n - current_level; i++)
{
build_tree(current_node->next[i], current_level + 1);
}
}
When called with the default parameter of current_level = 0, as you illustrate in your code below, this function exits on the first line without doing anything. You need to decide whether you are indexing starting from 0 or from 1.
Other than that, the general outline of the algorithm looks okay, although I did not explicitly check for correctness.
Now, more broadly: is this an exercise to see if you can write a tree structure, or are you trying to get the job done? In the latter case you probably want to use a prebuilt data structure like that in the boost graph library.
If it's an exercise in building a tree structure, is it specifically an exercise to see if you can write code dealing with raw pointers-to-pointers? If not, you should work with the correct C++ containers for the job. For instance you probably want to store the list of child nodes in a std::vector rather than have a pointer-to-pointer with the only way to tell how many child nodes exist being the depth of the node in the tree. (There may be some use case for such an extremely specialized structure if you are hyper-optimizing something for a very specific reason, but it doesn't look like that's what's going on here.)
From your explanation what you are trying to build is a data structure that reuses sub-trees for common permutations:
012 -> X
210 -> X
such that X is only instantiated once. This, of course, is recursive, seeing as
01 -> Y
10 -> Y
Y2 -> X
If you look at it closely, there are 2^n such subtrees, because any prefix can have any one of the n input tasks used or not. This means you can represent the subtree as an index into an array of size 2^n, with a total footprint O(n*2^n), which improves on the vastly larger >n! tree:
struct Edge {
std::size_t task;
std::size_t sub;
};
struct Node {
std::vector<Edge> successor; // size in [0,n]
};
std::vector<Node> permutations; // size exactly 2^n
This will have this structure:
permutations: 0 1 2 3 4 ...
|-^
|---^
|-------^
|---^
|-^
Where the node at, e.g., location 3 has both task 0 and 1 already used and "points" to all (n-2) subtrees.
Of course, building this is not entirely trivial, but it compressed the search space and allows you re-use results for specific sub-trees.
You can build the table like this:
permutations.resize(1<<n);
for (std::size_t i = 0; i < size(permutations); ++i) {
permutations[i].successor.reserve(n); // maybe better heuristic?
for (std::size_t j = 0; j < n; ++j) {
if (((1<<j) & i) == 0) {
permutations[i].successor.push_back({j,(1<<j)|i});
}
}
}
Here is a live demo for n=4.
The recursive way to generate permutations is if you have n items then all of the permutations of the items are each of the n items concatenated with the permutations of the n-1 remaining items. In code this is easier to do if you pass around the collection of items.
Below I do it with an std::vector<int>. Once using a vector it makes more sense to just follow the "rule of zero" pattern and let the nodes have vectors of children and then not need to dynamically allocate anything manually:
#include <vector>
#include <algorithm>
#include <iostream>
struct node
{
int task_index = -1;
double value;
std::vector<node> next;
};
std::vector<int> remove_item(int item, const std::vector<int>& items) {
std::vector<int> output(items.size() - 1);
std::copy_if(items.begin(), items.end(), output.begin(),
[item](auto v) {return v != item; }
);
return output;
}
void build_tree(node& current_node, const std::vector<int>& tasks)
{
auto n = static_cast<int>(tasks.size());
for (auto curr_task : tasks) {
node child{ curr_task, 0.0, {} };
if (n > 1) {
build_tree(child, remove_item(curr_task, tasks));
}
current_node.next.emplace_back(std::move(child));
}
}
void print_tree(const node& current_node)
{
std::cout << "( " << current_node.task_index << " ";
for (const auto& child : current_node.next) {
print_tree(child);
}
std::cout << " )";
}
int main()
{
node root{ -1, 0.0, {} };
build_tree(root, { 1, 2, 3 });
print_tree(root);
return 0;
}
Related
I've searched around but can't really understand or find help, since this iterative algorithm will require two stacks (to contain a left_Index and right_Index).
The main recursive way involves having it one side until the left_Index >= right_Index, and recursively doing so for both sides and per subsection (if that makes sense), which I don't understand how to do so exactly since I'm maintaining two stacks and need to see how exactly they relate to one another.
This problem is mostly due to me not understanding the way the normal recursive method words, although when looking at them side by side to see how to approach it, I always get stuck on what to do.
The backstory as to why I'm doing this:
Trying to solve the word ladder problem to go from A to B and decided to make a BST where the connections are connected by singular character differences and lengths. I'm getting the words from a text file containing a lot of the dictionary, and since I'm using a BST as the master list with all vertices the fact that this is a dictionary means every insert will be in order so the tree is right-leaning (so the speeds are slow for inserting O(n^2) which is a big hinderance). I was planning on storing data in an array then making a balanced BST from that since I believe speeds should go faster since insertion will be O(n*logn) which seems great. The problem with that is that I can't use a recursive approach since there's a lot of data leading to stack overflows, so I need to make it iteratively with stacks and loops, but am finding it too difficult.
My bad attempt at a start:
while (lindx.the_front() < rindx.the_back())
{
mid =(lindx.the_front() + rindx.the_back()) / 2;
dictionary.addVertex(vector[mid]);
std::cout << "Pushed " << vector[mid] << '\n';
rindx.push(mid - 1);
}
That basically gets the 1/2's from the left half of the program from a linked stack I made. "the_front()" is the first insertion, "the_back()" is the final/latest insert into the list. The main problem I have is understanding how to make it repeat per half to get all the values.
I need to find my past homework where I've done this but the code is something along the lines of...
void array2balanced(int array[], int lIndex, int rIndex)
{
//base case
if(lIndex > rIndex)
{
return;
}
//recursive cals
else
{
mid = (lIndex+rIndex)/2;
tree.insert(array[mid]);
array2balanced(array, lIndex, mid-1);
array2balanced(array, mid+1, rIndex);
}
}
UPDATE:
Progress so far
void balancedTree(std::vector<std::string> vector, dictionaryGraph &dictionary) // divide and conquer into tree?
{
linkedStack<int> lindx, rindx, midX;
unsigned int l_Index{ 0 }, r_Index{ vector.size() - 1 }, mid{ (l_Index + r_Index) / 2 };;
lindx.push(l_Index);
rindx.push(r_Index);
midX.push(mid);
int testCount{ 0 };
std::cout << "There are " << vector.size() << " words.\n";
while (!midX.empty())
{
mid = midX.pop();
l_Index = lindx.pop();
r_Index = rindx.pop();
std::cout << "inputted " << vector[mid] << '\n';
dictionary.addVertex(vector[mid]);
testCount++;
if (r_Index > l_Index)
{
midX.push((l_Index + mid) / 2);
lindx.push(l_Index);
rindx.push(mid - 1);
}
if (l_Index < r_Index)
{
midX.push((mid + r_Index) / 2);
lindx.push(mid + 1);
rindx.push(r_Index);
}
}
std::cout << testCount << " words were inputted...\n"; // To see how many were inserted
system("pause");
}
Problem I have is some inputs get repeated and some missed.
I don't think you need two stacks. You just need either a one stack or one queue.
Below codes can be tested on Leetcode
Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
One Stack Method
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
l = len(nums)
node = TreeNode(0)
head = node
s = collections.deque([(node, 0, l)])
while s:
node, left, right = s.pop()
mid = (right + left) // 2
node.val = nums[mid]
if mid < right-1:
node.right = TreeNode(0)
s.append((node.right, mid+1, right))
if left < mid:
node.left = TreeNode(0)
s.append((node.left, left, mid))
return head
One Queue Method
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
l = len(nums)
node = TreeNode(0)
head = node
q = collections.deque([(node, 0, l)])
while q:
node, left, right = q.popleft()
mid = (right + left) // 2
node.val = nums[mid]
if left < mid:
node.left = TreeNode(0)
q.append((node.left, left, mid))
if mid < right-1:
node.right = TreeNode(0)
q.append((node.right, mid+1, right))
return head
They are implemented using deque. Notice popleft() returns the first element(like stack) and pop() returns the last element(like queue).
This problem is mostly due to me not understanding the way the normal
recursive method words, although when looking at them side by side to
see how to approach it, I always get stuck on what to do.
It takes practice ... and maybe reviewing other peoples work.
require two stacks (to contain a left_Index and right_Index).
My apologies, I do not understand why the OP thinks this. My demo below has only 1 stack called 'todo', perhaps you will find the idea useful.
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <cassert>
#include "./BTree.hh" // code not provided, used in this MCVE to
// conveniently provide "showTallTreeView()"
typedef std::vector<int> IVec_t;
class T607_t
{
IVec_t m_sortedIVec; // sorted - created with for loop
IVec_t m_recursiveIVec; // extract from sorted by recursion
IVec_t m_iterativeIVec; // extract from sorted by iteration
public:
T607_t() = default;
~T607_t() = default;
int exec(int , char** )
{
fillShowSortedIVec();
fillShowRecursiveIVec();
fillShowIterativeIVec();
showResults();
return 0;
}
private: // methods
The vectors are in class T607_t, so that each is available to any member function.
For this MCVE, I simply create "IVec_t m_sortedIVec;" and fill with a simple for loop:
void fillShowSortedIVec()
{
for (int i=0; i<15; ++i)
m_sortedIVec.push_back (i*100); // create in sorted order
showIVec(m_sortedIVec, "\n m_sortedIVec :");
}
Next (in this MCVE) is the recursive fill and show, and my adaptation of the OP's recursive method to produce the recursive insert sequence:
// ///////////////////////////////////////////////////////////////
void fillShowRecursiveIVec()
{
assert(m_sortedIVec.size() > 0);
int max = static_cast<int>(m_sortedIVec.size()) - 1;
// use OP's recursive insert
array2balancedR (m_sortedIVec, 0, max);
// NOTE - 'sequence' is inserted to 'm_recursiveIVec'
// instead of into tree the op did not share
showIVec(m_recursiveIVec, "\n m_recursiveIVec:");
}
// recursive extract from: m_sortedIVec to: m_recursiveIVec
// my adaptation of OP's recursive method
void array2balancedR(IVec_t& array, int lIndex, int rIndex)
{
//base case
if(lIndex > rIndex)
{
return;
}
else //recursive calls
{
int mid = (lIndex+rIndex)/2;
m_recursiveIVec.push_back(array[mid]); // does this
// tree.insert(array[mid]); // instead of this
array2balancedR(array, lIndex, mid-1); // recurse left
array2balancedR(array, mid+1, rIndex); // recurse right
}
}
Note: I left the "IVec_t& array" as a parameter to this function, because the OP's code has it. Within this 'class' wrapper, the function need not pass the array 'through the recursion', because each method has access to the instance data.
Next (in this MCVE) is a fill and show action using one possible iterative approach. I styled this iterative approach carefully to match the OP's recursive effort.
First, I added a 'tool' (IndxRng_t) to simplify the 'stack' capture of iterations for later processing. (i.e. "todo").
// //////////////////////////////////////////////////////////////
// iterative extract from m_sortedIVec to: m_iterativeIVec
class IndxRng_t // tool to simplify iteration
{
public:
IndxRng_t() = delete; // no default
IndxRng_t(int li, int ri)
: lIndx (li)
, rIndx (ri)
{}
~IndxRng_t() = default;
// get'er and set'er free. also glutton free. gmo free.
bool done() { return (lIndx > rIndx); } // range used up
int mid() { return ((lIndx + rIndx) / 2); } // compute
IndxRng_t left(int m) { return {lIndx, m-1}; } // ctor
IndxRng_t right(int m) { return {m+1, rIndx}; } // ctor
private:
int lIndx;
int rIndx;
};
void fillShowIterativeIVec()
{
assert(m_sortedIVec.size() > 0);
int max = static_cast<int>(m_sortedIVec.size()) - 1;
array2balancedI(m_sortedIVec, 0, max);
// 'sequence' inserted to 'm_iterativeIVec'
showIVec(m_iterativeIVec, "\n m_iterativeIVec:");
}
void array2balancedI(IVec_t& array, int lIndex, int rIndex)
{
std::vector<IndxRng_t> todo;
todo.push_back({lIndex, rIndex}); // load the first range
// iterative loop (No recursion)
do
{
if (0 == todo.size()) break; // exit constraint
// no more ranges to extract mid from
// fetch something to do
IndxRng_t todoRng = todo.back();
todo.pop_back(); // and remove from the todo list
if(todoRng.done()) continue; // lIndex > rIndex
int mid = todoRng.mid();
m_iterativeIVec.push_back(array[mid]); // do this
// tree.insert(array[mid]); // instead of this
todo.push_back(todoRng.right(mid) ); // iterate on right
todo.push_back(todoRng.left(mid) ); // iterate on left
}while(1);
}
And this mcve generates a result display:
void showResults()
{
assert(m_recursiveIVec.size() == m_sortedIVec.size());
assert(m_iterativeIVec.size() == m_sortedIVec.size());
std::cout << std::endl;
std::stringstream ss; // for btree use only
std::cout << "\n demo:\n create a BTree, "
<< std::flush;
std::cout << "\n Insert IVec_t " << std::endl;
BBT::BTree_t btree(ss);
std::cout << std::flush;
for (size_t i=0; i<m_iterativeIVec.size(); ++i)
btree.insertPL(m_iterativeIVec[i]);
std::cout << "\n iterative result:\n\n"
<< btree.showTallTreeView();
}
void showIVec(IVec_t& ivec, std::string lbl)
{
std::cout << lbl << std::endl;
for (auto it : ivec)
std::cout << std::setw(5) << it << std::flush;
std::cout << std::endl;
}
}; // class T607_t
int main(int argc, char* argv[])
{
T607_t t607;
return t607.exec(argc, argv);
}
My output (on Ubuntu 17.10, g++ 7.2.0),
m_sortedIVec :
0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400
m_recursiveIVec:
700 300 100 0 200 500 400 600 1100 900 800 1000 1300 1200 1400
m_iterativeIVec:
700 300 100 0 200 500 400 600 1100 900 800 1000 1300 1200 1400
demo:
create a BTree,
Insert IVec_t
iterative result:
BTree_t::showTallTreeView(): (balance: 0 sz: 15)
0
100
200
300
400
500
600
700
800
900
1000
1100
1200
1300
1400
-----------------
Iterative JavaScript implementation of converting sorted array to Binary Search Tree (BST):
function sortedArrayToBstIteratively(nums) {
// use stack to iteratively split nums into node tuples and reuse values
const stack = []
// add root node to tree
const tree = { first: 0, last: nums.length - 1 }
stack.push(tree)
// split array in the middle and continue with the two halfs
while (stack.length > 0) {
const node = stack.pop()
if (node.last >= node.first) {
if (node.last === node.first) {
// node reaches a single leaf value (last == first)
node.value = nums[node.first]
} else {
// node has still valid indices to further split the array (last > first)
const middle = Math.ceil((node.first + node.last) / 2)
node.value = nums[middle]
node.left = { first: node.first, last: middle - 1 }
node.right = { first: middle + 1, last: node.last }
stack.push(node.left)
stack.push(node.right)
}
} else {
// node has no more valid indices (last < first), create empty leaf
node.value = null
}
delete node.first
delete node.last
}
// console.log(JSON.stringify(tree))
return tree
}
Using the following A star visualization as a way to compare path accuracy, I found a large variation between my implementation and this one.
https://qiao.github.io/PathFinding.js/visual/
Path I'm comparing to:
(source: imgsafe.org)
My test paths:
(source: imgsafe.org)
There are times when it seems like the algorithm is checking too few nodes (i.e Test#6). Is this to be expected, or is it not correct?
Important variables in algorithm:
TileMap* m_tileMap;
vector<Tile*> m_openList;
vector<Tile*> m_path;
// Direct mapping of 2D tile map.
// Stores the list type for the same-indexed tile
vector<vector<int>> m_listMap;
Comparator for sorting open list:
struct CompareNodes
{
// sorts lowest F cost to end of vector
bool operator() (Tile* lhs, Tile* rhs)
{
return lhs->getFCost() > rhs->getFCost();
}
};
High level implementation:
vector<Tile*> PathGenerator::generatePath(Tile* startNode, Tile* endNode)
{
setUpListMap();
startNode->setGCost(0);
startNode->setHCost(calculateHCost(startNode, endNode)); // Manhattan (no diagonal). { abs(y2 - y1) + abs(x2 - x1) }
startNode->calculateFCost(); // calculates G+H internally
m_openList.push_back(startNode);
Vector2D startNodePos = startNode->getMapPos();
m_listMap[startNodePos.x][startNodePos.y] = LIST_TYPES::OPEN;
Tile* currentNode;
while (m_openList.empty() == false)
{
currNode = m_openList[m_openList.size() - 1];
m_openList.pop_back();
Vector2D currNodePos = currNode->getMapPos();
m_listMap[currNodePos.x][currNodePos.y] = LIST_TYPES::CLOSED;
if (currNode != endNode)
{
vector<Tile*> neighbours = findNeighbours(currNode);
removeUnnecessaryNodes(&neighbours); // remove walls and closed nodes
computeCosts(&neighbours, currNode, endNode);
addUniqueNodesToOpenList(&neighbours); // ignores duplicates and then sorts open list
}
else
{
m_path = getPath(currNode);
resetLists(); // erases all vectors
}
}
return m_path;
}
void PathGenerator::computeCosts(vector<Tile*>* nodes, Tile* current, Tile* end)
{
int newGCost = current->getGCost() + 1;
for (int i = 0; i < nodes->size(); i++)
{
Tile* node = nodes->at(i);
unsigned int nodeGCost = node->getGCost(); // G cost defaults to max int limit
if (newG < nodeGCost)
{
// set up node costs like above
node->setParentNode(current);
}
}
}
I've added the most important code. If the high level functions don't help to find the source of the issue, let me know and I'll add the implementation for them also.
Help appreciated.
The sorting part seems correct, but since it's a vector this should be very easy for you to verify.
Instead, try using a for-loop as a test-case to make sure you're really get the lowest f-cost node:
Tile* currnode = m_openlist[0];
for (int i = 0; i < m_openList.size() i++)
{
if (m_openList[i]->getFCost() < currnode->getFCost())
currnode = m_openList[i];
}
See if that fixes it. If it does, there's an issue in your sort, but i'm not sure what the issue would be.
Also, in your computeCosts function, you do:
for (int i = 0; i < nodes->size(); i++)
{
Tile* node = nodes->at(i);
//.. other code
}
Since you're using an std::vector, why not make use of its functionality, and use iterators or a range based loop:
// Iterators
for (auto it = nodes->begin(); it != nodes->end(); it++)
{
Tile* node = *it;
//.. other code
}
// Range based loop
for (auto node : *nodes)
{
//.. other code
}
i would like some help for my AStar algorithm search, which takes from my point of view far to long. Even though my map is with 500 * 400 coordinates(objectively is my tile graph a bit smaller since I don't took the walls into the TileGraph.) large, I would like to expect the result after a few seconds. The world looks like this, despite the task not being mine
I want to search from marked coordinates "Start"(120|180) to "Ziel"(320|220), which currently takes 48 minutes. And sorry for all, who don't speak german, but the text at the picture isn't important.
At first I want to show you, what I've programmed for A*. In General adapted myself to the pseudocode at https://en.wikipedia.org/wiki/A*_search_algorithm .
bool AStarPath::Processing(Node* Start, Node* End)
m_Start = Start;
m_End = End;
for (Node* n : m_SearchRoom->GetAllNodes())
{
DistanceToStart[n] = std::numeric_limits<float>::infinity();
CameFrom[n] = nullptr;
}
DistanceToStart[m_Start] = 0;
NotEvaluatedNodes.AddElement(0, m_Start);
while (NotEvaluatedNodes.IsEmpty() == false)
{
Node* currentNode = NotEvaluatedNodes.GetElement();
NotEvaluatedNodes.DeleteElement();
if (currentNode == m_End)
{
ReconstructPath();
return true;
}
EvaluatedNodes.insert(currentNode);
ExamineNeighbours(currentNode);
}
return false;
//End Processing
void AStarPath::ExamineNeighbours(Node* current)
for (Node* neighbour : m_SearchRoom->GetNeighbours(current))
{
if (std::find(EvaluatedNodes.begin(), EvaluatedNodes.end(), neighbour) != EvaluatedNodes.end())
{
continue;
}
bool InOpenSet = NotEvaluatedNodes.ContainsElement(neighbour);
float tentative_g_score = DistanceToStart[current] + DistanceBetween(current, neighbour);
if (InOpenSet == true && tentative_g_score >= DistanceToStart[neighbour])
{
continue;
}
CameFrom[neighbour] = current;
DistanceToStart[neighbour] = tentative_g_score;
float Valuation = tentative_g_score + DistanceBetween(neighbour, m_End);
if (InOpenSet == false)
{
NotEvaluatedNodes.AddElement(Valuation, neighbour);
}
else
{
NotEvaluatedNodes.UpdatePriority(neighbour, Valuation);
}
}
//END ExamineNeighbours
double AStarPath::DistanceBetween(Node* a, Node* b)
return sqrt(pow(m_SearchRoom->GetNodeX(a) - m_SearchRoom->GetNodeX(b), 2)
+ pow(m_SearchRoom->GetNodeY(a) - m_SearchRoom->GetNodeY(b), 2));
//END DistanceBetween
I'm sorry for the bad formatting, but I don't really know how to work with the code blocks here.
class AStarPath
private:
std::unordered_set<Node*> EvaluatedNodes;
Binary_Heap NotEvaluatedNodes;
std::unordered_map<Node*, float> DistanceToStart;
std::unordered_map<Node*, Node*> CameFrom;
std::vector<Node*> m_path;
TileGraph* m_SearchRoom;
//END Class AStarPath
Anyway, i have thought myself over my problem already and changed some things.
Firstly, I implemented a binary heap instead of the std::priority_queue. I used a page at policyalmanac for it, but I'm not permitted to add another link, so I can't really give you the address. It improved the performance, but it still takes quite long as I told at the beginning.
Secondly, I used unordered containers (if there are two options), so that the containers don't have to be sorted after the changes. For my EvaluatedNodes I took the std::unordered_set, since from my knowledge it's fastest for std::find, which I use for containment checks.
The usage of std::unordered_map is caused by the need of having seperate keys and values.
Thirdly, I thought about splitting my map into nodes, which represent multiple coordinates(instead of now where one node represents one coordinate) , but I'm not really sure how to choose them. I thought about setting points at position, that the algorithm decises based on the length and width of the map and add neighbouring coordinates, if there aren't a specific distance or more away from the base node/coordinate and I can reach them only from previous added coordinates. To Check whether there is a ability to walk, I would have used the regular A*, with only the coordinates(converted to A* nodes), which are in these big nodes. Despite this I'm unsure which coordinates I should take for the start and end of this pathfinding. This would probably reduce the number of nodes/coordinates, which are checked, if I only use the coordinates/nodes, which were part of the big nodes.(So that only nodes are used, which where part of the bigger nodes at an upper level)
I'm sorry for my english, but hope that all will be understandable. I'm looking forward to your answers and learning new techniques and ways to handle problems and as well learn about all the hundreds of stupids mistakes I produced.
If any important aspect is unclear or if I should add more code/information, feel free to ask.
EDIT: Binary_Heap
class Binary_Heap
private:
std::vector<int> Index;
std::vector<int> m_Valuation;
std::vector<Node*> elements;
int NodesChecked;
int m_NumberOfHeapItems;
void TryToMoveElementUp(int i_pos);
void TryToMoveElementDown(int i_pos);
public:
Binary_Heap(int i_numberOfElements);
void AddElement(int Valuation, Node* element);
void DeleteElement();
Node* GetElement();
bool IsEmpty();
bool ContainsElement(Node* i_node);
void UpdatePriority(Node* i_node, float newValuation);
Binary_Heap::Binary_Heap(int i_numberOfElements)
Index.resize(i_numberOfElements);
elements.resize(i_numberOfElements);
m_Valuation.resize(i_numberOfElements);
NodesChecked = 0;
m_NumberOfHeapItems = 0;
void Binary_Heap::AddElement(int valuation, Node* element)
++NodesChecked;
++m_NumberOfHeapItems;
Index[m_NumberOfHeapItems] = NodesChecked;
m_Valuation[NodesChecked] = valuation;
elements[NodesChecked] = element;
TryToMoveElementUp(m_NumberOfHeapItems);
void Binary_Heap::DeleteElement()
elements[Index[1]] = nullptr;
m_Valuation[Index[1]] = 0;
Index[1] = Index[m_NumberOfHeapItems];
--m_NumberOfHeapItems;
TryToMoveElementDown(1);
bool Binary_Heap::IsEmpty()
return m_NumberOfHeapItems == 0;
Node* Binary_Heap::GetElement()
return elements[Index[1]];
bool Binary_Heap::ContainsElement(Node* i_element)
return std::find(elements.begin(), elements.end(), i_element) != elements.end();
void Binary_Heap::UpdatePriority(Node* i_node, float newValuation)
if (ContainsElement(i_node) == false)
{
AddElement(newValuation, i_node);
}
else
{
int treePosition;
for (int i = 1; i < Index.size(); i++)
{
if (elements[Index[i]] == i_node)
{
treePosition = i;
break;
}
}
//Won't influence each other, since only one of them will change the position
TryToMoveElementUp(treePosition);
TryToMoveElementDown(treePosition);
}
void Binary_Heap::TryToMoveElementDown(int i_pos)
int nextPosition = i_pos;
while (true)
{
int currentPosition = nextPosition;
if (2 * currentPosition + 1 <= m_NumberOfHeapItems)
{
if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition]])
{
nextPosition = 2 * currentPosition;
}
if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition + 1]])
{
nextPosition = 2 * currentPosition + 1;
}
}
else
{
if (2 * currentPosition <= m_NumberOfHeapItems)
{
if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition]])
{
nextPosition = 2 * currentPosition;
}
}
}
if (currentPosition != nextPosition)
{
int tmp = Index[currentPosition];
Index[currentPosition] = Index[nextPosition];
Index[nextPosition] = tmp;
}
else
{
break;
}
}
void Binary_Heap::TryToMoveElementUp(int i_pos)
int treePosition = i_pos;
while (treePosition != 1)
{
if (m_Valuation[Index[treePosition]] <= m_Valuation[Index[treePosition / 2]])
{
int tmp = Index[treePosition / 2];
Index[treePosition / 2] = Index[treePosition];
Index[treePosition] = tmp;
treePosition = treePosition / 2;
}
else
{
break;
}
}
This line introduces major inefficiency, as it needs to iterate over all the nodes in the queue, in each iteration.
bool InOpenSet = NotEvaluatedNodes.ContainsElement(neighbour);
Try using a more efficient data structure, e.g. the unordered_set you use for EvaluatedNodes. Whenever you push or pop a node from the heap, modify the set accordingly to always contain only the nodes in the heap.
I am investigating data structures to satisfy O(1) get operations and came across a structure called Trie.
I have implemented the below simple Trie structure to hold numbers (digits only).
Ignore the memory leak - it is not the topic here :)
The actual storage in the Data class is not related as well.
#include <sstream>
#include <string>
struct Data
{
Data(): m_nData(0){}
int m_nData;
};
struct Node
{
Node(): m_pData(NULL)
{
for (size_t n = 0; n < 10; n++)
{
digits[n] = NULL;
}
}
void m_zAddPartialNumber(std::string sNumber)
{
if (sNumber.empty() == true) // last digit
{
m_pData = new Data;
m_pData->m_nData = 1;
}
else
{
size_t nDigit = *(sNumber.begin()) - '0';
if (digits[nDigit] == NULL)
{
digits[nDigit] = new Node;
}
digits[nDigit]->m_zAddPartialNumber(sNumber.substr(1, sNumber.length() - 1));
}
}
Data* m_pData;
Node* digits[10];
};
struct DB
{
DB() : root(NULL){}
void m_zAddNumber(std::string sNumber)
{
if (root == NULL)
{
root = new Node;
}
root->m_zAddPartialNumber(sNumber);
}
Node* root;
};
int main()
{
DB oDB;
for (size_t nNumber = 0; nNumber <= 10000; nNumber++)
{
std::ostringstream convert;
convert << nNumber;
std::string sNumber = convert.str();
oDB.m_zAddNumber(sNumber);
}
return 0;
}
My main function is simply inserting numbers into the data structure.
I've examined the overall memory allocated using Windows task manager and came across an interesting feature i can't explain and am seeking your advice.
I've re-executed my simple program with different numbers inserted to the structure (altering the for loop stop condition) - here is a table of the experiment results:
Plotting the numbers in a logarithmic scaled graph reveals:
As you can see, the graph is not linear.
My question is why?
I would expect the allocation to behave linear across the range.
A linear relation of y on x is of the form y=a+bx. This is a straight line in a y vs x plot, but not in a log(y) vs log(x) plot, unless the constant a=0. So, I conjecture that your relation may still be (nearly) linear with a~340 kB.
Good day, I found this priority queue implementation and I am trying to get a min version of it (instead of max). I have no idea where to start. I tried mixing the signs of the functions (naive attempt) but it didn't get me far. Any help of how to implement it and a few words explaining it are very wellcome. The source is below:
Note I have left it's comments
#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;
class PriorityQueue
{
vector<int> pq_keys;
void shiftRight(int low, int high);
void shiftLeft(int low, int high);
void buildHeap();
public:
PriorityQueue(){}
PriorityQueue(vector<int>& items)
{
pq_keys = items;
buildHeap();
}
/*Insert a new item into the priority queue*/
void enqueue(int item);
/*Get the maximum element from the priority queue*/
int dequeue();
/*Just for testing*/
void print();
};
void PriorityQueue::enqueue(int item)
{
pq_keys.push_back(item);
shiftLeft(0, pq_keys.size() - 1);
return;
}
int PriorityQueue::dequeue()
{
assert(pq_keys.size() != 0);
int last = pq_keys.size() - 1;
int tmp = pq_keys[0];
pq_keys[0] = pq_keys[last];
pq_keys[last] = tmp;
pq_keys.pop_back();
shiftRight(0, last-1);
return tmp;
}
void PriorityQueue::print()
{
int size = pq_keys.size();
for (int i = 0; i < size; ++i)
cout << pq_keys[i] << " ";
cout << endl;
}
void PriorityQueue::shiftLeft(int low, int high)
{
int childIdx = high;
while (childIdx > low)
{
int parentIdx = (childIdx-1)/2;
/*if child is bigger than parent we need to swap*/
if (pq_keys[childIdx] > pq_keys[parentIdx])
{
int tmp = pq_keys[childIdx];
pq_keys[childIdx] = pq_keys[parentIdx];
pq_keys[parentIdx] = tmp;
/*Make parent index the child and shift towards left*/
childIdx = parentIdx;
}
else
{
break;
}
}
return;
}
void PriorityQueue::shiftRight(int low, int high)
{
int root = low;
while ((root*2)+1 <= high)
{
int leftChild = (root * 2) + 1;
int rightChild = leftChild + 1;
int swapIdx = root;
/*Check if root is less than left child*/
if (pq_keys[swapIdx] < pq_keys[leftChild])
{
swapIdx = leftChild;
}
/*If right child exists check if it is less than current root*/
if ((rightChild <= high) && (pq_keys[swapIdx] < pq_keys[rightChild]))
{
swapIdx = rightChild;
}
/*Make the biggest element of root, left and right child the root*/
if (swapIdx != root)
{
int tmp = pq_keys[root];
pq_keys[root] = pq_keys[swapIdx];
pq_keys[swapIdx] = tmp;
/*Keep shifting right and ensure that swapIdx satisfies
heap property aka left and right child of it is smaller than
itself*/
root = swapIdx;
}
else
{
break;
}
}
return;
}
void PriorityQueue::buildHeap()
{
/*Start with middle element. Middle element is chosen in
such a way that the last element of array is either its
left child or right child*/
int size = pq_keys.size();
int midIdx = (size -2)/2;
while (midIdx >= 0)
{
shiftRight(midIdx, size-1);
--midIdx;
}
return;
}
int main()
{
//example usage
PriorityQueue asd;
asd.enqueue(2);
asd.enqueue(3);
asd.enqueue(4);
asd.enqueue(7);
asd.enqueue(5);
asd.print();
cout<< asd.dequeue() << endl;
asd.print();
return 0;
}
Well generally in such problems, i.e. algorithms based on comparison of elements, you can redefine what does (a < b) mean. (That is how things in standard library work by the way. You can define your own comparator.)
So if you change it's meaning to the opposite. You will reverse the ordering.
You need to identify every comparison of elements, and switch it. So for every piece of code like this
/*if child is bigger than parent we need to swap*/
if (pq_keys[childIdx] > pq_keys[parentIdx])
invert it's meaning/logic.
Simple negation should do the trick:
/*if child is NOT bigger than parent we need to swap*/
if !(pq_keys[childIdx] > pq_keys[parentIdx])
You do not even need to understand algorithm. Just inverse meaning of what lesser element is.
Edit:
Additional note. You could actually refactor it into some kind of bool compare(T a, T b). And use this function where comparison is used. So whenever you want to change the behaviour you just need to change one place and it will be consistent. But that is mostly to avoid work to look for every such occurrence, and stupid bugs and when you miss one.
Easier:
std::prioroty_queue<int, std::vector<int>, std::greater<int>> my_queue;
If this is part of an exercise, then I suggest following the standard library's design principles: split the problem up:
data storage (e.g. std::vector)
sorting or "heapifying" algorithm (c.f. std::make_heap etc.)
ordering criteria (to be used by 2. above)
Your class should give you some leeway to change any of these independently. With that in place, you can trivially change the "less-than" ordering for a "greater than" one.