diff --git a/README.md b/README.md index a7da39d..94eaa49 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Good heuristic for the traveling salesman problem using simulated annealing. - [salesman](#salesman) - [salesman~Point](#salesmanpoint) - [new Point(x, y)](#new-pointx-y) - - [salesman~solve(points, \[temp\_coeff\], \[callback\], \[callback\]) ⇒ Array.\](#salesmansolvepoints-temp_coeff-callback-callback--arraynumber) + - [salesman~solve(points, \[temp\_coeff\], \[callback\], \[callback\], \[keep\_end\]) ⇒ Array.\](#salesmansolvepoints-temp_coeff-callback-callback-keep_end--arraynumber) @@ -31,7 +31,7 @@ Represents a point in two dimensions. Used as the input for `solve`. -### salesman~solve(points, [temp_coeff], [callback], [callback]) ⇒ Array.<number> +### salesman~solve(points, [temp_coeff], [callback], [callback], [keep_end]) ⇒ Array.<number> Solves the following problem: Given a list of points and the distances between each pair of points, what is the shortest possible route that visits each point exactly @@ -46,6 +46,7 @@ Solves the following problem: | [temp_coeff] | number | 0.999 | changes the convergence speed of the algorithm. Smaller values (0.9) work faster but give poorer solutions, whereas values closer to 1 (0.99999) work slower, but give better solutions. | | [callback] | function | | An optional callback to be called after each iteration. | | [callback] | function | euclidean | An optional argument to specify how distances are calculated. The function takes two Point objects as arguments and returns a number for distance. Defaults to simple Euclidean distance calculation. | +| [keep_end] | boolean | false | An optional argument to specify if the last point is fixed. If false then the minimum circuit is calculated, if true the minimum path from first to last node is calculated. | **Example** ```js diff --git a/salesman.d.ts b/salesman.d.ts index 6c157ee..226a140 100644 --- a/salesman.d.ts +++ b/salesman.d.ts @@ -13,6 +13,7 @@ * @param [temp_coeff=0.999] changes the convergence speed of the algorithm: the closer to 1, the slower the algorithm and the better the solutions. * @param [callback=] An optional callback to be called after each iteration. * @param [distance=euclidean] An optional argument to specify how distances are calculated. The function takes two Point objects as arguments and returns a number for distance. Defaults to simple Euclidean distance calculation. + * @param [keep_end=false] An optional argument to specify if the last point is fixed. If false then the minimum circuit is calculated, if true the minimum path from first to last node is calculated. * * @returns An array of indexes in the original array. Indicates in which order the different points are visited. * @@ -25,7 +26,7 @@ * var ordered_points = solution.map(i => points[i]); * // ordered_points now contains the points, in the order they ought to be visited. */ -export function solve(points: Point[], temp_coeff?: number, callback?: (order: number[]) => void, distance?: (p: Point, q: Point) => number): number[]; +export function solve(points: Point[], temp_coeff?: number, callback?: (order: number[]) => void, distance?: (p: Point, q: Point) => number, keep_end?: boolean): number[]; export class Point { /** diff --git a/salesman.js b/salesman.js index b81095a..994eb2e 100644 --- a/salesman.js +++ b/salesman.js @@ -19,10 +19,12 @@ * along with an array which maintains a record of distances between points. * @param {Points[]} points The points in the path. * @param {Function} distanceFunc The function to use to calculate the distance between two points. - */ -function Path(points, distanceFunc) { +* @param {boolean} keepEnd Specify if the last point is fixed. If false then the minimum circuit is calculated, if true the minimum path from first to last node is calculated. + */ +function Path(points, distanceFunc, keepEnd) { this.points = points; this.distanceFunc = distanceFunc; + this.keepEnd = keepEnd; this.initializeOrder(); this.initializeDistances(); } @@ -140,7 +142,7 @@ Path.prototype.distance = function(i, j) { * @returns {number} A random index. */ Path.prototype.randomPos = function() { - return 1 + Math.floor(Math.random() * (this.points.length - 1)); + return 1 + Math.floor(Math.random() * (this.points.length - (this.keepEnd ? 2 : 1))); }; /** @@ -176,8 +178,8 @@ function Point(x, y) { * var ordered_points = solution.map(i => points[i]); * // ordered_points now contains the points, in the order they ought to be visited. **/ -function solve(points, temp_coeff = 0.999, callback, distance = euclidean) { - var path = new Path(points, distance); +function solve(points, temp_coeff = 0.999, callback, distance = euclidean, keep_end = false) { + var path = new Path(points, distance, keep_end); // Optimization: If there is only one point in the list, there is no path. if (points.length < 2) return path.order; // Optimization: If the user would provide a bad input, end immediately. diff --git a/test.js b/test.js index 6bad092..df2e05a 100644 --- a/test.js +++ b/test.js @@ -4,11 +4,12 @@ var salesman = require("./salesman.js"); var tests = [ {q:[[0,0]], r:[0]}, {q:[[0,0],[1,1]], r:[0,1]}, + {q:[[0,0],[2,2],[1,1]], r:[0,1,2], e: true}, ]; for(let test of tests) { var points = test.q.map(([x,y])=>new salesman.Point(x,y)); - var res = salesman.solve(points); + var res = salesman.solve(points, undefined, undefined, undefined, test.e); assert.deepEqual(test.r, res); }