我有一个
Graph对象(这是在Perl中),我计算了它的
transitive closure(即用于解决
all-pairs shortest paths问题).
从这个对象,我对计算感兴趣:
>来自任何顶点的最短路径u – >诉
>所有顶点的距离矩阵.
>一般可达性问题.
>一般图形特征(密度等).
该图有大约2000个顶点,因此计算传递闭包(使用Floyd-Warshall的算法)需要几个小时.目前我只是缓存序列化对象(使用Storable,所以它已经非常有效).
我的问题是,反序列化这个对象仍然需要相当长的时间(一分钟左右),并消耗大约4GB的RAM.这对我的申请来说是不可接受的.
因此,我一直在考虑如何设计数据库模式以将此对象保持为“展开”形式.换句话说,预先计算所有对最短路径,并以适当的方式存储它们.然后,也许使用存储过程来检索必要的信息.
我的另一个问题是,我没有数据库设计的经验,也没有关于实现上述内容的线索,因此我的帖子.我也想听听其他我可能无视的解决方案.谢谢!
最佳答案 首先,听起来你需要两个实体:顶点和边缘,也许还有几个表格用于结果.我建议一个存储节点到节点信息的表.如果A可从Y到达,则关系将获得可达属性.所以这里
Vertex:
any coordinates (x,y,...)
name: string
any attributes of a vertex*
Association:
association_id: ID
association_type: string
VertexInAssociation:
vertex: (constrained to Vertex)
association: (constrained to association)
AssociationAttributes:
association_id: ID (constrained to association)
attribute_name: string
attribute_value: variable -- possibly string
*您可能还希望将顶点属性存储在表中,具体取决于它们的复杂程度.
我添加Association的复杂性的原因是边缘不被认为是方向性的,它简化了查询以将两个顶点都视为一组顶点“connected-by-edge-x”的成员
因此,边缘仅仅是边缘类型的关联,其具有距离属性.路径是路径类型的关联,它可能具有跳跃属性.
可能还有其他更优化的模式,但这个模式在概念上是纯粹的 – 即使它没有使“边缘”的第一类概念成为第一类实体.
要创建最小边缘,您需要执行以下操作:
begin transaction
select associd = max(association_id) + 1 from Association
insert into Association ( association_id, association_type )
values( associd, 'edge' )
insert
into VertexInAssociation( association_id, vertex_id )
select associd, ? -- $vertex->[0]->{id}
UNION select associd, ? -- $vertex->[1]->{id}
insert into AssociationAttributes ( association_id, association_name, association_value )
select associd, 'length', 1
UNION select associd, 'distance', ? -- $edge->{distance}
commit
您可能还希望创建各种类型的关联类型.这样“边缘”关联自动被计为“可达”关联.否则,您可能还想在其中插入UNION select associd,reachable,’true’.
>然后,您可以查询两个顶点的可到达关联的并集,并将它们作为可到达的关联转储到另一个节点(如果它们不存在)并将现有长度属性值1转储到length属性中.
但是,您可能想要所有这些的ORM,并且只是在Perl中操作它.
my $v1 = Vertex->new( 'V', x => 23, y => 89, red => 'hike!' );
my $e = Edge->new( $v1, $v2 ); # perhaps Edge knows how to calculate distance.