WhiZTiM

Dijkstra-algo example

Oct 2nd, 2020
1,492
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.99 KB | None | 0 0
  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. #include <fstream>
  5. #include <sstream>
  6. #include <optional>
  7. #include <algorithm>
  8. #include <unordered_map>
  9. #include <iomanip>
  10. #include <queue>
  11. #include <cmath>
  12. #include <tuple>
  13. #include <set>
  14.  
  15.  
  16. class CSVReader {
  17. public:
  18.     CSVReader(std::string inputFileName) {
  19.         using namespace std::string_literals;
  20.         std::ifstream file(inputFileName);
  21.         std::string line;
  22.  
  23.         if (!file.is_open()) {
  24.             throw std::invalid_argument("Unknown Input given; The filename \"" + inputFileName + "\" does not exist");
  25.         }
  26.  
  27.         while (std::getline(file, line)) {
  28.             m_csv_data.emplace_back();
  29.  
  30.             std::istringstream rowStream(line);
  31.             std::string row;
  32.             while (std::getline(rowStream, row, ',')) {
  33.                 m_csv_data.back().push_back(std::move(row));
  34.             }
  35.             if (!line.empty() and line.back() == ',') {
  36.                 m_csv_data.back().emplace_back();
  37.             }
  38.  
  39.             if (m_csv_data.size() > 1) {
  40.                 auto beforeLastRowSize = std::prev(m_csv_data.end())->size();
  41.                 auto lastRowSize = std::prev(m_csv_data.end(), 2)->size();
  42.                 if (beforeLastRowSize != lastRowSize) {
  43.  
  44.                     std::cerr << "Row number: " + std::to_string(m_csv_data.size()) + " with length " +
  45.                               std::to_string(beforeLastRowSize) + " has a different length than earlier row with length " +
  46.                               std::to_string(lastRowSize);
  47.                     throw std::invalid_argument("Row number: " + std::to_string(m_csv_data.size()) + " has a different length than earlier rows");
  48.                 }
  49.             }
  50.         }
  51.     }
  52.  
  53.     int getTotalRowCount() const {
  54.         return m_csv_data.size();
  55.     }
  56.  
  57.     int getTotalColCount() const {
  58.         return m_csv_data.empty() ? 0 : m_csv_data.back().size();
  59.     }
  60.  
  61.     const std::vector<std::vector<std::string>>& getDataAsTable() const& {
  62.         return m_csv_data;
  63.     }
  64.  
  65. private:
  66.     std::vector<std::vector<std::string>> m_csv_data;
  67. };
  68.  
  69.  
  70.  
  71. struct OutdegreeGraphNode {
  72.     int nodeId;
  73.     float cost;
  74. };
  75.  
  76.  
  77.  
  78.  
  79. class AdjacencyList {
  80. public:
  81.     const std::vector<OutdegreeGraphNode>& getOutboundNodes(int nodeId) const {
  82.         return m_adj_list.at(nodeId);
  83.     }
  84.  
  85.     void connectNode(int nodeIdA, int nodeIdB, float cost) {
  86.         assertNodeExists(nodeIdA);
  87.         assertNodeExists(nodeIdB);
  88.         m_adj_list[nodeIdA].push_back({nodeIdB, cost});
  89.         m_adj_list[nodeIdB].push_back({nodeIdA, cost});
  90.     }
  91.  
  92.     int addOrGetNode(std::string nodeName) {
  93.         auto nextId = lastIdGenerated + 1;
  94.         if (auto [iter, inserted] = m_node_to_id.insert({nodeName, nextId}); inserted) {
  95.             m_id_to_node[nextId] = nodeName;
  96.             lastIdGenerated++;
  97.             return nextId;
  98.         } else {
  99.             return iter->second;
  100.         }
  101.     }
  102.  
  103.     std::optional<int> getNodeId(std::string nodeName) const {
  104.         if (auto iter = m_node_to_id.find(nodeName); iter != m_node_to_id.end()) {
  105.             return iter->second;
  106.         }
  107.         return std::nullopt;
  108.     }
  109.  
  110.  
  111.     std::optional<std::string> getNodeName(int id) const {
  112.         if (auto iter = m_id_to_node.find(id); iter != m_id_to_node.end()) {
  113.             return iter->second;
  114.         }
  115.         return std::nullopt;
  116.     }
  117.  
  118.     void assertNodeExists(int nodeId) const {
  119.         if (m_id_to_node.find(nodeId) == m_id_to_node.end()) {
  120.             throw std::invalid_argument("city Id:" + std::to_string(nodeId) + " does not exist");
  121.         }
  122.     }
  123.  
  124.     int size() const {
  125.         return m_adj_list.size();
  126.     }
  127.  
  128.     const std::unordered_map<int, std::string>& getNodeIdLookup() const {
  129.         return m_id_to_node;
  130.     }
  131.  
  132.     const std::unordered_map<std::string, int>& getNodeNameLookup() const {
  133.         return m_node_to_id;
  134.     }
  135.  
  136. private:
  137.     std::unordered_map<int, std::string> m_id_to_node;
  138.     std::unordered_map<std::string, int> m_node_to_id;
  139.     int lastIdGenerated = 0;
  140.  
  141.     std::unordered_map<int, std::vector<OutdegreeGraphNode>> m_adj_list;
  142. };
  143.  
  144.  
  145. class DijsktraShortestPath {
  146. public:
  147.     DijsktraShortestPath(const AdjacencyList& adjacencyList, int nodeIdA, int nodeIdB)
  148.         : m_adjacencyList(adjacencyList), m_nodeIdA(nodeIdA), m_nodeIdB(nodeIdB) {}
  149.  
  150.     std::tuple<float, std::vector<OutdegreeGraphNode>> computePath() {
  151.         std::vector<OutdegreeGraphNode> result;
  152.  
  153.         auto [minCost, _] = computeCost();
  154.         auto node = m_nodeIdB;
  155.         int i = 0;
  156.         for (auto iter = m_predecessors.find(node); iter != m_predecessors.end(); iter = m_predecessors.find(node)) {
  157.             result.push_back({iter->second, m_minCosts[node] - m_minCosts[iter->second]});
  158.             node = iter->second;
  159.             if (i++ > 10000) {
  160.                 throw std::logic_error("WTF");
  161.             }
  162.         }
  163.         std::reverse(result.begin(), result.end());
  164.         return {minCost, result};
  165.     }
  166.  
  167.     std::tuple<float, bool> computeCost() {
  168.         m_minCosts.clear();
  169.         m_predecessors.clear();
  170.  
  171.         constexpr auto Infinity = std::numeric_limits<float>::max();
  172.         for (auto& [nodeId, _] : m_adjacencyList.getNodeIdLookup()) {
  173.             m_minCosts[nodeId] = Infinity;
  174.         }
  175.  
  176.         const auto cmp = [this](int nodeIdA, int nodeIdB){ return m_minCosts.at(nodeIdA) > m_minCosts.at(nodeIdB); };
  177.         std::priority_queue<int, std::vector<int>, decltype(cmp)> priorityQueue(cmp);
  178.  
  179.         m_minCosts[m_nodeIdA] = 0.0;
  180.         priorityQueue.push(m_nodeIdA);
  181.  
  182.         while(!priorityQueue.empty()) {
  183.             auto currentNodeId = priorityQueue.top();
  184.             priorityQueue.pop();
  185.             auto currentNodeIdTotalCost = m_minCosts[currentNodeId];
  186.  
  187.             for (auto [nodeId, cost] : m_adjacencyList.getOutboundNodes(currentNodeId)) {
  188.                 auto nodeIdTotalCost = m_minCosts.at(nodeId);
  189.                 if (cost < 0) {
  190.                     throw std::invalid_argument("Negative costs are not allowed");
  191.                 }
  192.                 if (nodeIdTotalCost > currentNodeIdTotalCost + cost) {
  193.                     m_minCosts[nodeId] = currentNodeIdTotalCost + cost;
  194.                     m_predecessors[nodeId] = currentNodeId;
  195.                     priorityQueue.push(nodeId);
  196.                 }
  197.             }
  198.             if (currentNodeId == m_nodeIdB) {
  199.                 break;
  200.             }
  201.         }
  202.  
  203.         if (auto minCost = m_minCosts[m_nodeIdB]; minCost != Infinity) {
  204.             return {minCost, true};
  205.         }
  206.         return {Infinity, false};
  207.     }
  208.  
  209.  
  210.  
  211.  
  212.  
  213. private:
  214.     const AdjacencyList& m_adjacencyList;
  215.     const int m_nodeIdA;
  216.     const int m_nodeIdB;
  217.  
  218.     std::unordered_map<int, float> m_minCosts;
  219.     std::unordered_map<int, int> m_predecessors;
  220. };
  221.  
  222.  
  223.  
  224.  
  225.  
  226. class MileageChartCSVToAdjacencyListCreator
  227. {
  228. public:
  229.     AdjacencyList create(std::vector<std::vector<std::string>>& csvTable) {
  230.         AdjacencyList adjacencyList;
  231.  
  232.         for(auto row = 1u; row < csvTable.size(); row++) {
  233.             auto rowNodeId = adjacencyList.addOrGetNode(csvTable[row][0]);
  234.             for(auto col = 1u; col < csvTable[row].size(); col++) {
  235.                 if(!csvTable[row][col].empty())  {
  236.                     auto colNodeId = adjacencyList.addOrGetNode(csvTable[0][col]);
  237.                     auto cost = std::stof(csvTable[row][col]);
  238.                     adjacencyList.connectNode(rowNodeId, colNodeId, cost);
  239.                 }
  240.             }
  241.         }
  242.  
  243.         return adjacencyList;
  244.     }
  245. };
  246.  
  247.  
  248.  
  249. int main() try {
  250.     CSVReader csv("NG.csv");
  251.     std::vector<std::vector<std::string>> m_csv = csv.getDataAsTable();
  252.  
  253.     MileageChartCSVToAdjacencyListCreator chart;
  254.     AdjacencyList adjList = chart.create(m_csv);
  255.  
  256.     std::string cityA = "Aba";
  257.     std::string cityB = "Bauchi";
  258.  
  259.     auto nodeIdA = adjList.getNodeId(cityA).value();
  260.     auto nodeIdB = adjList.getNodeId(cityB).value();
  261.  
  262.     DijsktraShortestPath dijkstra(adjList, nodeIdA, nodeIdB);
  263.     auto [minDistance, paths] = dijkstra.computePath();
  264.     if (!paths.empty()) {
  265.         std::cout << "Distance between " << std::quoted(cityA) << " and " << std::quoted(cityB) << " is " << minDistance << "km\n";
  266.         std::cout << "The paths to follow are: \n";
  267.         for (auto iter = paths.begin(); iter != paths.end(); std::advance(iter, 1)) {
  268.             auto nextCityId = std::next(iter) != paths.end() ? std::next(iter)->nodeId : nodeIdB;
  269.             auto [currCityId, distance] = *iter;
  270.             std::cout << "\t" << adjList.getNodeName(currCityId).value()
  271.                       << "   to   "
  272.                       << adjList.getNodeName(nextCityId).value()
  273.                       << " is " << distance << "\n";
  274.         }
  275.     } else {
  276.         std::cout << "Unfortunately, there is no road between " << std::quoted(cityA) << " and " << std::quoted(cityB) << "\n";
  277.     }
  278.  
  279. } catch (std::exception& e) {
  280.     std::cerr << "[FATAL exception]: " << e.what() << "\n";
  281. }
Add Comment
Please, Sign In to add comment