UNPKG

clark-wright-alg

Version:

Clark-Wright Algorithm for Capacitated Vehicle Routing Problem

253 lines (203 loc) 6.48 kB
/** * Created by tnlam on 5/6/15. */ var ClarkWrightAlg = function () { this.options = { NODES: [], DISTANCES: [[]], CAPACITY: 0 }; }; function Node (amount, address, x, y) { this.amount = amount; this.address = address; this.x = x; this.y = y; this.Route = {}; this.index = {}; } function Edge (n1, n2, dist) { this.n1 = n1; this.n2 = n2; this.val = dist; this.next = {}; this.reverse = function () { var n = this.n2; this.n2 = this.n1; this.n1 = n; } } function Route (n) { this.allowed = {}; this.actual = 0; this.totalCost = 0; this.nodes = new Array(n); this.inEdges = new Array(n); this.outEdges = new Array(n); this.edges = []; this.add = function (e) { this.edges.push(e); this.outEdges[e.n1.index] = e; this.inEdges[e.n2.index] = e; e.n1.route = this; e.n2.route = this; this.totalCost += e.val; }; this.predecesor = function (nodeIndex) { return this.inEdges[nodeIndex].n1.index; }; this.sucessor = function (nodeIndex) { return this.outEdges[nodeIndex].n2.index; }; this.removeEdgeToNode = function (index) { var e = this.inEdges[index]; this.outEdges[e.n1.index] = null; this.totalCost -= e.val; this.edges.splice(this.edges.indexOf(e), 1); this.inEdges[index] = null; }; this.removeEdgeFromNode = function (index) { var e = this.outEdges[index]; this.inEdges[e.n2.index] = null; this.totalCost -= e.val; this.edges.splice(this.edges.indexOf(e), 1); this.outEdges[index] = null; }; this.merge = function (r2, mergingEdge) { var from = mergingEdge.n1.index; var to = mergingEdge.n2.index; var predecesorI = this.predecesor(from); var predecesorJ = r2.predecesor(to); var sucessorI = this.sucessor(from); var sucessorJ = r2.sucessor(to); if (sucessorI == 0 && predecesorJ == 0) { this.removeEdgeToNode(0); r2.removeEdgeFromNode(0); for (var i = 0; i < r2.edges.length; i++) { this.add(r2.edges[i]); } this.actual += r2.actual; this.add(mergingEdge); return true; } else if (sucessorJ == 0 && predecesorI == 0) { mergingEdge.reverse(); this.removeEdgeFromNode(0); r2.removeEdgeToNode(0); for (var i = 0; i < r2.edges.length; i++) { this.add(r2.edges[i]); } this.actual += r2.actual; this.add(mergingEdge); return true; } } } function Saving (val, from, to) { this.val = val; this.from = from; this.to = to; } function computeSaving(dist, n, sav, nodesField) { sav = new Array(n); for (var k = 0; k < sav.length; k++) { sav[k] = new Array(n); } var sList = new Array(); for (var i = 1; i < n; i++) { for (var j = i + 1; j < n; j++) { sav[i][j] = dist[0][i] + dist[j][0] - dist[i][j]; var n1 = nodesField[i]; var n2 = nodesField[j]; var s = new Saving(sav[i][j], n1, n2); sList.push(s); } } return sList; } ClarkWrightAlg.prototype.routing = function (args, cb) { // validate arguments if (arguments.length < 4) { throw new Error('Invalid number of arguments'); } var callback = arguments[arguments.length - 1]; if (typeof callback != 'function') { throw new Error('Missing callback function'); } // get arguments this.options.NODES = arguments[0]; this.options.DISTANCES = arguments[1]; this.options.CAPACITY = arguments[2]; console.log(this.options.NODES); console.log(this.options.DISTANCES); console.log(this.options.CAPACITY); var nCount = this.options.NODES.length; var routes = new Array(); var savings = [[]]; for (var i = 0; i < nCount; i++) { var n = this.options.NODES[i]; n.index = i; if (i != 0) { var e = new Edge(this.options.NODES[0], n, this.options.DISTANCES[0][i]); var e2 = new Edge(n, this.options.NODES[0], this.options.DISTANCES[0][i]); var r = new Route(nCount); r.allowed = this.options.CAPACITY; r.add(e); r.add(e2); r.actual += n.amount; routes.push(r); } } var totalCost = 0; routes.forEach(function (route) { totalCost += route.totalCost; }); var sList = computeSaving(this.options.DISTANCES, nCount, savings, this.options.NODES); sList.sort(function (s1, s2) { return s2.val - s1.val; }); while (sList.length != 0) { var actualS = sList[0]; var n1 = actualS.from; var n2 = actualS.to; var r1 = n1.route; var r2 = n2.route; var from = n1.index; var to = n2.index; if (actualS.val > 0 && r1.actual + r2.actual < r1.allowed && !(r1 === r2)) { var outgoingR2 = r2.outEdges[to]; var incommingR1 = r1.inEdges[from]; if (outgoingR2 != null && incommingR1 != null) { var succ = r1.merge(r2, new Edge(n1, n2, this.options.DISTANCES[n1.index][n2.index])); if(succ){ routes.splice(routes.indexOf(r2), 1); } } else { console.log('problem'); } } sList.splice(sList.indexOf(sList[0]), 1); } console.log('-result-'); console.log(routes.length); var result = new Array(routes.length); for (var k = 0; k < result.length; k++) { result[k] = new Array(); } console.log(result); routes.forEach(function (r) { var idx = routes.indexOf(r); var edge = r.outEdges[0]; var s = "0 "; result[idx].push(0); s += edge.n2.index + " "; result[idx].push(edge.n2.index); do { edge = r.outEdges[edge.n2.index]; s += edge.n2.index + " "; result[idx].push(edge.n2.index); } while (edge.n2.index != 0); console.log(s); if (routes.indexOf(r) == routes.length-1) { callback(null, result); } }); //}); }; module.exports = new ClarkWrightAlg();