【问题标题】:Efficiently combine two uneven lists into dictionary based on condition根据条件有效地将两个不均匀列表组合成字典
【发布时间】:2020-09-10 15:47:02
【问题描述】:

我有两个元组列表,我想根据条件将e 中的元素映射到s 中的元素。条件是e 中某物的第一个元素需要>= 到s 中的第一个元素,e 中的元素1+2 需要s 中的元素1+2。每个s 元组中的第一个数字是起始位置,第二个是长度。我可以这样做:

e = [('e1',3,3),('e2',6,2),('e3',330,3)]
s = [('s1',0,10),('s2',11,24),('s3',35,35),('s4',320,29)]

for i in e:
    d = {i:j for j in s if i[1]>=j[1] and i[1]+i[2]<=j[1]+j[2]}
    print(d)

输出(我想要的):

{('e1', 3, 3): ('s1', 0, 10)}
{('e2', 6, 2): ('s1', 0, 10)}
{('e3', 330, 3): ('s4', 320, 29)}

有没有更有效的方法来获得这个结果,理想情况下没有 for 循环(至少不是双循环)?我已经用zip 尝试了一些东西,以及一些类似的东西

list(map(lambda i,j: {i:j} if i[1]&gt;=j[1] and i[1]+i[2]&lt;=j[1]+j[2] else None, e, s))

但它并没有给我我想要的东西。

s 中的元素永远不会重叠。例如,您不会有 ('s1',0,10) 和 ('s2', 5,15)。换句话说,范围 (0, 0+10) 永远不会与 (5,5+15) 或任何其他元组的范围重叠。此外,e 中的所有元组都是唯一的。

【问题讨论】:

  • 在最坏的情况下,你不能比双重嵌套循环做得更好(假设e 中的每个元素都是('ex', 1, 0)s 中的每个元素都是('sx', 0, 2))。您是否有任何额外的限制,以便我们平均可以做得更好?例如,与所有可能结果的空间相比,结果集是否稀疏?
  • @FMc 谢天谢地,这实际上并非如此。他们指定的约束在某种意义上是关联的,可以让你避免很多额外的比较。在最坏的情况下,您可能仍然有 2 个循环的输出,我认为对于大多数输入,我们可以做得更好。
  • @HansMusgrave 他们唯一的约束是列出的。这是否意味着我所拥有的和它所能得到的一样好?
  • @formicaman 有点像。现在您的解决方案需要len(e) * len(s) 时间,即使len(results) == 0 也是如此。我正在研究一个运行时间略高于len(results) + len(e) + len(s) 的答案,这对于大多数输入来说会更快。如果你没有额外的限制,那么仍然会有一些输入会导致这个想法变慢,但它们不应该经常出现。
  • @HansMusgrave 谢谢。是的,唯一的事情是从 i[1] 到 i[2] 的范围需要在 s[1] 到 s[2] 之内——如果要创建该映射。

标签: python list dictionary


【解决方案1】:

s 中的元组不能重叠的约束非常强。特别是,这意味着e 中的每个值最多只能与s 中的一个值匹配(我认为最简单的证明方法是假设s 中的两个不同的、不重叠的元组与e 中的单个元素并导出矛盾)。

由于该属性,任何匹配 e-tuple e1 的 s-tuple s1 都具有以下属性:在ssx[1]&lt;=e1[1] 中的 所有 元组 sx 中,它是具有最大sum(sx[1:]) 的那个,因为如果不是,那么具有足够小的第一个坐标和更大的总和的 s 元组也将是匹配的(我们已经知道这是不可能的)。

该观察结果适用于一个相当简单的算法,我们线性遍历es(已排序),跟踪总和最大的 s 元组。如果与我们正在查看的 e-tuple 相比,该总和足够大,则将该对添加到我们的结果集中。

def pairs(e, s):
  # Might be able to skip or replace with .sort() depending
  # on the rest of your program
  e = sorted(e, key=lambda t: t[1])
  s = sorted(s, key=lambda t: t[1])

  i, max_seen, max_sum, results = 0, None, None, {}
  for ex in e:
    while i < len(s) and (sx:=s[i])[1] <= ex[1]:
      if not max_seen or sum(sx[1:]) > max_sum:
        max_seen, max_sum = sx, sum(sx[1:])
      i += 1
    
    if max_seen and max_sum > sum(ex[1:]):
      results[ex] = s[i]

  return results

【讨论】:

  • 感谢详细的解释!永远不会有重复的键——换句话说,e 中的两个元素永远不会相同。
  • 不客气 :) 也许我不清楚钥匙; e 中的一个元素可以匹配 s 中的大量元素,但 Python 不允许 {'e1': 's1', 'e1': 's2'}(或者,如果你尝试,它会覆盖结果)。
  • 是的,但是键应该是元组('e1',0,0)。元组中的另外两个元素应该使其唯一。
  • 对不起,我试图避免评论太长,并且使示例过于简化:考虑e = [('e1', 3, 0)]s = [('s1', 2, 8), ('s2', 1, 9)]。根据您概述的规则,您的输出对由(e[0], s[0]) and (e[0], s[1]) 组成,但是当您尝试将其推入字典时,它就会分崩离析;如果你写{e[0]: s[0], e[0]: s[1]},你只会得到{e[0]: s[1]}。如果您想要这种破坏行为,那么我认为我上面给出的算法可以稍微改进一下(或者您可以将 results 包装在 dict 中),但它可以发生,除非您的数据很漂亮特别的。
  • 抱歉,我的问题并不清楚——s 中的元素永远不会重叠。
猜你喜欢
  • 2016-10-18
  • 1970-01-01
  • 2013-06-25
  • 2018-11-26
  • 1970-01-01
  • 2016-12-16
  • 2018-10-05
  • 2021-12-05
相关资源
最近更新 更多