diff --git a/DSA/Graphs/Maze.js b/DSA/Graphs/Maze.js new file mode 100644 index 0000000..d33ef15 --- /dev/null +++ b/DSA/Graphs/Maze.js @@ -0,0 +1,209 @@ +const maze = [ + ['S', 0, 1, 0, 1], + [0, 1, 0, 0, 0], + [0, 0, 1, 1, 0], + [1, 0, 0, 0, 'E'], +]; + + +function solveMazeDFS(maze) { + const rows = maze.length; + const cols = maze[0].length; + + function dfs(x, y) { + if (x < 0 || x >= rows || y < 0 || y >= cols || maze[x][y] === 1) { + return false; + } + + if (maze[x][y] === 'E') { + return true; // Reached the end of the maze + } + + maze[x][y] = 1; // Mark as visited + + // Explore in all four directions (up, down, left, right) + if (dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1)) { + return true; + } + + maze[x][y] = 0; // Mark as unvisited if no path was found + return false; + } + + // Find the start point + let startX, startY; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (maze[i][j] === 'S') { + startX = i; + startY = j; + break; + } + } + } + + // Call DFS from the start point + if (dfs(startX, startY)) { + return "Maze is solvable."; + } else { + return "Maze has no solution."; + } +} + +console.log(solveMazeDFS(maze)); + + + + +function solveMazeBFS(maze) { + const rows = maze.length; + const cols = maze[0].length; + const queue = []; + + // Find the start point + let startX, startY; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (maze[i][j] === 'S') { + startX = i; + startY = j; + break; + } + } + } + + // Define possible moves (up, down, left, right) + const moves = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + queue.push([startX, startY]); + + while (queue.length > 0) { + const [x, y] = queue.shift(); + + if (maze[x][y] === 'E') { + return "Maze is solvable."; + } + + maze[x][y] = 1; // Mark as visited + + for (const [dx, dy] of moves) { + const newX = x + dx; + const newY = y + dy; + + if (newX >= 0 && newX < rows && newY >= 0 && newY < cols && maze[newX][newY] === 0) { + queue.push([newX, newY]); + } + } + } + + return "Maze has no solution."; +} + +console.log(solveMazeBFS(maze)); + + +class PriorityQueue { + constructor() { + this.elements = []; + } + + enqueue(element, priority) { + this.elements.push({ element, priority }); + this.elements.sort((a, b) => a.priority - b.priority); + } + + dequeue() { + return this.elements.shift().element; + } + + isEmpty() { + return this.elements.length === 0; + } +} + +function heuristic(x1, y1, x2, y2) { + // A simple heuristic function (Manhattan distance) + return Math.abs(x1 - x2) + Math.abs(y1 - y2); +} + +function solveMazeAStar(maze) { + const rows = maze.length; + const cols = maze[0].length; + + // Find the start and end points + let startX, startY, endX, endY; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (maze[i][j] === 'S') { + startX = i; + startY = j; + } else if (maze[i][j] === 'E') { + endX = i; + endY = j; + } + } + } + + const openSet = new PriorityQueue(); + openSet.enqueue({ x: startX, y: startY, cost: 0 }, 0); + + const cameFrom = {}; + const gScore = {}; + + gScore[`${startX}-${startY}`] = 0; + + while (!openSet.isEmpty()) { + const current = openSet.dequeue(); + const { x, y } = current; + + if (x === endX && y === endY) { + // Reconstruct the path + const path = []; + let currentNode = { x: endX, y: endY }; + while (currentNode) { + path.unshift(currentNode); + currentNode = cameFrom[`${currentNode.x}-${currentNode.y}`]; + } + return path; + } + + for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) { + const newX = x + dx; + const newY = y + dy; + + if ( + newX >= 0 && + newX < rows && + newY >= 0 && + newY < cols && + maze[newX][newY] !== 1 + ) { + const tentativeGScore = gScore[`${x}-${y}`] + 1; + + if ( + !gScore[`${newX}-${newY}`] || + tentativeGScore < gScore[`${newX}-${newY}`] + ) { + cameFrom[`${newX}-${newY}`] = { x, y }; + gScore[`${newX}-${newY}`] = tentativeGScore; + const fScore = + tentativeGScore + heuristic(newX, newY, endX, endY); + openSet.enqueue({ x: newX, y: newY, cost: fScore }, fScore); + } + } + } + } + + return "Maze has no solution."; +} + +const path = solveMazeAStar(maze); + +if (path !== "Maze has no solution.") { + console.log("Path found:"); + path.forEach((cell, index) => { + console.log(`Step ${index + 1}: (${cell.x}, ${cell.y})`); + }); +} else { + console.log("Maze has no solution."); +} diff --git a/DSA/Graphs/Travelling Salesman Problem/AntColonyOptimization.js b/DSA/Graphs/Travelling Salesman Problem/AntColonyOptimization.js new file mode 100644 index 0000000..3096252 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/AntColonyOptimization.js @@ -0,0 +1,101 @@ +function antColonyOptimizationTSP(cities, distances, numAnts, maxIterations, pheromoneEvaporationRate, alpha, beta) { + const numCities = cities.length; + const initialPheromoneLevel = 1 / (numCities * numCities); + let pheromoneMatrix = Array.from({ length: numCities }, () => + Array(numCities).fill(initialPheromoneLevel) + ); + let bestOrder; + let bestDistance = Infinity; + + for (let iteration = 0; iteration < maxIterations; iteration++) { + const antPaths = []; + for (let ant = 0; ant < numAnts; ant++) { + const path = constructAntPath(pheromoneMatrix, distances, alpha, beta); + antPaths.push(path); + const pathDistance = calculateTotalDistance(path, distances); + if (pathDistance < bestDistance) { + bestOrder = path; + bestDistance = pathDistance; + } + } + + updatePheromoneMatrix(pheromoneMatrix, antPaths, pheromoneEvaporationRate); + } + + return { order: bestOrder, distance: bestDistance }; +} + +function constructAntPath(pheromoneMatrix, distances, alpha, beta) { + const numCities = pheromoneMatrix.length; + const startCity = Math.floor(Math.random() * numCities); + let currentCity = startCity; + const path = [startCity]; + const unvisitedCities = new Set([...Array(numCities).keys()].filter((i) => i !== startCity)); + + while (unvisitedCities.size > 0) { + const nextCity = chooseNextCity(currentCity, unvisitedCities, pheromoneMatrix, distances, alpha, beta); + path.push(nextCity); + unvisitedCities.delete(nextCity); + currentCity = nextCity; + } + + return path; +} + +function chooseNextCity(currentCity, unvisitedCities, pheromoneMatrix, distances, alpha, beta) { + const pheromoneLevels = []; + const totalProbability = [...unvisitedCities].reduce((sum, city) => { + const pheromone = pheromoneMatrix[currentCity][city]; + const distance = distances[currentCity][city]; + const probability = Math.pow(pheromone, alpha) * Math.pow(1 / distance, beta); + pheromoneLevels.push(probability); + return sum + probability; + }, 0); + + const randomValue = Math.random() * totalProbability; + let accumulatedProbability = 0; + + for (let i = 0; i < pheromoneLevels.length; i++) { + accumulatedProbability += pheromoneLevels[i]; + if (accumulatedProbability >= randomValue) { + return [...unvisitedCities][i]; + } + } + + return [...unvisitedCities][0]; // Fallback in case of numerical instability +} + +function updatePheromoneMatrix(pheromoneMatrix, antPaths, pheromoneEvaporationRate) { + const numCities = pheromoneMatrix.length; + + // Evaporate pheromone + for (let i = 0; i < numCities; i++) { + for (let j = 0; j < numCities; j++) { + pheromoneMatrix[i][j] *= (1 - pheromoneEvaporationRate); + } + } + + // Deposit pheromone based on ant paths + for (const path of antPaths) { + const pathDistance = calculateTotalDistance(path, distances); + for (let i = 0; i < path.length - 1; i++) { + const fromCity = path[i]; + const toCity = path[i + 1]; + pheromoneMatrix[fromCity][toCity] += 1 / pathDistance; + pheromoneMatrix[toCity][fromCity] += 1 / pathDistance; + } + } +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = antColonyOptimizationTSP(cities, distances, 10, 100, 0.5, 1.0, 2.0); +console.log("Ant Colony Optimization Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Ant Colony Optimization Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/BranchAndBound.js b/DSA/Graphs/Travelling Salesman Problem/BranchAndBound.js new file mode 100644 index 0000000..68e764e --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/BranchAndBound.js @@ -0,0 +1,50 @@ +function tspBranchAndBound(cities, distances) { + const n = cities.length; + const visited = new Array(n).fill(false); + visited[0] = true; + const initialPath = [0]; + const { order, distance } = branchAndBoundHelper(0, initialPath, 0, Infinity); + + function branchAndBoundHelper(currentCity, path, currentDistance, bestDistance) { + if (path.length === n) { + return { order: path, distance: currentDistance + distances[currentCity][0] }; + } + + let minDistance = bestDistance; + let minOrder = []; + + for (let nextCity = 0; nextCity < n; nextCity++) { + if (!visited[nextCity]) { + visited[nextCity] = true; + const newPath = [...path, nextCity]; + const newDistance = currentDistance + distances[currentCity][nextCity]; + + if (newDistance < minDistance) { + const result = branchAndBoundHelper(nextCity, newPath, newDistance, minDistance); + if (result.distance < minDistance) { + minDistance = result.distance; + minOrder = result.order; + } + } + + visited[nextCity] = false; + } + } + + return { order: minOrder, distance: minDistance }; + } + + return { order, distance }; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = tspBranchAndBound(cities, distances); +console.log("Branch and Bound Optimal Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Branch and Bound Optimal Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/BruteForce.js b/DSA/Graphs/Travelling Salesman Problem/BruteForce.js new file mode 100644 index 0000000..fdba98c --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/BruteForce.js @@ -0,0 +1,45 @@ +function permute(arr) { + if (arr.length === 0) return [[]]; + const [first, ...rest] = arr; + const permutationsWithoutFirst = permute(rest); + return permutationsWithoutFirst.flatMap((perm) => + perm.map((_, idx) => [...perm.slice(0, idx), first, ...perm.slice(idx)]) + ); +} + +function calculateTotalDistance(order, distances) { + let totalDistance = 0; + for (let i = 0; i < order.length - 1; i++) { + totalDistance += distances[order[i]][order[i + 1]]; + } + return totalDistance; +} + +function bruteForceTSP(cities, distances) { + const cityIndices = Array.from({ length: cities.length }, (_, i) => i); + const permutations = permute(cityIndices); + let minDistance = Infinity; + let bestOrder = []; + + for (const perm of permutations) { + const distance = calculateTotalDistance(perm, distances); + if (distance < minDistance) { + minDistance = distance; + bestOrder = perm; + } + } + + return { order: bestOrder, distance: minDistance }; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = bruteForceTSP(cities, distances); +console.log("Optimal Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Optimal Distance:", result.distance); \ No newline at end of file diff --git a/DSA/Graphs/Travelling Salesman Problem/ChristofidesAlgorithm.js b/DSA/Graphs/Travelling Salesman Problem/ChristofidesAlgorithm.js new file mode 100644 index 0000000..1ea53c3 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/ChristofidesAlgorithm.js @@ -0,0 +1,127 @@ +function minimumSpanningTree(graph) { + const numVertices = graph.length; + const parent = new Array(numVertices).fill(-1); + const key = new Array(numVertices).fill(Infinity); + const inMST = new Array(numVertices).fill(false); + + key[0] = 0; + + for (let count = 0; count < numVertices - 1; count++) { + const u = minKey(key, inMST); + inMST[u] = true; + + for (let v = 0; v < numVertices; v++) { + if (graph[u][v] && !inMST[v] && graph[u][v] < key[v]) { + parent[v] = u; + key[v] = graph[u][v]; + } + } + } + + return parent; +} + +function minKey(key, inMST) { + const numVertices = key.length; + let min = Infinity; + let minIndex = -1; + + for (let v = 0; v < numVertices; v++) { + if (!inMST[v] && key[v] < min) { + min = key[v]; + minIndex = v; + } + } + + return minIndex; +} + +function minimumWeightPerfectMatching(graph, parent) { + const numVertices = graph.length; + const edges = []; + + for (let i = 1; i < numVertices; i++) { + edges.push([i, parent[i]]); + } + + const matching = []; + + const visited = new Array(numVertices).fill(false); + + for (const [u, v] of edges) { + if (!visited[u] && !visited[v]) { + matching.push([u, v]); + visited[u] = visited[v] = true; + } + } + + return matching; +} + +function eulerianTour(graph, startVertex) { + const tour = []; + const stack = [startVertex]; + while (stack.length) { + const currentVertex = stack.pop(); + tour.push(currentVertex); + for (let i = graph[currentVertex].length - 1; i >= 0; i--) { + if (graph[currentVertex][i] !== 0) { + stack.push(i); + graph[currentVertex][i] = graph[i][currentVertex] = 0; // Remove edge to avoid revisiting + } + } + } + return tour; +} + +function christofidesTSP(cities, distances) { + const numCities = cities.length; + const minimumSpanningTreeGraph = minimumSpanningTree(distances); + const matching = minimumWeightPerfectMatching(distances, minimumSpanningTreeGraph); + const graph = Array(numCities).fill(null).map(() => Array(numCities).fill(0)); + + for (const [u, v] of matching) { + graph[u][v] = graph[v][u] = distances[u][v]; + } + + for (let i = 0; i < numCities; i++) { + for (let j = 0; j < numCities; j++) { + if (minimumSpanningTreeGraph[i] !== j) { + graph[i][j] = distances[i][j]; + } + } + } + + const eulerianTourPath = eulerianTour(graph, 0); + const visited = new Set(); + const hamiltonianTour = []; + + for (const vertex of eulerianTourPath) { + if (!visited.has(vertex)) { + hamiltonianTour.push(vertex); + visited.add(vertex); + } + } + + hamiltonianTour.push(hamiltonianTour[0]); + + let totalDistance = 0; + for (let i = 0; i < hamiltonianTour.length - 1; i++) { + totalDistance += distances[hamiltonianTour[i]][hamiltonianTour[i + 1]]; + } + + return { order: hamiltonianTour, distance: totalDistance }; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = christofidesTSP(cities, distances); +console.log("Christofides Algorithm Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Christofides Algorithm Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/DynamicProgramingBitmask.js b/DSA/Graphs/Travelling Salesman Problem/DynamicProgramingBitmask.js new file mode 100644 index 0000000..60d237a --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/DynamicProgramingBitmask.js @@ -0,0 +1,40 @@ +function tspDynamicProgrammingBitmask(cities, distances) { + const n = cities.length; + const memo = new Array(n).fill(null).map(() => new Array(1 << n).fill(null)); + + function dp(node, bitmask) { + if (bitmask === (1 << n) - 1) { + return distances[node][0]; + } + + if (memo[node][bitmask] !== null) { + return memo[node][bitmask]; + } + + let minDistance = Infinity; + for (let nextNode = 0; nextNode < n; nextNode++) { + if ((bitmask & (1 << nextNode)) === 0) { + const newDistance = distances[node][nextNode] + dp(nextNode, bitmask | (1 << nextNode)); + minDistance = Math.min(minDistance, newDistance); + } + } + + memo[node][bitmask] = minDistance; + return minDistance; + } + + const initialBitmask = 1; // Start from city 0 + const minDistance = dp(0, initialBitmask); + return minDistance; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = tspDynamicProgrammingBitmask(cities, distances); +console.log("Dynamic Programming (Bitmask) Optimal Distance:", result); diff --git a/DSA/Graphs/Travelling Salesman Problem/Dynamic_programming.js b/DSA/Graphs/Travelling Salesman Problem/Dynamic_programming.js new file mode 100644 index 0000000..f66fb90 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/Dynamic_programming.js @@ -0,0 +1,44 @@ +function dynamicProgrammingTSP(cities, distances) { + const numCities = cities.length; + const numStates = 1 << numCities; // 2^n states to represent subsets of cities + const memo = Array(numCities) + .fill(null) + .map(() => Array(numStates).fill(null)); + + function tspDP(currentCity, state) { + if (state === (1 << numCities) - 1) { + // All cities visited, return to the starting city + return distances[currentCity][0]; + } + + if (memo[currentCity][state] !== null) { + return memo[currentCity][state]; + } + + let minDistance = Infinity; + for (let nextCity = 0; nextCity < numCities; nextCity++) { + if ((state & (1 << nextCity)) === 0) { + // Next city not visited + const newDistance = distances[currentCity][nextCity] + tspDP(nextCity, state | (1 << nextCity)); + minDistance = Math.min(minDistance, newDistance); + } + } + + memo[currentCity][state] = minDistance; + return minDistance; + } + + const optimalDistance = tspDP(0, 1); // Start from city 0 (the first city), with only city 0 visited + return optimalDistance; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = dynamicProgrammingTSP(cities, distances); +console.log("Dynamic Programming Optimal Distance:", result); diff --git a/DSA/Graphs/Travelling Salesman Problem/GeneticAlgorithm.js b/DSA/Graphs/Travelling Salesman Problem/GeneticAlgorithm.js new file mode 100644 index 0000000..c0ad8fd --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/GeneticAlgorithm.js @@ -0,0 +1,76 @@ +function generateRandomOrder(n) { + const order = Array.from({ length: n }, (_, i) => i); + for (let i = n - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [order[i], order[j]] = [order[j], order[i]]; // Swap elements randomly + } + return order; +} + +function calculateTotalDistance(order, distances) { + let totalDistance = 0; + for (let i = 0; i < order.length - 1; i++) { + totalDistance += distances[order[i]][order[i + 1]]; + } + return totalDistance; +} + +function geneticAlgorithmTSP(cities, distances, populationSize, generations) { + let population = Array.from({ length: populationSize }, () => generateRandomOrder(cities.length)); + let bestOrder = population[0]; + let minDistance = calculateTotalDistance(bestOrder, distances); + + for (let generation = 0; generation < generations; generation++) { + population = population.sort(() => 0.5 - Math.random()); // Shuffle population + + for (let i = 0; i < populationSize; i += 2) { + const parent1 = population[i]; + const parent2 = population[i + 1]; + const [child1, child2] = crossover(parent1, parent2); + mutate(child1); + mutate(child2); + + population[i] = child1; + population[i + 1] = child2; + } + + const newBestOrder = population[0]; + const newMinDistance = calculateTotalDistance(newBestOrder, distances); + if (newMinDistance < minDistance) { + bestOrder = newBestOrder; + minDistance = newMinDistance; + } + } + + return { order: bestOrder, distance: minDistance }; +} + +// Helper functions for Genetic Algorithm +function crossover(parent1, parent2) { + const n = parent1.length; + const start = Math.floor(Math.random() * n); + const end = Math.floor(Math.random() * (n - start)) + start; + const child1 = parent1.slice(start, end + 1).concat(parent2.filter((city) => !child1.includes(city))); + const child2 = parent2.slice(start, end + 1).concat(parent1.filter((city) => !child2.includes(city))); + return [child1, child2]; +} + +function mutate(order) { + const n = order.length; + const i = Math.floor(Math.random() * n); + const j = (i + 1) % n; + [order[i], order[j]] = [order[j], order[i]]; // Swap two cities +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = geneticAlgorithmTSP(cities, distances, 100, 1000); +console.log("Genetic Algorithm Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Genetic Algorithm Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/HeuristicApproach.js b/DSA/Graphs/Travelling Salesman Problem/HeuristicApproach.js new file mode 100644 index 0000000..d68cb97 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/HeuristicApproach.js @@ -0,0 +1,44 @@ +function solveILPHeuristic() { + const coefficients = [2, 3, 5]; // Coefficients of the objective function + const constraintCoefficients = [ + [1, 2, 1], // Coefficients of the constraints + ]; + const constraintLimits = [0]; // Right-hand side of the constraints + + const numVariables = coefficients.length; + const numConstraints = constraintCoefficients.length; + + const solution = Array(numVariables).fill(0); + let objectiveValue = 0; + + for (let i = 0; i < numVariables; i++) { + // Calculate the marginal contribution of variable i to the objective function + const marginalContribution = coefficients[i]; + + // Check if adding variable i to the solution violates any constraints + let isFeasible = true; + for (let j = 0; j < numConstraints; j++) { + let constraintValue = 0; + for (let k = 0; k < numVariables; k++) { + constraintValue += solution[k] * constraintCoefficients[j][k]; + } + if (constraintValue + constraintCoefficients[j][i] > constraintLimits[j]) { + isFeasible = false; + break; + } + } + + // If adding variable i is feasible and improves the objective, include it in the solution + if (isFeasible && marginalContribution > 0) { + solution[i] = 1; + objectiveValue += marginalContribution; + } + } + + return { solution, objectiveValue }; +} + +// Test the heuristic approach for ILP +const result = solveILPHeuristic(); +console.log('Solution:', result.solution); +console.log('Objective Value (approximate):', result.objectiveValue); diff --git a/DSA/Graphs/Travelling Salesman Problem/IntegerLinearProgramming.js b/DSA/Graphs/Travelling Salesman Problem/IntegerLinearProgramming.js new file mode 100644 index 0000000..1d29fe4 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/IntegerLinearProgramming.js @@ -0,0 +1,52 @@ +function solveILP() { + const coefficients = [2, 3, 5]; // Coefficients of the objective function + const constraintCoefficients = [ + [1, 2, 1], // Coefficients of the constraints + ]; + const constraintLimits = [0]; // Right-hand side of the constraints + + const numVariables = coefficients.length; + const numConstraints = constraintCoefficients.length; + + let bestObjectiveValue = -Infinity; + let bestSolution = Array(numVariables).fill(0); + + // Generate all possible binary combinations for the variables + for (let i = 0; i < Math.pow(2, numVariables); i++) { + const binary = (i >>> 0).toString(2).padStart(numVariables, '0').split('').map(Number); + let isFeasible = true; + + // Check if the binary combination satisfies the constraints + for (let j = 0; j < numConstraints; j++) { + let constraintValue = 0; + for (let k = 0; k < numVariables; k++) { + constraintValue += binary[k] * constraintCoefficients[j][k]; + } + if (constraintValue > constraintLimits[j]) { + isFeasible = false; + break; + } + } + + if (isFeasible) { + // Calculate the objective value for this combination + let objectiveValue = 0; + for (let k = 0; k < numVariables; k++) { + objectiveValue += binary[k] * coefficients[k]; + } + + // Update the best solution if this combination has a better objective value + if (objectiveValue > bestObjectiveValue) { + bestObjectiveValue = objectiveValue; + bestSolution = [...binary]; + } + } + } + + return { solution: bestSolution, objectiveValue: bestObjectiveValue }; +} + +// Test the ILP solver +const result = solveILP(); +console.log('Solution:', result.solution); +console.log('Optimal Objective Value:', result.objectiveValue); diff --git a/DSA/Graphs/Travelling Salesman Problem/IterativeImprovement(Heuristic).js b/DSA/Graphs/Travelling Salesman Problem/IterativeImprovement(Heuristic).js new file mode 100644 index 0000000..3fb118c --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/IterativeImprovement(Heuristic).js @@ -0,0 +1,48 @@ +function iterativeImprovementTSP(cities, distances) { + const numCities = cities.length; + let currentOrder = generateRandomOrder(numCities); + let currentDistance = calculateTotalDistance(currentOrder, distances); + let improvement = true; + + while (improvement) { + improvement = false; + + for (let i = 0; i < numCities - 1; i++) { + for (let j = i + 1; j < numCities; j++) { + const newOrder = twoOptSwap(currentOrder, i, j); + const newDistance = calculateTotalDistance(newOrder, distances); + + if (newDistance < currentDistance) { + currentOrder = newOrder; + currentDistance = newDistance; + improvement = true; + } + } + } + } + + return { order: currentOrder, distance: currentDistance }; +} + +function twoOptSwap(order, i, j) { + const newOrder = [...order]; + while (i < j) { + [newOrder[i], newOrder[j]] = [newOrder[j], newOrder[i]]; + i++; + j--; + } + return newOrder; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = iterativeImprovementTSP(cities, distances); +console.log("Iterative Improvement Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Iterative Improvement Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/Lin-Kernighan.js b/DSA/Graphs/Travelling Salesman Problem/Lin-Kernighan.js new file mode 100644 index 0000000..79b0f88 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/Lin-Kernighan.js @@ -0,0 +1,73 @@ +function linKernighanTSP(cities, distances) { + const n = cities.length; + let bestTour = []; + let bestCost = Infinity; + + function calculateTourCost(tour) { + let cost = 0; + for (let i = 0; i < n - 1; i++) { + const from = tour[i]; + const to = tour[i + 1]; + cost += distances[from][to]; + } + cost += distances[tour[n - 1]][tour[0]]; // Return to the starting city + return cost; + } + + function reverseSubtour(tour, i, j) { + while (i < j) { + const temp = tour[i]; + tour[i] = tour[j]; + tour[j] = temp; + i++; + j--; + } + } + + function explore(k, tour, gain, canRemove) { + if (k === n) { + const tourCost = calculateTourCost(tour); + if (tourCost < bestCost) { + bestTour = [...tour]; + bestCost = tourCost; + } + } else { + for (let i = k; i < n; i++) { + if (canRemove[tour[i]]) { + for (let j = 0; j < n; j++) { + if (!canRemove[tour[j]]) { + const nextTour = [...tour]; + reverseSubtour(nextTour, k, i); + nextTour[k] = tour[i]; + const newGain = gain - distances[tour[k - 1]][tour[k]] + distances[tour[i]][tour[i + 1]]; + + if (newGain < 0) { + explore(k + 1, nextTour, newGain, canRemove); + } + } + } + } + } + } + } + + const initialTour = Array.from({ length: n }, (_, i) => i); + const canRemove = Array(n).fill(true); + canRemove[0] = false; // Starting city cannot be removed + explore(1, initialTour, 0, canRemove); + + return { order: bestTour.map((idx) => cities[idx]), distance: bestCost }; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = linKernighanTSP(cities, distances); +console.log("Lin-Kernighan Algorithm Order:", result.order.join(" -> ")); +console.log("Lin-Kernighan Algorithm Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/NearestNeighborAlgorithm.js b/DSA/Graphs/Travelling Salesman Problem/NearestNeighborAlgorithm.js new file mode 100644 index 0000000..9202d2e --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/NearestNeighborAlgorithm.js @@ -0,0 +1,38 @@ +function nearestNeighborTSP(cities, distances) { + const n = cities.length; + const visited = Array(n).fill(false); + const order = [0]; // Start from the first city (index 0) + let totalDistance = 0; + + visited[0] = true; + for (let i = 1; i < n; i++) { + let nearestIdx = -1; + let minDistance = Infinity; + + for (let j = 0; j < n; j++) { + if (!visited[j] && distances[order[i - 1]][j] < minDistance) { + nearestIdx = j; + minDistance = distances[order[i - 1]][j]; + } + } + + order.push(nearestIdx); + visited[nearestIdx] = true; + totalDistance += minDistance; + } + + totalDistance += distances[order[n - 1]][order[0]]; // Return to the starting city + return { order, distance: totalDistance }; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = nearestNeighborTSP(cities, distances); +console.log("Approximate Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Approximate Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/README.md b/DSA/Graphs/Travelling Salesman Problem/README.md new file mode 100644 index 0000000..d2f0d43 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/README.md @@ -0,0 +1,57 @@ +# Travelling Salesman Problem + +The Traveling Salesman Problem (TSP) is a classic combinatorial optimization problem in the field of mathematics and computer science. It is a challenging problem that can be stated as follows: + +Given a list of cities and the distances between each pair of cities, the objective is to find the shortest possible route that visits each city exactly once and returns to the starting city. + +In other words, a traveling salesman needs to determine the most efficient way to visit all the cities in their itinerary without revisiting any city and returning to the starting point while minimizing the total distance traveled. The problem is often represented as a graph, where cities are nodes, and the distances between them are represented as edges. + +The Traveling Salesman Problem has numerous practical applications beyond sales, such as: + +1. Logistics and Transportation: Optimizing delivery routes for couriers, trucks, or drones to minimize travel time and fuel costs. + +2. Circuit Design: Designing the most efficient electronic circuit layout to connect a set of components. + +3. Manufacturing: Determining the order in which machines should process parts to minimize production time. + +4. DNA Sequencing: Finding the optimal order to sequence fragments of DNA in genomics research. + +5. Network Design: Planning the most efficient layout for connecting network nodes while minimizing data transmission costs. + +The TSP is a well-known NP-hard problem, which means that finding the optimal solution becomes increasingly difficult as the number of cities increases. There are various algorithms and heuristics used to approximate solutions to the TSP, such as brute force, dynamic programming, and various metaheuristic methods like genetic algorithms, simulated annealing, and ant colony optimization. + +Due to its computational complexity, finding the exact optimal solution for large instances of the TSP is often infeasible, and approximation algorithms are used to find near-optimal solutions that are practical for real-world applications. + +## Solution to the Travelling Salesman Problem + +### Exact Algorithms: + + 1. Brute Force: Enumerate all possible permutations of cities and calculate the total distance for each permutation to find the optimal solution. Practical only for small problem instances due to its exponential time complexity. + + 2. Dynamic Programming: The Held-Karp algorithm is a dynamic programming approach that improves efficiency by avoiding redundant calculations. It can solve TSP for moderate-sized instances but still has exponential time complexity in the worst case. + +### Approximation Algorithms: + + 1. Nearest Neighbor Algorithm: Start from a selected city and repeatedly choose the nearest unvisited city until all cities are visited. This is a simple and fast heuristic but may not always produce optimal solutions. + + 2. Christofides Algorithm: An approximation algorithm that guarantees a solution within 3/2 times the optimal solution for metric TSP instances (where distances satisfy the triangle inequality). It includes minimum spanning tree and minimum-weight perfect matching steps. + + 3. Lin-Kernighan Algorithm: An improvement heuristic that iteratively swaps edges in the tour to improve the solution quality. + +### Metaheuristic Algorithms: + + 1. Genetic Algorithms: Inspired by natural selection, these algorithms involve creating a population of candidate solutions and iteratively evolving them through selection, crossover, and mutation operations. Genetic algorithms can be effective for finding near-optimal solutions. + + 2. Simulated Annealing: Based on the annealing process in metallurgy, this algorithm explores the solution space by accepting suboptimal solutions with a decreasing probability as the search progresses. It can escape local optima. + + 3. Ant Colony Optimization: Inspired by the foraging behavior of ants, this algorithm models the exploration of solution space as ants deposit pheromones on edges. Over time, paths with higher pheromone concentrations are more likely to be chosen. + + 4. Integer Linear Programming (ILP): Formulate the TSP as an ILP problem and use ILP solvers like CPLEX or Gurobi to find exact solutions for small to moderately sized instances. + + 5. Heuristic Approaches: Various heuristics and custom algorithms can be designed specifically for certain types of TSP instances, such as planar TSP, Euclidean TSP, or TSP with time windows. + + 6. Hybrid Approaches: Combine multiple algorithms or heuristics to improve solution quality and efficiency. For example, a genetic algorithm might be enhanced with local search. + + 7. Preprocessing and Data Reduction: Reduce the problem size by removing dominated or redundant cities or by using geometric properties of the problem to simplify the search. + +The choice of method depends on factors such as the size and type of the TSP instance, the desired solution quality, and available computational resources. In practice, many large TSP instances are solved using approximation algorithms or metaheuristic methods to find high-quality solutions efficiently. diff --git a/DSA/Graphs/Travelling Salesman Problem/SimulatedAnnealing.js b/DSA/Graphs/Travelling Salesman Problem/SimulatedAnnealing.js new file mode 100644 index 0000000..907878a --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/SimulatedAnnealing.js @@ -0,0 +1,41 @@ +function simulatedAnnealingTSP(cities, distances, temperature, coolingRate) { + let currentOrder = generateRandomOrder(cities.length); + let currentDistance = calculateTotalDistance(currentOrder, distances); + let bestOrder = currentOrder.slice(); + let minDistance = currentDistance; + + while (temperature > 1) { + const i = Math.floor(Math.random() * cities.length); + const j = Math.floor(Math.random() * cities.length); + const newOrder = currentOrder.slice(); + [newOrder[i], newOrder[j]] = [newOrder[j], newOrder[i]]; // Swap two cities + const newDistance = calculateTotalDistance(newOrder, distances); + + const delta = newDistance - currentDistance; + if (delta < 0 || Math.random() < Math.exp(-delta / temperature)) { + currentOrder = newOrder; + currentDistance = newDistance; + if (currentDistance < minDistance) { + bestOrder = currentOrder.slice(); + minDistance = currentDistance; + } + } + + temperature *= coolingRate; + } + + return { order: bestOrder, distance: minDistance }; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = simulatedAnnealingTSP(cities, distances, 1000, 0.99); +console.log("Simulated Annealing Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Simulated Annealing Distance:", result.distance);