UNPKG

dist-javascript-algorithms-and-data-structures

Version:

Algorithms and data-structures implemented on JavaScript

108 lines (83 loc) 4.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = bfTravellingSalesman; function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * Get all possible paths * @param {GraphVertex} startVertex * @param {GraphVertex[][]} [paths] * @param {GraphVertex[]} [path] */ function findAllPaths(startVertex, paths = [], path = []) { // Clone path. const currentPath = [...path]; // Add startVertex to the path. currentPath.push(startVertex); // Generate visited set from path. const visitedSet = currentPath.reduce((accumulator, vertex) => { const updatedAccumulator = _objectSpread({}, accumulator); updatedAccumulator[vertex.getKey()] = vertex; return updatedAccumulator; }, {}); // Get all unvisited neighbors of startVertex. const unvisitedNeighbors = startVertex.getNeighbors().filter(neighbor => { return !visitedSet[neighbor.getKey()]; }); // If there no unvisited neighbors then treat current path as complete and save it. if (!unvisitedNeighbors.length) { paths.push(currentPath); return paths; } // Go through all the neighbors. for (let neighborIndex = 0; neighborIndex < unvisitedNeighbors.length; neighborIndex += 1) { const currentUnvisitedNeighbor = unvisitedNeighbors[neighborIndex]; findAllPaths(currentUnvisitedNeighbor, paths, currentPath); } return paths; } /** * @param {number[][]} adjacencyMatrix * @param {object} verticesIndices * @param {GraphVertex[]} cycle * @return {number} */ function getCycleWeight(adjacencyMatrix, verticesIndices, cycle) { let weight = 0; for (let cycleIndex = 1; cycleIndex < cycle.length; cycleIndex += 1) { const fromVertex = cycle[cycleIndex - 1]; const toVertex = cycle[cycleIndex]; const fromVertexIndex = verticesIndices[fromVertex.getKey()]; const toVertexIndex = verticesIndices[toVertex.getKey()]; weight += adjacencyMatrix[fromVertexIndex][toVertexIndex]; } return weight; } /** * BRUTE FORCE approach to solve Traveling Salesman Problem. * * @param {Graph} graph * @return {GraphVertex[]} */ function bfTravellingSalesman(graph) { // Pick starting point from where we will traverse the graph. const startVertex = graph.getAllVertices()[0]; // BRUTE FORCE. // Generate all possible paths from startVertex. const allPossiblePaths = findAllPaths(startVertex); // Filter out paths that are not cycles. const allPossibleCycles = allPossiblePaths.filter(path => { /** @var {GraphVertex} */ const lastVertex = path[path.length - 1]; const lastVertexNeighbors = lastVertex.getNeighbors(); return lastVertexNeighbors.includes(startVertex); }); // Go through all possible cycles and pick the one with minimum overall tour weight. const adjacencyMatrix = graph.getAdjacencyMatrix(); const verticesIndices = graph.getVerticesIndices(); let salesmanPath = []; let salesmanPathWeight = null; for (let cycleIndex = 0; cycleIndex < allPossibleCycles.length; cycleIndex += 1) { const currentCycle = allPossibleCycles[cycleIndex]; const currentCycleWeight = getCycleWeight(adjacencyMatrix, verticesIndices, currentCycle); // If current cycle weight is smaller then previous ones treat current cycle as most optimal. if (salesmanPathWeight === null || currentCycleWeight < salesmanPathWeight) { salesmanPath = currentCycle; salesmanPathWeight = currentCycleWeight; } } // Return the solution. return salesmanPath; }