Cypher查询通过有向加权图查找路径以填充有序列表

时间:2014-02-23 19:53:18

标签: graph neo4j cypher

我是Neo4j的新手,我正在尝试围绕Cypher中的以下问题。

我正在寻找一个节点列表,按照升序的访问顺序排序,经过n次路径迭代后,每个节点都将节点添加到列表中。访问类型取决于深度和边缘成本。因为最终列表代表一系列节点,所以您也可以将其视为路径路径。

描述

  • 我的图表有一个初始起始节点(START),是方向性的,大小未知,并且有加权边。

  • 节点只能在首次访问时添加到列表中一次(例如,当访问节点时,我们将与最终列表进行比较,并添加节点是否已在列表中)。 / p>

  • 每个边缘只能行走一次。

  • 我们只能访问下一个相邻的,成本最低的节点。

  • 有两个基础层次结构:深度(越接近START越好)和边缘成本(到达下一个相邻节点所花费的成本越低越好)。深度遵循以下示例中的字母顺序。成本属性是整数,但在示例中显示为字符串(例如“costs1”表示边缘成本= 1)。

  • 每个路径都以“可用”的最小深度的起始节点开始(=拥有未经处理的出站边缘)。在下面的例子中,从START发出的所有边缘都会在某个时刻耗尽。对于下一次运行,我们将继续使用A作为起始节点。

  • 当路径无法再继续时(即没有可用的出站边缘),路径运行完成

  • 当列表包含y个节点时,我们就完成了,这些节点可能代表也可能不代表遍历。

有关如何使用Cypher查询解决此问题的任何想法?


示例数据: http://console.neo4j.org/r/o92sjh

这就是:

  1. 我们从START开始,沿着可用的最低成本边缘行驶,到达A. - > A获得列表中的#1点和START中的cost1边缘 - [:costs1] - > a因为我们刚刚使用它而被淘汰。

  2. 我们在A.最低成本边缘(成本1)回到START,这是一个禁忌,所以我们也把这个优势放在桌面上并选择下一个可用的最低成本优势(费用2),将我们引向B. - >我们将B输出到列表并消除a - [:costs2] - > b中的边缘。

  3. 我们现在在B.最低成本优势(成本1)回到START,这是一个禁忌,所以我们也消除了这个优势。下一个成本最低的边缘(成本2)将我们引向C. - >我们将C输出到列表中,消除了B和C之间刚刚行进的边缘。

  4. 我们在C上并且从C开始继续从最低成本关系到G. - >我们将G输出到列表并消除c - [:costs1] - > g中的边缘。

  5. 我们在G上,然后通过g - [:costs1] - > e继续前进到E. - > E列在清单上,消除了刚刚走过的边缘。

  6. 我们在E上,它与I只有一个关系。我们承担1的费用并继续前往I. - >我进入名单,E的“cost1”优势被淘汰。

  7. 我们在I上,没有出站边缘,因此没有相邻节点。我们的路径结束,我们返回到START,继续使用剩下的边缘迭代整个过程。

  8. 我们正在开始。其最低可用出站边缘为“cost3”,将我们引向C. - > C已经在列表中,所以我们只是消除了START - [:costs3] - > c中的边缘,然后转到下一个可用的最低成本节点,即F.注意,现在我们已经用尽所有边缘来自START。

  9. 我们在F上,这导致我们J(成本= 1) - > J列在名单上,边缘消失了。

  10. 我们在J上,这导致我们L(成本= 1) - > L列在名单上,边缘消失了。

  11. 我们在L,这导致我们N(成本= 1) - > N在列表中,边缘被消除。

  12. 我们在N上,这是一个死胡同,意味着我们的第二条路径结束了。因为我们无法从START开始下一次运行(因为它没有可用的边缘),所以我们继续进行下一个最小深度的可用节点,即A.

  13. 我们在A上,这导致我们B(成本= 2) - > B已经在列表中,我们抛弃边缘。

  14. 我们在B上,这导致我们D(成本= 3) - > D列在清单上,边缘消失了。

  15. 输出/最终列表/“路径路径”(希望我能正确完成此操作):

    A
    
    B
    
    C
    
    G
    
    E
    
    I
    
    F
    
    J
    
    L
    
    N
    
    D
    
    M
    
    O
    
    H
    
    K
    
    P
    
    Q
    
    R
    

    CREATE (  START { n:"Start" }),(a { n:"A" }),(b { n:"B" }),(c { n:"C" }),(d { n:"D" }),(e { n:"E" }),(f { n:"F" }),(g { n:"G" }),(h { n:"H" }),(i { n:"I" }),(j { n:"J" }),(k { n:"K" }),(l { n:"L" }),(m { n:"M" }),(n { n:"N" }),(o { n:"O" }),(p { n:"P" }),(q { n:"Q" }),(r { n:"R" }),
    
    START-[:costs1]->a, START-[:costs2]->b, START-[:costs3]->c, 
    a-[:costs1]->START, a-[:costs2]->b, a-[:costs3]->c, a-[:costs4]->d, a-[:costs5]->e, 
    b-[:costs1]->START, b-[:costs2]->c, b-[:costs3]->d, b-[:costs4]->f, 
    c-[:costs1]->g, c-[:costs2]->f, 
    d-[:costs1]->g, d-[:costs2]->f, d-[:costs3]->h, 
    e-[:costs1]->i, 
    f-[:costs1]->j, 
    g-[:costs1]->e, g-[:costs2]->j, g-[:costs3]->k, 
    j-[:costs1]->l, j-[:costs2]->m, j-[:costs3]->n, 
    l-[:costs1]->n, l-[:costs2]->f, 
    m-[:costs1]->o, m-[:costs2]->p, m-[:costs3]->q,
    q-[:costs1]->n, q-[:costs2]->r;
    

2 个答案:

答案 0 :(得分:0)

正在寻找的算法是对TSP的nearest neighbor (greedy) heuristic的修改。对算法的更改会产生如下算法:

  1. 站在起始顶点上的任意顶点作为当前顶点。
  2. 找出最短的未访问边,E,连接当前顶点,如果没有这样的边,则终止。
  3. 将当前顶点设置为V。
  4. 将E标记为已访问。
  5. 如果访问边缘的数量已达到限制,则终止。
  6. 转到第2步。
  7. 与原始算法一样,输出是访问顶点。

    要处理用例,请允许算法接受一组已访问过的边作为附加输入。而不是总是从一组空的访问边开始。然后,您只需再次调用该函数,但使用一组访问边而不是空集,直到起始顶点仅导致访问边。

答案 1 :(得分:0)

(抱歉,我是网站上的新用户,无法发表评论) 我被聘请找到这个特定查询的解决方案。之后我才知道这个问题。我不会在这里完整发布,但我愿意讨论解决方案并获得任何感兴趣的人的反馈。

事实证明单独使用cypher是不可能的(好吧,我无法找到自己的方式)。所以我用Neo4j绑定编写了一个java函数来实现它。

解决方案是单线程,扁平(无递归),非常接近@Nuclearman的描述。它使用两个数据结构(有序映射),一个用于记住访问边缘,另一个用于保存“开始”节点列表(用于路径用完时):

  1. 遵循最小成本路径(记住访问边缘,按深度/成本存储节点)
  2. 在路径末尾,选择一个新的起始节点(最小深度,然后是最小成本)
  3. 按照访问顺序报告任何新节点
  4. 哈希集的使用,加上边访问一次的事实,使得它快速且内存效率高。