clark-wright-alg
Version:
Clark-Wright Algorithm for Capacitated Vehicle Routing Problem
253 lines (203 loc) • 6.48 kB
JavaScript
/**
* 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();