在图中查找最近节点的算法

时间:2014-03-16 07:04:45

标签: algorithm neo4j graph-theory graph-algorithm graph-databases

我有一个很大的道路网络图。假设我知道一个特定的位置(比如源节点)。现在我的兴趣是找出特定范围内的所有最近邻居节点。假设我想找出已知位置/源节点周围20公里范围内的所有位置(其他节点)。

我知道BFS或Dijkastra的算法可以解决这个问题,但我觉得这些在我的应用程序中效率低下,因为我的应用程序需要一次又一次地处理这类查询。

还有其他算法或技术可以实现这个目标。

假设这是加权图,节点表示边表示两个相应位置之间距离的位置。

编辑: diskastra可以解决问题,但存储结果呢?如果我在一定数量的查询后存储所有可能对的结果,那么缓存大小是多少?如何解决空间效率低下问题?

我也听说过kd-tree,r-tree索引等。它们在这种情况下有用吗?

编辑:为了更高级,我愿意使用neo4j图形数据库制作此图表。我看过neo4j有一个名为“neo4j spatial'其中R-Tree索引用于此目的但我想使用有向图概念而不是空间索引库。有没有办法做到这一点?

5 个答案:

答案 0 :(得分:3)

您要使用的是Dijkstra's algorithm

它确实完全按照您的意愿行事 - 获取源节点,找到所有成本最低的节点,直到成本达到指定大小(IE 20km)

  

我认为这些在我的应用程序中效率低下,因为我的应用程序需要一次又一次地处理这些类型的查询。

您是否考虑过缓存给定源节点的结果?只要图表永远不会改变,就永远不需要重新计算。

如果您的图表太大,那么还可以选择Hierarchical Graph - 它将图形抽象为部分并预处理这些部分之间的路径。这里的链接特指A *,但它使用的抽象可以应用于任何搜索方法。

修改:Mehrdad的答案中的Transit Nodes是使用Dijkstra专门搜索的分层图。

还值得考虑是否需要图表。如果您的节点位于线性空间上,并且destination.position - source.position总是给出确切的距离,那么将它们存储在列表中会更快。

答案 1 :(得分:2)

我不太了解他们,但我听说过一些可能有用的技巧:

Here's也谈了一些名为" Highway dimension"这可以用来证明这些技术的时间限制。

答案 2 :(得分:0)

single source all destinations shortest path with a stopping condition用于初始定义。但是你添加了再次调用此查询。然后问题变成All pairs shortest paths图形问题。这通常通过“动态编程”解决,Floyd–Warshall algorithm O(V ^ 3)时间复杂度, O(V ^ 2)空间的示例解决方案复杂。

鉴于Floyd-Warshal,图表应根据您的范围限制进行划分,但重叠区域可以消除O(V ^ 2)空间复杂度。

例如;

200公里区域,第一个居中区域是x_1 = 20公里,y_1 = 20公里,这是一个边长40公里的广场。第二个方格中心x_2 = 40km,y_2 = 40km。方形区域代表四次。对于每个分区,进行Floyd-Warshall算法。考虑到所有节点,这比整体计算的O(V ^ 2)空间复杂度要好得多。根据我的计算,原始算法需要 2.5B 节点来存储相关信息,而此处提出的算法要求 1M 节点存储,假设您有 50K 节点。

创建结果矩阵后,您将可以即时访问限制范围内最近的节点。

答案 3 :(得分:0)

使用图数据库方法,我们可以使用infiniteGraph 图数据库和DO 查询语言。我们创建一个重量计算器,然后在查询中使用它,如下所示:

CREATE WEIGHT CALCULATOR shortestRoute {
            minimum:    0,
            default:    0, 
            edges: {
                ()-[r:Road]->(): r.distance
            }
};
    
Match m = max weight 8.0 shortestRoute 
            ((:Town {name == 'A'})-[*..10]->(t:Town)) 
            GROUP BY t.name 
            RETURN t.name as Name;

在此查询中,我们将“最大重量”指定为 8.0 以及我们要使用的 WEIGHT CALCUALTOR。然后我们指定起始城镇 {name == 'A'} 和我们想要去的度数 [*..10]。然后我们指定没有谓词的端点 (t:Town),它是一个带有标签“t”的 Town 类型的节点。我们按 t.name 分组并返回 t.name 作为 NAME。

此查询中使用的图形是:

enter image description here

查询结果如下:

DO> Match m = max weight 8.0 shortestRoute ((:Town {name == 'A'})-[*..10]->(t:Town)) GROUP BY t.name RETURN t.name as Name;

{
  _Projection
  {
    Name:'B'
  },
  _Projection
  {
    Name:'D'
  },
  _Projection
  {
    Name:'E'
  },
  _Projection
  {
    Name:'F'
  }
}

设置(架构和示例数据)如下:

UPDATE SCHEMA {
    
    CREATE CLASS Town  {
        name                : STRING,
                
        roadsIn             : List { Element: Reference { EdgeClass: Road, EdgeAttribute: from }, CollectionTypeName: TreeListOfReferences },
        roadsOut            : List { Element: Reference { EdgeClass: Road, EdgeAttribute: to }, CollectionTypeName: TreeListOfReferences }      
    }
    
    CREATE CLASS Road {
        name                : String,
    
        from                : Reference { referenced: Town, Inverse: roadsOut },
        to                  : Reference { referenced: Town, Inverse: roadsIn },
                
        distance            : REAL { Storage: B32 },
        avgTravelTime       : REAL { Storage: B32 },
        stopLightCount      : INTEGER { Encoding: Signed, Storage: B16 }
    }
};


let townA = create Town { name: "A" };
let townB = create Town { name: "B" };
let townC = create Town { name: "C" };
let townD = create Town { name: "D" };
let townE = create Town { name: "E" };
let townF = create Town { name: "F" };
let townG = create Town { name: "G" };
let townH = create Town { name: "H" };

let ab = create Road { name: "AB", distance: 4.0, stopLightCount: 1, from: $townA, to: $townB };

let bc = create Road { name: "BC", distance: 5.0, stopLightCount: 2, from: $townB, to: $townC };

let cd = create Road { name: "CD", distance: 6.0, stopLightCount: 3, from: $townC, to: $townD };

let cH = create Road { name: "CH", distance: 10.0, stopLightCount: 0, from: $townC, to: $townH };

let ad = create Road { name: "AD", distance: 3.0, stopLightCount: 0, from: $townA, to: $townD };

let ae = create Road { name: "AE", distance: 4.0, stopLightCount: 3, from: $townA, to: $townE };

let ed = create Road { name: "ED", distance: 2.0, stopLightCount: 1, from: $townE, to: $townD };

let ef = create Road { name: "EF", distance: 4.0, stopLightCount: 7, from: $townE, to: $townF };

let fg = create Road { name: "FG", distance: 3.0, stopLightCount: 0, from: $townF, to: $townG };

let dg = create Road { name: "DG", distance: 8.0, stopLightCount: 6, from: $townD, to: $townG };

let dH = create Road { name: "DH", distance: 8.0, stopLightCount: 9, from: $townD, to: $townH };

let gH = create Road { name: "GH", distance: 8.0, stopLightCount: 4, from: $townG, to: $townH };

查询对可能的结果路径进行快速/早期修剪,因此它只加载确定结果所需的数据。

答案 4 :(得分:-1)

如果图表由adjecency矩阵或列表表示, 你需要只扫描一行(对于矩阵)或列表(对于adjecency列表),所以这个操作并不复杂。

对于具有n个节点的图,adjecency矩阵说graph [] []的大小为n * n, 如果源节点是s, 然后简单地扫描图[s] [i],其中i从0变为n-1并检查图[s] [i]< = DISTANCE