2016-09-11 3 views
2

Я пытаюсь реализовать алгоритм обнаружения циклов в ориентированном графе (только исходящие соединения) с использованием JointJS. Я написал следующий код, который не работает должным образом.JointJs Cycle Detection

 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; 
    } 

Было бы очень полезно, если бы кто-нибудь мог мне помочь в этом.

+0

Так что, как ожидается, поведение и что реальное поведение, которое отличается от ожидаемого? Кроме того, вы получили '}' missing, это просто ошибка копирования или вы получили его в своем коде, и это вызывает ошибки? – YakovL

+0

A -----> B ------> C ------> D ------> E: Это довольно линейная структура, но по-прежнему в соответствии с моей программой, это показывает, что он имеет цикл. Я также пробовал со многими другими структурами, которые на самом деле имели петли, и если я удаляю циклы из этих структур, он работает, но в вышеупомянутом случае использования это не удалось. The} - это просто ошибка копирования. Пожалуйста, проигнорируйте это. Для информации, я также пытался со следующим: A ----> B C -----> D -----> D C ------> B B ------> D D -------> B В этом случае мои программы показывают существование цикла в этой структуре графика. –

ответ

0

Проблема преемника - это не то же самое, что прямой преемник. Проверьте значение graph.getSuccessors(comp): если lib использует согласованные имена функций, которые должны содержать B, C, D и E для первого запуска. Таким образом, вы отмечаете те, которые были посещены, но также запускаете hasCycle(c, visited.slice(), ++level), где вы снова проверяете узлы, начиная с D (я думаю, что D является первым, для которого вы получаете «уже посещенный» случай).

Во-первых, я бы рекомендовал, чтобы избавиться от удвоения итерацию в вашей функции, сделать что-л как

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; 
} 

И второе, что более важно, попробуйте graph.getNeighbors метод вместо graph.getSuccessors (не забудьте проверить только соседей в одном направлении).

+0

Спасибо за быстрый ответ. Я пробовал этот модифицированный метод hasCycle() и до сих пор работает над проверенными мной графами. Только небольшая небольшая ошибка опечатки во второй строке - существование ** i **. Я буду держать вас в курсе здесь, если я нахожу что-то интересное в выполнении этого алгоритма. –

+0

@AmitKumarMondal 'i' во второй строке по замыслу. Вы можете поместить переменные разделенным запятой образом: 'var a = ..., b = ...;' или определить отдельно 'var a = ...; var b = ...; 'и поместите' var b' в выражение 'for', но это все равно. Это всего лишь вопрос стиля кода. – YakovL

+0

Я пытался со следующими: 'вар преемники = graph.getNeighbors (сравн { \t \t \t 'opt.outbound': истинный \t \t});' Это на самом деле не удалось в сценарии я уже упоминал ранее, в котором мой предыдущий алгоритм также не удался. Согласно API JointJS, 'getNeighbors()' должен был быть реальным методом, который нужно использовать, но в этом сценарии это не сработало. –

0

Я считаю, что я нашел основную причину такого неустойчивого поведения, используя этот алгоритм.

На самом деле метод JointJS API getElements() возвращает массив элементов в порядке ввода в ячейках графа. Это означает, что если вы создаете 3 элемента A, B, C, чтобы создать граф вроде A-->B-->C, но вы добавили эти элементы в заказ A сначала, C второй и B в конце. Теперь, согласно условию if (graph.getPredecessors(elem).length > 0) && !elem.isLink() && hasCycle(elem, visited, level), обнаружение цикла фактически начинается с C. В этом случае C не имеет исходящего соседа и помечен как посещенный. Теперь, на следующей итерации, проверяется B (поскольку он вводится в График после C), и он имеет C в качестве исходящего соседа. Теперь C снова оценивается, но C уже отмечен как посетивший. Следовательно, это показывает существование цикла.

Итак, мы считаем, что наш поиск на графике должен начинаться с узлов, у которых нет предшественников. Например, в предыдущем примере наш поиск должен начинаться всегда с A. График также может содержать один или несколько узлов, которые не имеют предшественника. В этом сценарии мы также должны начать поиск с узлов, у которых нет предшественника. Например, A1-->B, A1-->C, A2-->B, A2-->C вот так. Наше обнаружение должно начинаться с A1 и A2.

Я изменил методы следующим которая соответствует предыдущей стратегии:

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; 
}