从成对数据中确定父,子

时间:2013-07-11 15:26:26

标签: algorithm

我需要从一些不寻常的数据中确定父/子关系。

航班号是营销创作,它们很奇怪。航空公司X的22号航班可能是指X和Y之间的单程旅行。同一航空公司的44号航班实际上可能是指城市对之间的多次航班。例如:

Flight 44:  Dallas - Paris
Flight 44:  Dallas - Chicago
Flight 44:  Chicago - New York
Flight 44:  New York - Paris
Flight 44:  Chicago - Paris
Flight 44:  Dallas - New York

现实 - 这是他们工作的方式。当我从“航班号和城市对的大列表”中提取数据时,我得到了这44个航班的6种组合。我每个都有乘客数量,所以如果有10个人在达拉斯 - 巴黎飞行,我需要拿这10个乘客并将它们添加到DAL - CHI,CHI - NY和NY - PAR段。

从所有细分的列表中,我需要弄清楚“啊,这是从达拉斯飞往巴黎的航班” - 然后当我看到乘客负载时,我可以相应地增加城市到城市的实际负载像这样:

- Value associated with AD -- > increment segments AB, BC, CD
- value associated with AC -->  increment only segments AB, BC
- value associated with AB --> increment only segment AB
etc.

假设我得到这样的航班44的无序值列表:(DAL-CHI,CHI-NYC,NYC-PAR,DAL-NYC,DAL-PAR,CHI-PAR)。如何计算父子结构,比较这6种组合中的这4个值?

5 个答案:

答案 0 :(得分:4)

制剂

a_i -> b_i成为航班44 i的成对列表中的i = 1..M条目。

V成为所有唯一a_ib_i值的集合:

V = {a_i | i = 1..M} U {b_i | i = 1..M}

E成为所有对(a_i, b_i)的集合:

E = {(a_i, b_i) | i = 1..M}

然后G = (V, E)directed acyclic graph,其中顶点V是城市,有向边E对应于列表中的条目a_i -> b_i

算法

您要找的是图G的{​​{3}}。链接的维基百科页面具有此算法的伪代码。

这将为您提供城市的线性排序(在您的示例中:[Dallas, Chicago, New York, Paris]),这与您的初始列表中存在的所有排序约束一致。如果您的初始列表包含少于|V| choose 2个对(意味着没有完整的约束集),那么您的集V中的城市可能会有多个一致的拓扑排序。

答案 1 :(得分:1)

注意:这是常识性分析,但是请参阅Timothy Shields解决方案,他将问题确定为拓扑排序问题,因此具有已知的计算复杂性和关于唯一性的已知条件。

我将尝试从您的答案中提取问题的核心,以便正式描述它。

在上面的示例中,您实际上有四个节点(城市),为简洁起见,表示为D,P,C和NY。您有一组有序对(x,y),它们被解释为“在该航班上,节点x在节点y之前”。将其写为x<y,我们实际上有以下内容:

(对于044航班):

D < P
D < C
C < NY
NY < P
C < P
D < NY

根据这些约束,我们希望找到一个有序元组(x, y, z, w),使得x < y < z < w和上述约束成立。我们知道解决方案是(x=D, y=C, z=NY, w=P)

注意:可能在您的数据库中,集合中的第一个元素始终是“起始 - 目标对”(在我们的示例中为D<P)。但是,随后的分析并没有太大变化。

如何以编程方式找到这个有序的元组?我对算法有相对公平的了解,但我不知道解决这个问题的标准方法(其他用户可能会在这里提供帮助)。我担心结果的独特性。它可能是对数据完整性的良好单元测试,您应该要求该有序元组的解决方案是唯一的,否则您可能会随后增加错误的段。

当我们处理唯一性问题时,我建议生成节点的所有排列,并显示在给定约束条件下可行的所有解决方案。

天真的实现可能如下所示:

import itertools 

nodes = ['D', 'P', 'NY', 'C']

result = [ot
          for ot in itertools.permutations(nodes) # ot = ordered tuple
          if ot.index('D') < ot.index('P')
          if ot.index('D') < ot.index('C')
          if ot.index('C') < ot.index('NY')
          if ot.index('NY') < ot.index('P')
          if ot.index('C') < ot.index('P')
          if ot.index('D') < ot.index('NY')
          ] 

print result

# displays: [('D', 'C', 'NY', 'P')]

如果节点数量较少,这种“天真”实现可能就足够了。如果数字越高,我建议以这样的方式实现它,即有效地使用约束来修剪解空间(问我是否需要提示)。

答案 2 :(得分:1)

从您的航班列表中构建所有离开或目的地城市的列表。这给了四个 城市:

Dallas
Paris
Chicago
New York

再次迭代航班列表并计算每个目的地城市的出现次数:

0 Dallas
3 Paris
1 Chicago
2 New York

按目的地计数对列表进行排序,您有路线:

Dallas -> Chicago -> New York -> Paris

注意:如果目的地计数从零开始不连续(例如,0,1,2,3 ...),则表示该航班的出发/目的地列表不一致或不完整。

答案 3 :(得分:0)

您是否考虑过使用带有列表商店的dictionary

字典基本上是一个哈希表,你可以存储一个键(前端和终点,AD)和一个值(它需要经过[AB,BC,CD]的段)

答案 4 :(得分:0)

好的,拿两个:这是一个函数,它将采用一个字符串,例如你提供的字符串,并根据维基百科文章进行拓扑排序。

import re
import itertools

def create_nodes(segments):
    remaining_segments = re.findall(r'(\w*?)-(\w*?)[,)]', segments)
    nodes = []
    while len(remaining_segments) > 1:
        outgoing, incoming = zip(*remaining_segments)
        point = next(node for node in outgoing if node not in incoming)
        nodes.append(point)
        remaining_segments = [segment for segment in remaining_segments if segment[0] != point]
    last_segment = remaining_segments.pop()
    nodes.extend(last_segment)
    return nodes

测试:

>>> foo = '(DAL-CHI, CHI-NYC, NYC-PAR, DAL-NYC, DAL-PAR, CHI-PAR)'
>>> scratch.create_nodes(foo)
['DAL', 'CHI', 'NYC', 'PAR']

请注意,对于每次使用,这都不是完美的拓扑排序函数;但是,对于您的多站单程旅行的具体使用情况,它应该是有效的。