mirror of
https://git.code.sf.net/p/seeddms/code
synced 2025-02-06 15:14:58 +00:00
600 lines
18 KiB
JavaScript
600 lines
18 KiB
JavaScript
/*
|
||
* Various algorithms and data structures, licensed under the MIT-license.
|
||
* (c) 2010 by Johann Philipp Strathausen <strathausen@gmail.com>
|
||
* http://strathausen.eu
|
||
*
|
||
*/
|
||
|
||
|
||
|
||
/*
|
||
Bellman-Ford
|
||
|
||
Path-finding algorithm, finds the shortest paths from one node to all nodes.
|
||
|
||
|
||
Complexity
|
||
|
||
O( |E| · |V| ), where E = edges and V = vertices (nodes)
|
||
|
||
|
||
Constraints
|
||
|
||
Can run on graphs with negative edge weights as long as they do not have
|
||
any negative weight cycles.
|
||
|
||
*/
|
||
function bellman_ford(g, source) {
|
||
|
||
/* STEP 1: initialisation */
|
||
for(var n in g.nodes)
|
||
g.nodes[n].distance = Infinity;
|
||
/* predecessors are implicitly null */
|
||
source.distance = 0;
|
||
|
||
step("Initially, all distances are infinite and all predecessors are null.");
|
||
|
||
/* STEP 2: relax each edge (this is at the heart of Bellman-Ford) */
|
||
/* repeat this for the number of nodes minus one */
|
||
for(var i = 1; i < g.nodes.length; i++)
|
||
/* for each edge */
|
||
for(var e in g.edges) {
|
||
var edge = g.edges[e];
|
||
if(edge.source.distance + edge.weight < edge.target.distance) {
|
||
step("Relax edge between " + edge.source.id + " and " + edge.target.id + ".");
|
||
edge.target.distance = edge.source.distance + edge.weight;
|
||
edge.target.predecessor = edge.source;
|
||
}
|
||
//Added by Jake Stothard (Needs to be tested)
|
||
if(!edge.style.directed) {
|
||
if(edge.target.distance + edge.weight < edge.source.distance) {
|
||
g.snapShot("Relax edge between "+edge.target.id+" and "+edge.source.id+".");
|
||
edge.source.distance = edge.target.distance + edge.weight;
|
||
edge.source.predecessor = edge.target;
|
||
}
|
||
}
|
||
}
|
||
step("Ready.");
|
||
|
||
/* STEP 3: TODO Check for negative cycles */
|
||
/* For now we assume here that the graph does not contain any negative
|
||
weights cycles. (this is left as an excercise to the reader[tm]) */
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
Path-finding algorithm Dijkstra
|
||
|
||
- worst-case running time is O((|E| + |V|) · log |V| ) thus better than
|
||
Bellman-Ford for sparse graphs (with less edges), but cannot handle
|
||
negative edge weights
|
||
*/
|
||
function dijkstra(g, source) {
|
||
|
||
/* initially, all distances are infinite and all predecessors are null */
|
||
for(var n in g.nodes)
|
||
g.nodes[n].distance = Infinity;
|
||
/* predecessors are implicitly null */
|
||
|
||
g.snapShot("Initially, all distances are infinite and all predecessors are null.");
|
||
|
||
source.distance = 0;
|
||
/* set of unoptimized nodes, sorted by their distance (but a Fibonacci heap
|
||
would be better) */
|
||
var q = new BinaryMinHeap(g.nodes, "distance");
|
||
|
||
/* pointer to the node in focus */
|
||
var node;
|
||
|
||
/* get the node with the smallest distance
|
||
as long as we have unoptimized nodes. q.min() can have O(log n). */
|
||
while(q.min() != undefined) {
|
||
/* remove the latest */
|
||
node = q.extractMin();
|
||
node.optimized = true;
|
||
|
||
/* no nodes accessible from this one, should not happen */
|
||
if(node.distance == Infinity)
|
||
throw "Orphaned node!";
|
||
|
||
/* for each neighbour of node */
|
||
for(e in node.edges) {
|
||
var other = (node == node.edges[e].target) ? node.edges[e].source : node.edges[e].target;
|
||
|
||
if(other.optimized)
|
||
continue;
|
||
|
||
/* look for an alternative route */
|
||
var alt = node.distance + node.edges[e].weight;
|
||
|
||
/* update distance and route if a better one has been found */
|
||
if (alt < other.distance) {
|
||
|
||
/* update distance of neighbour */
|
||
other.distance = alt;
|
||
|
||
/* update priority queue */
|
||
q.heapify();
|
||
|
||
/* update path */
|
||
other.predecessor = node;
|
||
g.snapShot("Enhancing node.")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* All-Pairs-Shortest-Paths */
|
||
/* Runs at worst in O(|V|³) and at best in Omega(|V|³) :-)
|
||
complexity Sigma(|V|²) */
|
||
/* This implementation is not yet ready for general use, but works with the
|
||
Dracula graph library. */
|
||
function floyd_warshall(g, source) {
|
||
|
||
/* Step 1: initialising empty path matrix (second dimension is implicit) */
|
||
var path = [];
|
||
var next = [];
|
||
var n = g.nodes.length;
|
||
|
||
/* construct path matrix, initialize with Infinity */
|
||
for(j in g.nodes) {
|
||
path[j] = [];
|
||
next[j] = [];
|
||
for(i in g.nodes)
|
||
path[j][i] = j == i ? 0 : Infinity;
|
||
}
|
||
|
||
/* initialize path with edge weights */
|
||
for(e in g.edges)
|
||
path[g.edges[e].source.id][g.edges[e].target.id] = g.edges[e].weight;
|
||
|
||
/* Note: Usually, the initialisation is done by getting the edge weights
|
||
from a node matrix representation of the graph, not by iterating through
|
||
a list of edges as done here. */
|
||
|
||
/* Step 2: find best distances (the heart of Floyd-Warshall) */
|
||
for(k in g.nodes){
|
||
for(i in g.nodes) {
|
||
for(j in g.nodes)
|
||
if(path[i][j] > path[i][k] + path[k][j]) {
|
||
path[i][j] = path[i][k] + path[k][j];
|
||
/* Step 2.b: remember the path */
|
||
next[i][j] = k;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Step 3: Path reconstruction, get shortest path */
|
||
function getPath(i, j) {
|
||
if(path[i][j] == Infinity)
|
||
throw "There is no path.";
|
||
var intermediate = next[i][j];
|
||
if(intermediate == undefined)
|
||
return null;
|
||
else
|
||
return getPath(i, intermediate)
|
||
.concat([intermediate])
|
||
.concat(getPath(intermediate, j));
|
||
}
|
||
|
||
/* TODO use the knowledge, e.g. mark path in graph */
|
||
}
|
||
|
||
/*
|
||
Ford-Fulkerson
|
||
|
||
Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed
|
||
graph from source to sink.
|
||
|
||
|
||
Complexity
|
||
|
||
O(E * max(f)), max(f) being the maximum flow
|
||
|
||
|
||
Description
|
||
|
||
As long as there is an open path through the residual graph, send the
|
||
minimum of the residual capacities on the path.
|
||
|
||
|
||
Constraints
|
||
|
||
The algorithm works only if all weights are integers. Otherwise it is
|
||
possible that the Ford–Fulkerson algorithm will not converge to the maximum
|
||
value.
|
||
|
||
|
||
Input
|
||
|
||
g - Graph object
|
||
s - Source ID
|
||
t - Target (sink) ID
|
||
|
||
|
||
Output
|
||
|
||
Maximum flow from Source s to Target t
|
||
|
||
*/
|
||
/*
|
||
Edmonds-Karp
|
||
|
||
Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed
|
||
graph from source to sink. An implementation of the Ford-Fulkerson
|
||
algorithm.
|
||
|
||
|
||
Complexity
|
||
|
||
O(|V|*|E|²)
|
||
|
||
|
||
Input
|
||
|
||
g - Graph object (with node and edge lists, capacity is a property of edge)
|
||
s - source ID
|
||
t - sink ID
|
||
|
||
*/
|
||
function edmonds_karp(g, s, t) {
|
||
|
||
}
|
||
|
||
/*
|
||
A simple binary min-heap serving as a priority queue
|
||
- takes an array as the input, with elements having a key property
|
||
- elements will look like this:
|
||
{
|
||
key: "... key property ...",
|
||
value: "... element content ..."
|
||
}
|
||
- provides insert(), min(), extractMin() and heapify()
|
||
- example usage (e.g. via the Firebug or Chromium console):
|
||
var x = {foo: 20, hui: "bla"};
|
||
var a = new BinaryMinHeap([x,{foo:3},{foo:10},{foo:20},{foo:30},{foo:6},{foo:1},{foo:3}],"foo");
|
||
console.log(a.extractMin());
|
||
console.log(a.extractMin());
|
||
x.foo = 0; // update key
|
||
a.heapify(); // call this always after having a key updated
|
||
console.log(a.extractMin());
|
||
console.log(a.extractMin());
|
||
- can also be used on a simple array, like [9,7,8,5]
|
||
*/
|
||
function BinaryMinHeap(array, key) {
|
||
|
||
/* Binary tree stored in an array, no need for a complicated data structure */
|
||
var tree = [];
|
||
|
||
var key = key || 'key';
|
||
|
||
/* Calculate the index of the parent or a child */
|
||
var parent = function(index) { return Math.floor((index - 1)/2); };
|
||
var right = function(index) { return 2 * index + 2; };
|
||
var left = function(index) { return 2 * index + 1; };
|
||
|
||
/* Helper function to swap elements with their parent
|
||
as long as the parent is bigger */
|
||
function bubble_up(i) {
|
||
var p = parent(i);
|
||
while((p >= 0) && (tree[i][key] < tree[p][key])) {
|
||
/* swap with parent */
|
||
tree[i] = tree.splice(p, 1, tree[i])[0];
|
||
/* go up one level */
|
||
i = p;
|
||
p = parent(i);
|
||
}
|
||
}
|
||
|
||
/* Helper function to swap elements with the smaller of their children
|
||
as long as there is one */
|
||
function bubble_down(i) {
|
||
var l = left(i);
|
||
var r = right(i);
|
||
|
||
/* as long as there are smaller children */
|
||
while(tree[l] && (tree[i][key] > tree[l][key]) || tree[r] && (tree[i][key] > tree[r][key])) {
|
||
|
||
/* find smaller child */
|
||
var child = tree[l] ? tree[r] ? tree[l][key] > tree[r][key] ? r : l : l : l;
|
||
|
||
/* swap with smaller child with current element */
|
||
tree[i] = tree.splice(child, 1, tree[i])[0];
|
||
|
||
/* go up one level */
|
||
i = child;
|
||
l = left(i);
|
||
r = right(i);
|
||
}
|
||
}
|
||
|
||
/* Insert a new element with respect to the heap property
|
||
1. Insert the element at the end
|
||
2. Bubble it up until it is smaller than its parent */
|
||
this.insert = function(element) {
|
||
|
||
/* make sure there's a key property */
|
||
(element[key] == undefined) && (element = {key:element});
|
||
|
||
/* insert element at the end */
|
||
tree.push(element);
|
||
|
||
/* bubble up the element */
|
||
bubble_up(tree.length - 1);
|
||
}
|
||
|
||
/* Only show us the minimum */
|
||
this.min = function() {
|
||
return tree.length == 1 ? undefined : tree[0];
|
||
}
|
||
|
||
/* Return and remove the minimum
|
||
1. Take the root as the minimum that we are looking for
|
||
2. Move the last element to the root (thereby deleting the root)
|
||
3. Compare the new root with both of its children, swap it with the
|
||
smaller child and then check again from there (bubble down)
|
||
*/
|
||
this.extractMin = function() {
|
||
var result = this.min();
|
||
|
||
/* move the last element to the root or empty the tree completely */
|
||
/* bubble down the new root if necessary */
|
||
(tree.length == 1) && (tree = []) || (tree[0] = tree.pop()) && bubble_down(0);
|
||
|
||
return result;
|
||
}
|
||
|
||
/* currently unused, TODO implement */
|
||
this.changeKey = function(index, key) {
|
||
throw "function not implemented";
|
||
}
|
||
|
||
this.heapify = function() {
|
||
for(var start = Math.floor((tree.length - 2) / 2); start >= 0; start--) {
|
||
bubble_down(start);
|
||
}
|
||
}
|
||
|
||
/* insert the input elements one by one only when we don't have a key property (TODO can be done more elegant) */
|
||
for(i in (array || []))
|
||
this.insert(array[i]);
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
Quick Sort:
|
||
1. Select some random value from the array, the median.
|
||
2. Divide the array in three smaller arrays according to the elements
|
||
being less, equal or greater than the median.
|
||
3. Recursively sort the array containg the elements less than the
|
||
median and the one containing elements greater than the median.
|
||
4. Concatenate the three arrays (less, equal and greater).
|
||
5. One or no element is always sorted.
|
||
TODO: This could be implemented more efficiently by using only one array object and several pointers.
|
||
*/
|
||
function quickSort(arr) {
|
||
/* recursion anchor: one element is always sorted */
|
||
if(arr.length <= 1) return arr;
|
||
/* randomly selecting some value */
|
||
var median = arr[Math.floor(Math.random() * arr.length)];
|
||
var arr1 = [], arr2 = [], arr3 = [];
|
||
for(var i in arr) {
|
||
arr[i] < median && arr1.push(arr[i]);
|
||
arr[i] == median && arr2.push(arr[i]);
|
||
arr[i] > median && arr3.push(arr[i]);
|
||
}
|
||
/* recursive sorting and assembling final result */
|
||
return quickSort(arr1).concat(arr2).concat(quickSort(arr3));
|
||
}
|
||
|
||
/*
|
||
Selection Sort:
|
||
1. Select the minimum and remove it from the array
|
||
2. Sort the rest recursively
|
||
3. Return the minimum plus the sorted rest
|
||
4. An array with only one element is already sorted
|
||
*/
|
||
function selectionSort(arr) {
|
||
/* recursion anchor: one element is always sorted */
|
||
if(arr.length == 1) return arr;
|
||
var minimum = Infinity;
|
||
var index;
|
||
for(var i in arr) {
|
||
if(arr[i] < minimum) {
|
||
minimum = arr[i];
|
||
index = i; /* remember the minimum index for later removal */
|
||
}
|
||
}
|
||
/* remove the minimum */
|
||
arr.splice(index, 1);
|
||
/* assemble result and sort recursively (could be easily done iteratively as well)*/
|
||
return [minimum].concat(selectionSort(arr));
|
||
}
|
||
|
||
/*
|
||
Merge Sort:
|
||
1. Cut the array in half
|
||
2. Sort each of them recursively
|
||
3. Merge the two sorted arrays
|
||
4. An array with only one element is considered sorted
|
||
|
||
*/
|
||
function mergeSort(arr) {
|
||
/* merges two sorted arrays into one sorted array */
|
||
function merge(a, b) {
|
||
/* result set */
|
||
var c = [];
|
||
/* as long as there are elements in the arrays to be merged */
|
||
while(a.length > 0 || b.length > 0){
|
||
/* are there elements to be merged, if yes, compare them and merge */
|
||
var n = a.length > 0 && b.length > 0 ? a[0] < b[0] ? a.shift() : b.shift() : b.length > 0 ? b.shift() : a.length > 0 ? a.shift() : null;
|
||
/* always push the smaller one onto the result set */
|
||
n != null && c.push(n);
|
||
}
|
||
return c;
|
||
}
|
||
/* this mergeSort implementation cuts the array in half, wich should be fine with randomized arrays, but introduces the risk of a worst-case scenario */
|
||
median = Math.floor(arr.length / 2);
|
||
var part1 = arr.slice(0, median); /* for some reason it doesn't work if inserted directly in the return statement (tried so with firefox) */
|
||
var part2 = arr.slice(median - arr.length);
|
||
return arr.length <= 1 ? arr : merge(
|
||
mergeSort(part1), /* first half */
|
||
mergeSort(part2) /* second half */
|
||
);
|
||
}
|
||
|
||
/* Balanced Red-Black-Tree */
|
||
function RedBlackTree(arr) {
|
||
|
||
}
|
||
|
||
function BTree(arr) {
|
||
|
||
}
|
||
|
||
function NaryTree(n, arr) {
|
||
|
||
}
|
||
|
||
/**
|
||
* Knuth-Morris-Pratt string matching algorithm - finds a pattern in a text.
|
||
* FIXME: Doesn't work correctly yet.
|
||
*/
|
||
function kmp(p, t) {
|
||
|
||
/**
|
||
* PREFIX, OVERLAP or FALIURE function for KMP. Computes how many iterations
|
||
* the algorithm can skip after a mismatch.
|
||
*
|
||
* @input p - pattern (string)
|
||
* @result array of skippable iterations
|
||
*/
|
||
function prefix(p) {
|
||
/* pi contains the computed skip marks */
|
||
var pi = [0], k = 0;
|
||
for(q = 1; q < p.length; q++) {
|
||
while(k > 0 && (p.charAt(k) != p.charAt(q)))
|
||
k = pi[k-1];
|
||
|
||
(p.charAt(k) == p.charAt(q)) && k++;
|
||
|
||
pi[q] = k;
|
||
}
|
||
return pi;
|
||
}
|
||
|
||
/* The actual KMP algorithm starts here. */
|
||
|
||
var pi = prefix(p), q = 0, result = [];
|
||
|
||
for(var i = 0; i < t.length; i++) {
|
||
/* jump forward as long as the character doesn't match */
|
||
while((q > 0) && (p.charAt(q) != t.charAt(i)))
|
||
q = pi[q];
|
||
|
||
(p.charAt(q) == t.charAt(i)) && q++;
|
||
|
||
(q == p.length) && result.push(i - p.length) && (q = pi[q]);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* step for algorithm visualisation */
|
||
function step(comment, funct) {
|
||
//wait for input
|
||
//display comment (before or after waiting)
|
||
// next.wait();
|
||
/* execute callback function */
|
||
funct();
|
||
}
|
||
|
||
/**
|
||
* Curry - Function currying
|
||
* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
|
||
* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
|
||
* Date: 10/4/2008
|
||
*
|
||
* @author Ariel Flesler
|
||
* @version 1.0.1
|
||
*/
|
||
function curry( fn ){
|
||
return function(){
|
||
var args = curry.args(arguments),
|
||
master = arguments.callee,
|
||
self = this;
|
||
|
||
return args.length >= fn.length ? fn.apply(self,args) : function(){
|
||
return master.apply( self, args.concat(curry.args(arguments)) );
|
||
};
|
||
};
|
||
};
|
||
|
||
curry.args = function( args ){
|
||
return Array.prototype.slice.call(args);
|
||
};
|
||
|
||
Function.prototype.curry = function(){
|
||
return curry(this);
|
||
};
|
||
|
||
/**
|
||
* Topological Sort
|
||
*
|
||
* Sort a directed graph based on incoming edges
|
||
*
|
||
* Coded by Jake Stothard
|
||
*/
|
||
function topological_sort(g) {
|
||
//Mark nodes as "deleted" instead of actually deleting them
|
||
//That way we don't have to copy g
|
||
|
||
for(i in g.nodes)
|
||
g.nodes[i].deleted = false;
|
||
|
||
var ret = topological_sort_helper(g);
|
||
|
||
//Cleanup: Remove the deleted property
|
||
for(i in g.nodes)
|
||
delete g.nodes[i].deleted
|
||
|
||
return ret;
|
||
}
|
||
function topological_sort_helper(g) {
|
||
//Find node with no incoming edges
|
||
var node;
|
||
for(i in g.nodes) {
|
||
if(g.nodes[i].deleted)
|
||
continue; //Bad style, meh
|
||
|
||
var incoming = false;
|
||
for(j in g.nodes[i].edges) {
|
||
if(g.nodes[i].edges[j].target == g.nodes[i]
|
||
&& g.nodes[i].edges[j].source.deleted == false) {
|
||
incoming = true;
|
||
break;
|
||
}
|
||
}
|
||
if(!incoming) {
|
||
node = g.nodes[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Either unsortable or done. Either way, GTFO
|
||
if(node == undefined)
|
||
return [];
|
||
|
||
//"Delete" node from g
|
||
node.deleted = true;
|
||
|
||
var tail = topological_sort_helper(g);
|
||
|
||
tail.unshift(node);
|
||
|
||
return tail;
|
||
}
|