diff --git a/src/js/hamiltonPath.js b/src/js/hamiltonPath.js deleted file mode 100644 index a830633..0000000 --- a/src/js/hamiltonPath.js +++ /dev/null @@ -1,979 +0,0 @@ -class GraphEdge { - /** - * @param {GraphVertex} startVertex - * @param {GraphVertex} endVertex - * @param {number} [weight=1] - */ - constructor(startVertex, endVertex, weight = 0) { - this.startVertex = startVertex; - this.endVertex = endVertex; - this.weight = weight; - } - - /** - * @return {string} - */ - getKey() { - const startVertexKey = this.startVertex.getKey(); - const endVertexKey = this.endVertex.getKey(); - - return `${startVertexKey}_${endVertexKey}`; - } - - /** - * @return {GraphEdge} - */ - reverse() { - const tmp = this.startVertex; - this.startVertex = this.endVertex; - this.endVertex = tmp; - - return this; - } - - /** - * @return {string} - */ - toString() { - return this.getKey(); - } -} - -class Comparator { - /** - * Constructor. - * @param {function(a: *, b: *)} [compareFunction] - It may be custom compare function that, let's - * say may compare custom objects together. - */ - constructor(compareFunction) { - this.compare = compareFunction || Comparator.defaultCompareFunction; - } - - /** - * Default comparison function. It just assumes that "a" and "b" are strings or numbers. - * @param {(string|number)} a - * @param {(string|number)} b - * @returns {number} - */ - static defaultCompareFunction(a, b) { - if (a === b) { - return 0; - } - - return a < b ? -1 : 1; - } - - /** - * Checks if two variables are equal. - * @param {*} a - * @param {*} b - * @return {boolean} - */ - equal(a, b) { - return this.compare(a, b) === 0; - } - - /** - * Checks if variable "a" is less than "b". - * @param {*} a - * @param {*} b - * @return {boolean} - */ - lessThan(a, b) { - return this.compare(a, b) < 0; - } - - /** - * Checks if variable "a" is greater than "b". - * @param {*} a - * @param {*} b - * @return {boolean} - */ - greaterThan(a, b) { - return this.compare(a, b) > 0; - } - - /** - * Checks if variable "a" is less than or equal to "b". - * @param {*} a - * @param {*} b - * @return {boolean} - */ - lessThanOrEqual(a, b) { - return this.lessThan(a, b) || this.equal(a, b); - } - - /** - * Checks if variable "a" is greater than or equal to "b". - * @param {*} a - * @param {*} b - * @return {boolean} - */ - greaterThanOrEqual(a, b) { - return this.greaterThan(a, b) || this.equal(a, b); - } - - /** - * Reverses the comparison order. - */ - reverse() { - const compareOriginal = this.compare; - this.compare = (a, b) => compareOriginal(b, a); - } -} - -class LinkedListNode { - constructor(value, next = null) { - this.value = value; - this.next = next; - } - - toString(callback) { - return callback ? callback(this.value) : `${this.value}`; - } -} - -class LinkedList { - /** - * @param {Function} [comparatorFunction] - */ - constructor(comparatorFunction) { - /** @var LinkedListNode */ - this.head = null; - - /** @var LinkedListNode */ - this.tail = null; - - this.compare = new Comparator(comparatorFunction); - } - - /** - * @param {*} value - * @return {LinkedList} - */ - prepend(value) { - // Make new node to be a head. - const newNode = new LinkedListNode(value, this.head); - this.head = newNode; - - // If there is no tail yet let's make new node a tail. - if (!this.tail) { - this.tail = newNode; - } - - return this; - } - - /** - * @param {*} value - * @return {LinkedList} - */ - append(value) { - const newNode = new LinkedListNode(value); - - // If there is no head yet let's make new node a head. - if (!this.head) { - this.head = newNode; - this.tail = newNode; - - return this; - } - - // Attach new node to the end of linked list. - this.tail.next = newNode; - this.tail = newNode; - - return this; - } - - /** - * @param {*} value - * @param {number} index - * @return {LinkedList} - */ - insert(value, rawIndex) { - const index = rawIndex < 0 ? 0 : rawIndex; - if (index === 0) { - this.prepend(value); - } else { - let count = 1; - let currentNode = this.head; - const newNode = new LinkedListNode(value); - while (currentNode) { - if (count === index) break; - currentNode = currentNode.next; - count += 1; - } - if (currentNode) { - newNode.next = currentNode.next; - currentNode.next = newNode; - } else { - if (this.tail) { - this.tail.next = newNode; - this.tail = newNode; - } else { - this.head = newNode; - this.tail = newNode; - } - } - } - return this; - } - - /** - * @param {*} value - * @return {LinkedListNode} - */ - delete(value) { - if (!this.head) { - return null; - } - - let deletedNode = null; - - // If the head must be deleted then make next node that is different - // from the head to be a new head. - while (this.head && this.compare.equal(this.head.value, value)) { - deletedNode = this.head; - this.head = this.head.next; - } - - let currentNode = this.head; - - if (currentNode !== null) { - // If next node must be deleted then make next node to be a next next one. - while (currentNode.next) { - if (this.compare.equal(currentNode.next.value, value)) { - deletedNode = currentNode.next; - currentNode.next = currentNode.next.next; - } else { - currentNode = currentNode.next; - } - } - } - - // Check if tail must be deleted. - if (this.compare.equal(this.tail.value, value)) { - this.tail = currentNode; - } - - return deletedNode; - } - - /** - * @param {Object} findParams - * @param {*} findParams.value - * @param {function} [findParams.callback] - * @return {LinkedListNode} - */ - find({ - value = undefined, - callback = undefined - }) { - if (!this.head) { - return null; - } - - let currentNode = this.head; - - while (currentNode) { - // If callback is specified then try to find node by callback. - if (callback && callback(currentNode.value)) { - return currentNode; - } - - // If value is specified then try to compare by value.. - if (value !== undefined && this.compare.equal(currentNode.value, value)) { - return currentNode; - } - - currentNode = currentNode.next; - } - - return null; - } - - /** - * @return {LinkedListNode} - */ - deleteTail() { - const deletedTail = this.tail; - - if (this.head === this.tail) { - // There is only one node in linked list. - this.head = null; - this.tail = null; - - return deletedTail; - } - - // If there are many nodes in linked list... - - // Rewind to the last node and delete "next" link for the node before the last one. - let currentNode = this.head; - while (currentNode.next) { - if (!currentNode.next.next) { - currentNode.next = null; - } else { - currentNode = currentNode.next; - } - } - - this.tail = currentNode; - - return deletedTail; - } - - /** - * @return {LinkedListNode} - */ - deleteHead() { - if (!this.head) { - return null; - } - - const deletedHead = this.head; - - if (this.head.next) { - this.head = this.head.next; - } else { - this.head = null; - this.tail = null; - } - - return deletedHead; - } - - /** - * @param {*[]} values - Array of values that need to be converted to linked list. - * @return {LinkedList} - */ - fromArray(values) { - values.forEach((value) => this.append(value)); - - return this; - } - - /** - * @return {LinkedListNode[]} - */ - toArray() { - const nodes = []; - - let currentNode = this.head; - while (currentNode) { - nodes.push(currentNode); - currentNode = currentNode.next; - } - - return nodes; - } - - /** - * @param {function} [callback] - * @return {string} - */ - toString(callback) { - return this.toArray().map((node) => node.toString(callback)).toString(); - } - - /** - * Reverse a linked list. - * @returns {LinkedList} - */ - reverse() { - let currNode = this.head; - let prevNode = null; - let nextNode = null; - - while (currNode) { - // Store next node. - nextNode = currNode.next; - - // Change next node of the current node so it would link to previous node. - currNode.next = prevNode; - - // Move prevNode and currNode nodes one step forward. - prevNode = currNode; - currNode = nextNode; - } - - // Reset head and tail. - this.tail = this.head; - this.head = prevNode; - - return this; - } -} - -class GraphVertex { - /** - * @param {*} value - */ - constructor(value) { - if (value === undefined) { - throw new Error('Graph vertex must have a value'); - } - - /** - * @param {GraphEdge} edgeA - * @param {GraphEdge} edgeB - */ - const edgeComparator = (edgeA, edgeB) => { - if (edgeA.getKey() === edgeB.getKey()) { - return 0; - } - - return edgeA.getKey() < edgeB.getKey() ? -1 : 1; - }; - - // Normally you would store string value like vertex name. - // But generally it may be any object as well - this.value = value; - this.edges = new LinkedList(edgeComparator); - } - - /** - * @param {GraphEdge} edge - * @returns {GraphVertex} - */ - addEdge(edge) { - this.edges.append(edge); - - return this; - } - - /** - * @param {GraphEdge} edge - */ - deleteEdge(edge) { - this.edges.delete(edge); - } - - /** - * @returns {GraphVertex[]} - */ - getNeighbors() { - const edges = this.edges.toArray(); - - /** @param {LinkedListNode} node */ - const neighborsConverter = (node) => { - return node.value.startVertex === this ? node.value.endVertex : node.value.startVertex; - }; - - // Return either start or end vertex. - // For undirected graphs it is possible that current vertex will be the end one. - return edges.map(neighborsConverter); - } - - /** - * @return {GraphEdge[]} - */ - getEdges() { - return this.edges.toArray().map((linkedListNode) => linkedListNode.value); - } - - /** - * @return {number} - */ - getDegree() { - return this.edges.toArray().length; - } - - /** - * @param {GraphEdge} requiredEdge - * @returns {boolean} - */ - hasEdge(requiredEdge) { - const edgeNode = this.edges.find({ - callback: (edge) => edge === requiredEdge, - }); - - return !!edgeNode; - } - - /** - * @param {GraphVertex} vertex - * @returns {boolean} - */ - hasNeighbor(vertex) { - const vertexNode = this.edges.find({ - callback: (edge) => edge.startVertex === vertex || edge.endVertex === vertex, - }); - - return !!vertexNode; - } - - /** - * @param {GraphVertex} vertex - * @returns {(GraphEdge|null)} - */ - findEdge(vertex) { - const edgeFinder = (edge) => { - return edge.startVertex === vertex || edge.endVertex === vertex; - }; - - const edge = this.edges.find({ - callback: edgeFinder - }); - - return edge ? edge.value : null; - } - - /** - * @returns {string} - */ - getKey() { - return this.value; - } - - /** - * @return {GraphVertex} - */ - deleteAllEdges() { - this.getEdges().forEach((edge) => this.deleteEdge(edge)); - - return this; - } - - /** - * @param {function} [callback] - * @returns {string} - */ - toString(callback) { - return callback ? callback(this.value) : `${this.value}`; - } -} - -class Graph { - /** - * @param {boolean} isDirected - */ - constructor(isDirected = false) { - this.vertices = {}; - this.edges = {}; - this.isDirected = isDirected; - } - - /** - * @param {GraphVertex} newVertex - * @returns {Graph} - */ - addVertex(newVertex) { - this.vertices[newVertex.getKey()] = newVertex; - - return this; - } - - /** - * @param {string} vertexKey - * @returns GraphVertex - */ - getVertexByKey(vertexKey) { - return this.vertices[vertexKey]; - } - - /** - * @param {GraphVertex} vertex - * @returns {GraphVertex[]} - */ - getNeighbors(vertex) { - return vertex.getNeighbors(); - } - - /** - * @return {GraphVertex[]} - */ - getAllVertices() { - return Object.values(this.vertices); - } - - /** - * @return {GraphEdge[]} - */ - getAllEdges() { - return Object.values(this.edges); - } - - /** - * @param {GraphEdge} edge - * @returns {Graph} - */ - addEdge(edge) { - // Try to find and end start vertices. - let startVertex = this.getVertexByKey(edge.startVertex.getKey()); - let endVertex = this.getVertexByKey(edge.endVertex.getKey()); - - // Insert start vertex if it wasn't inserted. - if (!startVertex) { - this.addVertex(edge.startVertex); - startVertex = this.getVertexByKey(edge.startVertex.getKey()); - } - - // Insert end vertex if it wasn't inserted. - if (!endVertex) { - this.addVertex(edge.endVertex); - endVertex = this.getVertexByKey(edge.endVertex.getKey()); - } - - // Check if edge has been already added. - if (this.edges[edge.getKey()]) { - throw new Error('Edge has already been added before'); - } else { - this.edges[edge.getKey()] = edge; - } - - // Add edge to the vertices. - if (this.isDirected) { - // If graph IS directed then add the edge only to start vertex. - startVertex.addEdge(edge); - } else { - // If graph ISN'T directed then add the edge to both vertices. - startVertex.addEdge(edge); - endVertex.addEdge(edge); - } - - return this; - } - - /** - * @param {GraphEdge} edge - */ - deleteEdge(edge) { - // Delete edge from the list of edges. - if (this.edges[edge.getKey()]) { - delete this.edges[edge.getKey()]; - } else { - throw new Error('Edge not found in graph'); - } - - // Try to find and end start vertices and delete edge from them. - const startVertex = this.getVertexByKey(edge.startVertex.getKey()); - const endVertex = this.getVertexByKey(edge.endVertex.getKey()); - - startVertex.deleteEdge(edge); - endVertex.deleteEdge(edge); - } - - /** - * @param {GraphVertex} startVertex - * @param {GraphVertex} endVertex - * @return {(GraphEdge|null)} - */ - findEdge(startVertex, endVertex) { - const vertex = this.getVertexByKey(startVertex.getKey()); - - if (!vertex) { - return null; - } - - return vertex.findEdge(endVertex); - } - - /** - * @return {number} - */ - getWeight() { - return this.getAllEdges().reduce((weight, graphEdge) => { - return weight + graphEdge.weight; - }, 0); - } - - /** - * Reverse all the edges in directed graph. - * @return {Graph} - */ - reverse() { - /** @param {GraphEdge} edge */ - this.getAllEdges().forEach((edge) => { - // Delete straight edge from graph and from vertices. - this.deleteEdge(edge); - - // Reverse the edge. - edge.reverse(); - - // Add reversed edge back to the graph and its vertices. - this.addEdge(edge); - }); - - return this; - } - - /** - * @return {object} - */ - getVerticesIndices() { - const verticesIndices = {}; - this.getAllVertices().forEach((vertex, index) => { - verticesIndices[vertex.getKey()] = index; - }); - - return verticesIndices; - } - - /** - * @return {*[][]} - */ - getAdjacencyMatrix() { - const vertices = this.getAllVertices(); - const verticesIndices = this.getVerticesIndices(); - - // Init matrix with infinities meaning that there is no ways of - // getting from one vertex to another yet. - const adjacencyMatrix = Array(vertices.length).fill(null).map(() => { - return Array(vertices.length).fill(Infinity); - }); - - // Fill the columns. - vertices.forEach((vertex, vertexIndex) => { - vertex.getNeighbors().forEach((neighbor) => { - const neighborIndex = verticesIndices[neighbor.getKey()]; - adjacencyMatrix[vertexIndex][neighborIndex] = this.findEdge(vertex, neighbor).weight; - }); - }); - - return adjacencyMatrix; - } - - /** - * @return {string} - */ - toString() { - return Object.keys(this.vertices).toString(); - } -} - -/** - * @param {number[][]} adjacencyMatrix - * @param {object} verticesIndices - * @param {GraphVertex[]} cycle - * @param {GraphVertex} vertexCandidate - * @return {boolean} - */ -function isSafe(adjacencyMatrix, verticesIndices, cycle, vertexCandidate) { - const endVertex = cycle[cycle.length - 1]; - - // Get end and candidate vertices indices in adjacency matrix. - const candidateVertexAdjacencyIndex = verticesIndices[vertexCandidate.getKey()]; - const endVertexAdjacencyIndex = verticesIndices[endVertex.getKey()]; - - // Check if last vertex in the path and candidate vertex are adjacent. - if (adjacencyMatrix[endVertexAdjacencyIndex][candidateVertexAdjacencyIndex] === Infinity) { - return false; - } - - // Check if vertexCandidate is being added to the path for the first time. - const candidateDuplicate = cycle.find((vertex) => vertex.getKey() === vertexCandidate.getKey()); - - return !candidateDuplicate; -} - -/** - * @param {number[][]} adjacencyMatrix - * @param {object} verticesIndices - * @param {GraphVertex[]} cycle - * @return {boolean} - */ -function isCycle(adjacencyMatrix, verticesIndices, cycle) { - // Check if first and last vertices in hamiltonian path are adjacent. - - // Get start and end vertices from the path. - const startVertex = cycle[0]; - const endVertex = cycle[cycle.length - 1]; - - // Get start/end vertices indices in adjacency matrix. - const startVertexAdjacencyIndex = verticesIndices[startVertex.getKey()]; - const endVertexAdjacencyIndex = verticesIndices[endVertex.getKey()]; - - // Check if we can go from end vertex to the start one. - return adjacencyMatrix[endVertexAdjacencyIndex][startVertexAdjacencyIndex] !== Infinity; -} - -/** - * @param {number[][]} adjacencyMatrix - * @param {GraphVertex[]} vertices - * @param {object} verticesIndices - * @param {GraphVertex[][]} cycles - * @param {GraphVertex[]} cycle - */ -function hamiltonianCycleRecursive({ - adjacencyMatrix, - vertices, - verticesIndices, - cycles, - cycle, -}) { - // Clone cycle in order to prevent it from modification by other DFS branches. - const currentCycle = [...cycle].map((vertex) => new GraphVertex(vertex.value)); - - if (vertices.length === currentCycle.length) { - // Hamiltonian path is found. - // Now we need to check if it is cycle or not. - if (isCycle(adjacencyMatrix, verticesIndices, currentCycle)) { - // Another solution has been found. Save it. - cycles.push(currentCycle); - } - return; - } - - for (let vertexIndex = 0; vertexIndex < vertices.length; vertexIndex += 1) { - // Get vertex candidate that we will try to put into next path step and see if it fits. - const vertexCandidate = vertices[vertexIndex]; - - // Check if it is safe to put vertex candidate to cycle. - if (isSafe(adjacencyMatrix, verticesIndices, currentCycle, vertexCandidate)) { - // Add candidate vertex to cycle path. - currentCycle.push(vertexCandidate); - - // Try to find other vertices in cycle. - hamiltonianCycleRecursive({ - adjacencyMatrix, - vertices, - verticesIndices, - cycles, - cycle: currentCycle, - }); - - // BACKTRACKING. - // Remove candidate vertex from cycle path in order to try another one. - currentCycle.pop(); - } - } -} - -/** - * @param {Graph} graph - * @return {GraphVertex[][]} - */ -function hamiltonianCycle(graph) { - // Gather some information about the graph that we will need to during - // the problem solving. - const verticesIndices = graph.getVerticesIndices(); - const adjacencyMatrix = graph.getAdjacencyMatrix(); - const vertices = graph.getAllVertices(); - - // Define start vertex. We will always pick the first one - // this it doesn't matter which vertex to pick in a cycle. - // Every vertex is in a cycle so we can start from any of them. - const startVertex = vertices[0]; - - // Init cycles array that will hold all solutions. - const cycles = []; - - // Init cycle array that will hold current cycle path. - const cycle = [startVertex]; - - // Try to find cycles recursively in Depth First Search order. - hamiltonianCycleRecursive({ - adjacencyMatrix, - vertices, - verticesIndices, - cycles, - cycle, - }); - - // Return found cycles. - return cycles; -} - - -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 = { - ...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; -} \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js index 30b8040..e2ea35e 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -65,11 +65,11 @@ class WaypointsData { result.push(point); } }); - + // Сортировка результата в порядке переданных индексов let ordered = []; - indexes.forEach((ind)=>{ - const index = result.findIndex(p=>p.id == ind); + indexes.forEach((ind) => { + const index = result.findIndex(p => p.id == ind); ordered.push(result[index]); result.slice(index, 1); }); @@ -80,7 +80,8 @@ class WaypointsData { // Точки для карты const waypoints = new WaypointsData(); const markerUrl = './img/map-marker.png'; - +// Максимальное число путей для перебора +const max_ways_count = 200000; let regionPointsWidget; let selectedPointsWidget; @@ -259,11 +260,17 @@ function calculateDistance(lat1, lon1, lat2, lon2) { return d; } -function shuffle(array) { +function shuffle(array, fix_start_point = true, fix_end_point = true) { let currentIndex = array.length; - + if (fix_end_point) { + currentIndex = array.length - 1; + } + let start_index = 0; + if (fix_start_point) { + start_index = 1; + } // While there remain elements to shuffle... - while (currentIndex != 0) { + while (currentIndex != start_index) { // Pick a remaining element... let randomIndex = Math.floor(Math.random() * currentIndex); @@ -286,8 +293,41 @@ function factorial(n) { } -function stochasticPathFind(array) { - +function stochasticPathFind(array, fix_start_point = true, fix_end_point = true) { + let variants_count = array.length; + if (fix_start_point) { + variants_count--; + } + if (fix_end_point) { + variants_count--; + } + let max_ways = factorial(array.length); + if (max_ways > max_ways_count) { + max_ways = max_ways_count; + } + var min_distance = Number.MAX_VALUE; + var min_distance_waypoint = []; + for (let i = 0; i < max_ways; i++) { + shuffle(array, fix_start_point, fix_end_point); + let prevPoint = null; + let distance = 0; + for (let ind in array) { + let point = array[ind]; + if (prevPoint != null) { + const d = calculateDistance(point.location[0], point.location[1], prevPoint.location[0], prevPoint.location[1]) / 1000.0; + distance += d; + } + prevPoint = point; + } + if (distance < min_distance) { + min_distance = distance; + min_distance_waypoint = []; + for (let ind in array) { + min_distance_waypoint.push(array[ind].id); + } + } + } + return min_distance_waypoint; } // Вычисление оптимального маршрута по начальной и конечной точке @@ -310,36 +350,10 @@ function recalculateWaypoints(waypoints) { // Три точки, старт, стоп и промежуточная выводим без расчета showTravelPoints([waypoints[0].id, waypoints[1].id, waypoints[2].id]); - } else if (waypoints.length <= 9) { - - // Если точек от 4 до 9, используем алгоритм коммивояжера - // Создаем словарь с вершинами - let vertexes = {}; - for (const ind in waypoints) { - vertexes[ind] = new GraphVertex(ind); - } - // Формируем граф - const graph = new Graph(); - const len = waypoints.length; - for (let i = 0; i < len - 1; i++) { - for (let j = i + 1; j < len; j++) { - const point1 = waypoints[i]; - const point2 = waypoints[j]; - const d = calculateDistance(point1.location[0], point1.location[1], point2.location[0], point2.location[1]); - graph.addEdge(new GraphEdge(vertexes[i], vertexes[j], d)); - } - } - // Решение задачи коммивояжера - const salesmanPath = bfTravellingSalesman(graph); - let indexes = []; - for (var ind in salesmanPath) { - indexes.push(waypoints[salesmanPath[ind].value].id); - } - showTravelPoints(indexes); } else { - // Если точек от 9, близкие к np алгоритмам расчеты слишком затратные // применяем лучшие поиск из 200.000 лучших вариантов (9! = 362880, берем такого порядка число чтобы не грузить браузер расчетами) - + let path_indexes = stochasticPathFind(waypoints, true, true); + showTravelPoints(path_indexes); } } }