JointJs Cycle Detection - javascript

I am trying to implement an algorithm to detect cycles in a directed graph (only outbound connections) using JointJS. I have written the following code which does not perform expectedly.
var _elements = graph.getElements();
var visited = [];
var level = 0;
var isCycleExists;
for (var i = 0; i < _elements.length; i++) {
var elem = _elements[i];
//only checking nodes which do have predecessors
if ((graph.getPredecessors(elem).length > 0) && !elem.isLink()
&& hasCycle(elem, visited, level)) {
isCycleExists = true;
break;
}
}
function hasCycle(comp, visited, level) {
var successors = graph.getSuccessors(comp);
visited.push(comp.id);
for (var i = 0; i < successors.length; i++) {
var c = successors[i];
var _successors = graph.getSuccessors(c);
if (_successors.length == 0) {
continue;
}
if (visited.indexOf(c.id) > -1) {
return true;
}
visited.push(c.id);
if (hasCycle(c, visited.slice(), ++level)) {
return true;
}
}
return false;
}
It would be really helpful if anyone could help me in this.

The problem is successor is not the same as direct successor. Check out graph.getSuccessors(comp) value: if the lib uses consistent function names, that should contain B,C,D and E for the first run. So you mark those visited, but also run hasCycle(c, visited.slice(), ++level) where you are checking nodes starting from D again (I guess D is the first one for which you get the "already visited" case).
First, I'd recommend to get rid of doubling the iteration in your function, do smth like
function hasCycle(comp, visited, level) {
var successors = graph.getSuccessors(comp), i;
if (visited.indexOf(comp.id) > -1)
return true;
visited.push(comp.id);
for (i = 0; i < successors.length; i++)
if (hasCycle(successors[i], visited.slice(), ++level))
return true;
return false;
}
And second, more importantly, try the graph.getNeighbors method instead of graph.getSuccessors (remember to check only neighbors in one direction).

I believe I have found the root cause for such erratic behavior using this algorithm.
Actually JointJS API method getElements() returns the array of elements in the order of entry in the graph cells. That means if you create 3 elements A, B, C to create a graph like A-->B-->C but you have added these elements in the order A first, C second and B at the end. Now according to the if condition (graph.getPredecessors(elem).length > 0) && !elem.isLink() && hasCycle(elem, visited, level), the cycle detection actually starts from C. In this scenario, C doesn't have any outbound neighbor and it is marked as visited. Now, in the next iteration, B is checked (as it is entered into the Graph after C) and it has C as its outbound neighbor. Now C is again evaluated but C is already marked as visited. Hence, it shows existence of a loop.
So, I believe, our search in a graph should be started from the nodes which doesn't have any predecessors. For instance, in the previous example, our search must begin always with A. The graph also might contain one or several nodes which don't have any predecessor. In this scenario as well, we need to start our search from the nodes which don't have any predecessor. For instance, A1-->B, A1-->C, A2-->B, A2-->C like this. Our detection must begin from A1 and A2.
I have modified the methods as follows which conforms to the previous strategy:
function checkForCycleExistence() {
var visited = [];
var level = 0;
var isCycleExists;
var _elements = graph.getElements();
for (var i = 0; i < _elements.length; i++) {
var elem = _elements[i];
if ((graph.getPredecessors(elem).length == 0)
&& hasCycle(elem, visited, level)) {
isCycleExists = true;
break;
}
}
if (isCycleExists) {
console.log("Cycle Exists");
}
return isCycleExists;
}
function hasCycle(comp, visited, level) {
var neighbors = graph.getNeighbors(comp, {
outbound : true
}), i;
if (visited.indexOf(comp.id) > -1)
return true;
visited.push(comp.id);
for (i = 0; i < neighbors.length; i++)
if (hasCycle(neighbors[i], visited.slice(), ++level))
return true;
return false;
}

Related

Javascript - DFS Topological / Recursive function

I have a Question about implementing a dfs/ topological sorting in JS.
My question is about the logic of the recursive call (function topSortHelper(...)).
we start from a vertex, we first print it and then recursively call topSortHelper(...) for its adjacent
vertices, We don’t print the vertex immediately, we first recursively call topological sorting for all
its adjacent vertices, then push it to a stack.
Could someone explains me why during this reccursive call in the example, below , instead of passing
an adjacent vertex (not visited), we instead pass the Boolean value "visited[w]" ?
(PS : This script can be found in the book Data Structures & algorithms with Javascript, Michael Mc Millian, page 155);
Thanks in advance
function topSort() {
var stack = [];
var visited = [];
for (var i = 0; i < this.vertices; i++) {
visited[i] = false;
}
for (var i = 0; i < this.vertices; i++) {
if (!visited[i]) {
this.topSortHelper(i, visited, stack);
}
}
for (var i = 0; i < stack.length; i++) {
if (stack[i] !== false & stack[i] !== undefined) {
console.log(this.vertexList[stack[i]]);
}
}
}
function topSortHelper(v, visited, stack) {
visited[v] = true;
for (var i = 0; i < this.adj[v]; i++) {
var w = this.adj[v][i];
if (!visited[w]) {
this.topSortHelper(visited[w], visited, stack);
}
}
stack.push(v);
}
topSortHelper is a depth-first-search, the first parameter should be a vertex id instead of boolean visited[w], it may be a typo:
Change the line " this.topSortHelper(visited[w], visited, stack);" to " this.topSortHelper(w, visited, stack);"
If you are still having problem with the top sort implementation, I have implemented a working version of top sort in my graph library hosted at github, you can take a look at its implementation :)
https://github.com/chen0040/js-graph-algorithms

Javascript - For loop vs Linked List vs ES6 Set to find two matching integers

I have prepared 2 Javascript functions to find matching integer pairs that add up to a sum and returns a boolean.
The first function uses a binary search like that:
function find2PairsBySumLog(arr, sum) {
for (var i = 0; i < arr.length; i++) {
for (var x = i + 1; x < arr.length; x++) {
if (arr[i] + arr[x] == sum) {
return true;
}
}
}
return false;
}
For the second function I implemented my own singly Linked List, in where I add the complementary integer to the sum and search for the value in the Linked List. If value is found in the Linked List we know there is a match.
function find2PairsBySumLin(arr, sum) {
var complementList = new LinkedList();
for (var i = 0; i < arr.length; i++) {
if (complementList.find(arr[i])) {
return true;
} else {
complementList.add(sum - arr[i]);
}
}
return false;
}
When I run both functions I clearly see that the Linked List search executes ~75% faster
var arr = [9,2,4,1,3,2,2,8,1,1,6,1,2,8,7,8,2,9];
console.time('For loop search');
console.log(find2PairsBySumLog(arr, 18));
console.timeEnd(‘For loop search’);
console.time('Linked List search');
console.log(find2PairsBySumLin(arr, 18));
console.timeEnd('Linked List search');
true
For loop search: 4.590ms
true
Linked List search: 0.709ms
Here my question: Is the Linked List approach a real linear search? After all I loop through all the nodes, while my outer loop iterates through the initial array.
Here is my LinkedList search function:
LinkedList.prototype.find = function(data) {
var headNode = this.head;
if(headNode === null) {
return false;
}
while(headNode !== null) {
if(headNode.data === data) {
return true;
} else {
headNode = headNode.next;
}
}
return false;
}
UPDATE:
It was a good idea to go back and have another think of the problem based the comments so far.
Thanks to #nem035 comment on small datasets, I ran another test but this time with 100,000 integers between 1 and 8. I assigned 9 to the first and last position and searched for 18 to make sure the entire array will be searched.
I also included the relatively new ES6 Set function for comparison thanks to #Oriol.
Btw #Oriol and #Deepak you are right. The first function is not a binary search but rather a O(n*n) search, which has no logarithmic complexity.
It turns out my Linked List implementation was the slowest of all searches. I ran 10 iterations for each function individually. Here the result:
For loop search: 24.36 ms (avg)
Linked List search: 64328.98 ms (avg)
Set search: 35.63 ms (avg)
Here the same test for a dataset of 10,000,000 integers:
For loop search: 30.78 ms (avg)
Set search: 1557.98 ms (avg)
Summary:
So it seems the Linked List is really fast for smaller dataset up to ~1,000, while ES6 Set is great for larger datasets.
Nevertheless the For loop is the clear winner in all tests.
All 3 methods will scale linearly with the amount of data.
Please note: ES6 Set is not backward compatible with old browsers in case this operation has to be done client side.
Don't use this. Use a set.
function find2PairsBySum(arr, sum) {
var set = new Set();
for(var num of arr) {
if (set.has(num)) return true;
set.add(sum - num);
}
return false;
}
That's all. Both add and has are guaranteed to be sublinear (probably constant) in average.
You can optimize this substantially, by pre-sorting the array and then using a real binary search.
// Find an element in a sorted array.
function includesBinary(arr, elt) {
if (!arr.length) return false;
const middle = Math.floor(arr.length / 2);
switch (Math.sign(elt - arr[middle])) {
case -1: return includesBinary(arr.slice(0, middle - 1), elt);
case 0: return true;
case +1: return includesBinary(arr.slice(middle + 1), elt);
}
}
// Given an array, pre-sort and return a function to detect pairs adding up to a sum.
function makeFinder(arr) {
arr = arr.slice().sort((a, b) => a - b);
return function(sum) {
for (let i = 0; i < arr.length; i++) {
const remaining = sum - arr[i];
if (remaining < 0) return false;
if (includesBinary(arr, remaining)) return true;
}
return false;
};
}
// Test data: 100 random elements between 0 and 99.
const arr = Array.from(Array(100), _ => Math.floor(Math.random() * 100));
const finder = makeFinder(arr);
console.time('test');
for (let i = 0; i < 1000; i++) finder(100);
console.timeEnd('test');
According to this rough benchmark, one lookup into an array of 100 elements costs a few microseconds.
Rewriting includesBinary to avoid recursion would probably provide a further performance win.
first of all find2PairsBySumLog function is not a binary search, it's a kind of brute force method which parses all the elements of array and it's worst case time complexity should be O(n*n), and the second function is a linear search that' why you are getting the second method to run fastly, for the first function i.e. find2PairsBySumLog what you can do is initialize binary HashMap and check for every pair of integers in array kind of like you are doing in the second function probably like
bool isPairsPresent(int arr[], int arr_size, int sum)
{
int i, temp;
bool binMap[MAX] = {0};
for (i = 0; i < arr_size; i++)
{
temp = sum - arr[i];
if (temp >= 0 && binMap[temp] == 1)
return true;
binMap[arr[i]] = 1;
}
}

Javascript: Detect and Remove a loop from cylic linked list

I have used Javascript to write a circular linked list and to detect and remove the loop.It is working fine untill the part of loop detection. How ever it is failing to remove the loopnode. More specifically: the removeLoop function of this code doesnot work.
Here is my code:
function Node(element){
this.element = element;
this.next = null;
}
//circular linked list class
function LList() {
this.head = new Node("head");
this.head.next = this.head;
this.find = find;
this.insert = insert;
this.display = display;
}
function find(item){
var curr = this.head;
while(curr.element != item){
curr = curr.next;
}
return curr;
}
//inserting items into linked list
function insert(newElem, after){
var newNode = new Node(newElem);
var curr = this.find(after);
newNode.next = curr.next;
curr.next = newNode;
}
function display() {
var currNode = this.head;
while ((currNode.next !== null) &&
(currNode.next.element !== "head")) {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
function findPrevious(item){
var curr = this.head;
while(curr.next !== null && curr.next.element !== item){
curr =curr.next;
}
return curr;
}
//creating a linkedlist object
var furniture = new LList();
furniture.insert("chair","head");
furniture.insert("table", "chair");
furniture.insert("couch", "table");
furniture.insert("stool","couch");
//furniture.display();
//detecting if a linked list is circular
function detectALoop(list){
var slow = list.head;
var fast = list.head;
while(slow && fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
removeLoop (slow, list);
return 1;
}
}
return 0;
}
//This part of the code doesnot work
function removeLoop(loopNode, list)
{
var ptr1 = loopNode;
var ptr2 = loopNode;
var looplen = 1,i;
// count the number of nodes in loop
while(ptr1.next != ptr2)
{
ptr1 = ptr1.next;
looplen++;
}
console.log(looplen)
ptr1 = list.head;
ptr2 = list.head;
for(i=0; i <= looplen; i++)
{
ptr2 = ptr2.next;
}
while(ptr2.next != ptr1.next)
{
ptr1 = ptr1.next;
ptr2 = ptr2.next;
}
ptr2.next = null; // breaking the loop
}
console.log(detectALoop(furniture))
furniture.display();
You are making this a lot more complicated than it needs to be if the loop has to be back onto the first element.
function breakLoop(list) {
var head = list.head, tail = head, len = 1;
while (tail.next != head) {
len++;
tail = tail.next;
}
tail.next = null;
console.log(len.toString());
}
Now if you may need to handle any arbitrary loop, I still have no idea what you need 3 loops for. Use an ES6 Set; most browsers now support this, I believe. I'm going to go ahead and return the length instead of logging it.
function breakLoopAnywhere(list) {
var seen = new Set, node = list.head;
while (!seen.has(node.next)) {
seen.add(node);
node = node.next;
}
node.next = null;
return seen.size;
}
If you don't have sets, you can hack it with an array, replacing has with indexOf and add with push.
If you feel you must have the ability to detect a loop vs a non-looping list without breaking it:
// takes a node, returns the node
// that points backwards on its next
function getLoopNode(node) {
var seen = new Set;
do {
seen.add(node);
} while (!seen.has(node.next) && node = node.next)
return node;
}
function detectLoop(node) {
return getLoopNode(node) != null;
}
function breakLoop(node) {
node = getLoopNode(node);
if (node) node.next = null;
}
Your detectALoop is less complicated, but it's wrong. The only loop this will detect is if node 2i loops back onto node i. But the list could be 3 elements long looping onto the start; it could be lots of numbers that aren't 2i and i. Since there are probably a lot of numbers, way too many to try them all, you can't fix this strategy. There is no clever way to find cycles in a graph that is any faster or more intuitive than the one I wrote above. As far as I know.
This variable is messed up...
var looplen = 1,i;
It looks like you want it to be a 1.
Your removeLoop code is wrong, it never terminates:
let's assume this list:
A -> B -> C -> A
with loop length 3.
You correctly find the loop length, 3, you then set ptr1 and ptr2 to the head of the list, and then call .next on ptr2 for the length of the loop + 1 times (because of <=).
// for i = 0; i <= 3
A.next -> B // i = 0
B.next -> C // i = 1
C.next -> A // i = 2
A.next -> B // i = 33
So in the end you have ptr2 = B and ptr1 = A, i.e. ptr2 === ptr1.next!
One is the next of the other, and in the while loop you advance both until one is equal to the other, but they will never be, because they always be one the next of the other!
If you change the <= to just < it works, but the second while loop is actually useless.

How to sort and slice an array of objects

I have an array of shots. I have been able to take that array and loop through it to get all shots that occurred on hole #1 and then rearrange them in order based on "shot_number". I now need to do this for every hole and to create an array for each hole (ex: holeArray1, holeArray2). I have attempted a number of solutions to increment x but if I do I end up missing some shots that occurred on certain holes.
How can I refactor this function to create this array for every hole without just copying and pasting the code and changing the variable x myself? Thank you for your help. I know I should be able to figure this one out but am struggling.
$scope.createHoleShotsArrays = function () {
var i = 0;
var x = 1;
var holeArray = [];
var len = $scope.shots.length;
for (; i < len; i++) {
if ($scope.shots[i].attributes.hole == x) {
holeArray.push($scope.shots[i]);
holeArray.sort(function (a, b) {
if (a.attributes.shot_number > b.attributes.shot_number) {
return 1;
}
if (a.attributes.shot_number < b.attributes.shot_number) {
return -1;
}
// a must be equal to b
return 0;
});
}
}
console.log(holeArray);
};
Push the items you want into arrays, and sort them once. I don't have cases to test the code. You may modified it a little if something goes wrong.
$scope.createHoleShotsArrays = function() {
var holeArrays = [];
$scope.shots.forEach(function(shot) {
if (holeArrays.length < shot.attributes.hole) {
holeArrays[shot.attributes.hole - 1] = [];
}
holeArrays[shot.attributes.hole - 1].push(shot);
});
holeArrays.forEach(function(arr) {
arr.sort(function(a, b) {
return a.attributes.shot_number - b.attributes.shot_number;
});
});
console.log(holeArrays);
};

JS crashes sometimes with Timer scramble

My Javascript timer is for people with a rubiks cube with generates a scramble (nevermind all this, but just to tell you I'm generating after each solve a new scramble will be generated) and my scrambles do actually have a while (true) statement. So that does crash my script, but it 95/100 times stops just before the script crashes but I don't wanna have any times.
Let me explain a bit more detailed about the problem.
Problem: javascript crashes because my script takes too long to generate a scramble.
Below you have 3 functions I use.
This function generates a scramble with the Fisher-Yates shuffle.
Timer.prototype.generateScramble = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
This function validates the input e.g. I receive an array as the following:
Here I only have to check the first character. That's why I use the seconds [ ] notation. I don't want people get an F with an F2 e.g.
var scr = ["F","R","U","B","L","D","F2","R2","U2","B2","L2","D2","F'","R'","U'","B'","L'","D'"]
Timer.prototype.validateScramble2 = function(array) {
var last = array.length-1;
for (var i = 0; i < array.length-1; i++) {
if (array[i][0] == array[i+1][0]) {
return false;
}
}
for (var i = 0; i < array.length-2; i++) {
if (array[i][0] == array[i+2][0]) {
return false;
}
}
if (array[0][0] == [last][0]) {
return false;
}
return true;
};
The above functions are just waiting to be called. Well in the function below I use them.
Timer.prototype.updateScramble2 = function(scrambleArr, len, type) {
var self = this;
var scramble = '', j, updatedArr = [];
while (updatedArr.length < len) {
j = (Math.floor(Math.random() * scrambleArr.length));
updatedArr.push(scrambleArr[j]);
}
while (!self.validateScramble2(updatedArr)) {
updatedArr = self.generateScramble(updatedArr);
}
for (var i = 0; i < updatedArr.length; i++) {
scramble += updatedArr[i] + ' ';
}
scrambleDiv.innerHTML = scramble;
};
I assume you guys understand it but let me explain it briefly.
The first while-loop adds a random value from the given array(scrambleArr) into a new array called updatedArr.
The next while-loop calls the validateScramble2() function if there isn't in an array F next to an F2.
The for-loop adds them into a new variable added with a whitespace and then later we show the scramble in the div: scrambleDiv.innerHTML = scramble;
What do I need know after all this information?
Well I wanna know why my updateScramble2() functions lets my browser crash every time and what I do wrong and how I should do it.
I'm not entirely sure I understand the question, but from the way your code looks, I think you have an infinite loop going on. It appears as if validateScramble2 always returns false which causes your second loop in updateScramble2 to perpetually run.
I suggest you insert a breakpoint in your code and inspect the values. You could also insert debugger; statements in your code, works the same way. Open dev tools prior to doing these.
A suggestion is instead of using loops, use a timer. This breaks up your loop into asynchronous iterations rather than synchronous. This allows the browser breathing space for other operations. Here's an example of a forEachAsync:
function forEachAsync(array, callback){
var i = 0;
var timer = setInterval(function(){
callback.call(null, array[i]);
if(++i >= array.length) clearInterval(timer);
}, 0);
}
forEachAsync([1,2,4], function(item){
console.log(item);
});
You can take this further and use Promises instead of callbacks.

Categories