Commit 6dd1ab34 authored by Nicolas Dumazet's avatar Nicolas Dumazet

algorithm: a topological sort in a graph can be much simpler

There were three surprising things in the previous version:
 * the XXX mention: leafs can indeed be detected at construction time
 * the need to track both children and parents of a node: usually only one
    or the other is enough to work efficiently on a graph
 * the idiom:
      if n in list: list.remove(n)
      list.append(n)

Replace it by a generic sort on node depth. A dictionary is used for lookups to
avoid linear list lookups, and only the successor relation is kept (parent_dict)

Also explicit in comments the graph we are building/the order expected for code
clarity (ie avoid confusion wrt what is the parent/child of the graph node)


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@31556 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 374799a6
...@@ -203,39 +203,54 @@ class TradeCondition(Path, Transformation, XMLMatrix): ...@@ -203,39 +203,54 @@ class TradeCondition(Path, Transformation, XMLMatrix):
reference_list.append(reference) reference_list.append(reference)
trade_model_line_composed_list.append(trade_model_line) trade_model_line_composed_list.append(trade_model_line)
# build a graph # build a graph of precedences
father_dict = {} # B---\
child_dict = {} # \
# C-----> A
# A is parent of B and C, and returned order should be
# (BC) A
# where (BC) cannot be sorted
parent_dict = {}
# B and C are leaves
leaf_line_list = []
for line in trade_model_line_composed_list: for line in trade_model_line_composed_list:
father_dict.setdefault(line, []) has_child = False
for other_line in trade_model_line_composed_list: for other_line in trade_model_line_composed_list:
if line == other_line: if line == other_line:
continue continue
child_dict.setdefault(other_line, []) parent_dict.setdefault(other_line, [])
for base_application in line.getBaseApplicationList(): for base_application in line.getBaseApplicationList():
if base_application in other_line.getBaseContributionList(): if base_application in other_line.getBaseContributionList():
father_dict[line].append(other_line) parent_dict[other_line].append(line)
child_dict[other_line].append(line) has_child = True
if not has_child:
leaf_line_list.append(line)
final_list = [] final_list = []
if len(father_dict): if len(parent_dict):
# find roots elements # longest distance to a root (A)
# XXX maybe this can be done while building the graph depth = {}
root_line_list = [] tovisit = leaf_line_list
for k, v in child_dict.iteritems(): while tovisit:
if len(v) == 0: node = tovisit[-1]
root_line_list.append(k) if node in depth:
# sort graph according to predecessors tovisit.pop()
f = root_line_list[:] continue
tmp = None
final_list = root_line_list[:] parent_list = parent_dict.get(node, [])
while len(f): if len(parent_list) == 0:
tmp = f.pop(0) depth[node] = 0
for predecessors in father_dict[tmp]: tovisit.pop()
f.append(predecessors) else:
if predecessors in final_list: for parent in parent_list:
final_list.remove(predecessors) if parent not in depth:
final_list.append(predecessors) tovisit.append(parent)
final_list.reverse() if tovisit[-1] == node:
depth[node] = max(depth[p] for p in parent_list) + 1
tovisit.pop()
# the farther a line is from a root, the earlier it should be returned
final_list = sorted(depth.iterkeys(), key=depth.get, reverse=True)
if len(final_list) == 0: if len(final_list) == 0:
# at least return original lines retrieved # at least return original lines retrieved
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment