Separate out Dijkstra implementation

This separates the Dijkstra implementation into its own generic function.

Whilst doing this we can simplify the code and also save yet more memory!

Still don't get correct results for part 2.
This commit is contained in:
2022-01-12 11:31:09 +00:00
parent a1a7d11583
commit 3ba00ad3eb
3 changed files with 45 additions and 11 deletions

View File

@@ -83,10 +83,13 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti
/** \c nodes maintains a map of all the nodes we've visited or want to visit. Nodes that cost
* less than the current cost at the front of costs have been visited. The rest haven't.
*/
std::map<Node const*, Cost, NodePCmp> nodes;
std::map<Node const*, std::pair<Cost, Node const*>, NodePCmp> nodes;
/** \c costs maintains a map of costs to the nodes that cost that much to visit. */
std::map<Cost, std::unordered_set<Node const*>> costs;
Cost current_cost{std::numeric_limits<Cost>::min()};
Cost current_cost{initial_cost};
Node const* current_node{new Node(initial)};
nodes.insert({current_node, {current_cost, nullptr}});
costs.insert({current_cost, {current_node}});
/* Helper lambda to clean up after ourselves. */
auto cleanup = [](auto& nodes) {
@@ -96,38 +99,35 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti
};
/* Helper lambda to insert into the maps. */
auto inserter = [&costs, &nodes, &current_cost](Node const& node, Cost cost) {
auto inserter = [&costs, &nodes, &current_node, &current_cost](Node const& node, Cost cost) {
cost += current_cost;
auto node_it{nodes.find(&node)};
/* Skip inserting nodes we've already visited, or that would cost more to visit. */
if (node_it != nodes.end() && node_it->second <= cost) {
if (node_it != nodes.end() && node_it->second.first <= cost) {
return;
}
Node const* nodep{nullptr};
if (node_it == nodes.end()) {
nodep = new Node(node);
nodes.insert({nodep, cost});
nodes.insert({nodep, {cost, current_node}});
}
else {
/* Node has a cheaper cost than we thought: Remove the node from its old cost list */
nodep = node_it->first;
auto cost_it{costs.find(node_it->second)};
auto cost_it{costs.find(node_it->second.first)};
assert(cost_it != costs.end());
cost_it->second.erase(nodep);
/* Now update the cost in the nodes map and use the nodep as the node pointer. */
node_it->second = cost;
node_it->second.first = cost;
node_it->second.second = current_node;
}
auto [cost_it, success] = costs.insert({cost, {}});
cost_it->second.insert(nodep);
};
Node* init{new Node(initial)};
nodes.insert({init, initial_cost});
costs.insert({initial_cost, {init}});
std::uint64_t iter{0};
while (!costs.empty()) {
auto cost_it{costs.begin()};
@@ -137,6 +137,7 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti
return a + c.second.size();
}) == nodes.size() - iter);
for (auto& nodep : cost_it->second) {
current_node = nodep;
if (iter++ % 100'000 == 0) {
std::cout << "Iteration: " << iter << " cost " << current_cost
<< " total number of nodes: " << nodes.size()
@@ -145,6 +146,13 @@ auto dijkstra(Node const& initial, Cost initial_cost, TransitionManager transiti
}
if (transition_manager.is_finished(*nodep)) {
auto result{std::make_pair(*nodep, current_cost)};
/* Print the backtrace. */
Node const* n{nodep};
while (n != nullptr) {
auto node_it{nodes.find(n)};
std::cout << "Cost: " << node_it->second.first << ":\n" << *n;
n = node_it->second.second;
}
cleanup(nodes);
return result;
}

View File

@@ -169,6 +169,18 @@ private:
std::array<Type, 15> State::finished_ = {'.', '.', '.', '.', '.', '.', '.', 'A',
'B', 'C', 'D', 'A', 'B', 'C', 'D'};
std::ostream& operator<<(std::ostream& os, State const& s)
{
os << "#############\n"
<< '#' << s.node(0) << s.node(1) << '.' << s.node(2) << '.' << s.node(3) << '.' << s.node(4)
<< '.' << s.node(5) << s.node(6) << "#\n"
<< "###" << s.node(7) << '#' << s.node(8) << '#' << s.node(9) << '#' << s.node(10) << "###\n"
<< " #" << s.node(11) << '#' << s.node(12) << '#' << s.node(13) << '#' << s.node(14) << "#\n"
<< " #########\n";
return os;
}
struct StateTranstitionManager
{
bool is_finished(State const& state) { return state.finished(); }

View File

@@ -191,6 +191,20 @@ std::array<Type, State::size_> State::finished_ = {'.', '.', '.', '.', '.', '.',
'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A',
'B', 'C', 'D', 'A', 'B', 'C', 'D'};
std::ostream& operator<<(std::ostream& os, State const& s)
{
os << "#############\n"
<< '#' << s.node(0) << s.node(1) << '.' << s.node(2) << '.' << s.node(3) << '.' << s.node(4)
<< '.' << s.node(5) << s.node(6) << "#\n"
<< "###" << s.node(7) << '#' << s.node(8) << '#' << s.node(9) << '#' << s.node(10) << "###\n"
<< " #" << s.node(11) << '#' << s.node(12) << '#' << s.node(13) << '#' << s.node(14) << "#\n"
<< " #" << s.node(15) << '#' << s.node(16) << '#' << s.node(17) << '#' << s.node(18) << "#\n"
<< " #" << s.node(19) << '#' << s.node(20) << '#' << s.node(21) << '#' << s.node(22) << "#\n"
<< " #########\n";
return os;
}
struct StateTranstitionManager
{
bool is_finished(State const& state) { return state.finished(); }