【问题标题】:Efficient algorithm for pairwise computation成对计算的高效算法
【发布时间】:2012-06-06 23:23:51
【问题描述】:

我有一个代理列表a = {a1, a2, a3,..., an},其中每个代理可以与零个或多个其他代理配对。例如,对于n = 6,我可以:

a1: a2, a3, a5
a2: a1, a3, a4
a3: a1, a2. a5
a4: a2
a5: a1, a3
a6:

每一对相互交互,每个代理都获得一个值作为交互的结果(例如,他们可以玩游戏,但交互的细节可以在这里抽象出来)。 我有兴趣根据给定的成对结构计算和存储这些交互的结果,例如上面。

显然,一个简单的算法是遍历每个代理,然后逐个计算与每个交互伙伴的成对交互。 但是,很明显这种方法会重复一些(或可能很多)计算。使用上面的示例:

当我们完成代理 a1 时,我们已经获得了 (a1, a2)(a1, a3)(a1, a5) 的结果,因此当我们为代理a2a3a5

一种改进的算法是在两个维度(即沿着代理本身和沿着它们各自的伙伴)按升序对输入结构进行排序,如上例所示,因此对于每个代理(例如a3),我们只需要计算这个代理(a3)和比他“更高”的代理(a5)之间的交互,因为我们知道他和“低”伙伴之间的交互((a1, a3),@987654335 @) 已经计算过了。

我想知道这个问题是否有不同的更好的算法?更好是指在时间和空间方面更高效的计算。

【问题讨论】:

  • 一旦你对某样东西进行排序,你的算法就会变成 O(n log(n)),所以输给任何 O(n) 实现

标签: python algorithm data-structures


【解决方案1】:

是的,这会尝试将每一对添加到集合中两次,但我觉得这可能比条件更有效。有没有人想尝试为替代方案计时?

agents = {
    'a1': ['a2', 'a3', 'a5'],
    'a2': ['a1', 'a3', 'a4'],
    'a3': ['a1', 'a2', 'a5'],
    'a4': ['a2'],
    'a5': ['a1', 'a3'],
    'a6': []
}
pairs = {(k,v) for k in agents for v in agents[k]}

这仍然是 O(n),所以在效率方面胜过任何涉及排序的事情

你可能会通过使用获得轻微的加速

pairs = {(k,v) for k,vs in agents.iteritems() for v in vs}

【讨论】:

  • 我的测试显示你的这个版本需要 0.4 秒,而我的版本需要 1.5 秒来进行 100,000 次迭代。请告诉我为什么这个版本要快得多?
  • @Trevor,检查/添加集合理解中的项目的底层 C 代码非常快。
  • 啊哈,有趣。感谢您的洞察力。
  • @gnibbler,感谢您的建议,但以上仍然会产生 重复 交互对,例如(a1, a2)(a2, a1)
  • 你可以这样做:pairs = {frozenset(k,v) for k,vs in agents.iteritems() for v in vs}
【解决方案2】:

您可以将代理 ID 与以下内容进行比较:

agents = {
    'a1': ['a2', 'a3', 'a5'],
    'a2': ['a1', 'a3', 'a4'],
    'a3': ['a1', 'a2', 'a5'],
    'a4': ['a2'],
    'a5': ['a1', 'a3'],
    'a6': []
}

interactions = []
for agent, connections in agents.iteritems():
    interactions.extend((agent, connection) for connection in connections if agent < connection)

print interactions
# [('a1', 'a2'), ('a1', 'a3'), ('a1', 'a5'), ('a3', 'a5'), ('a2', 'a3'), ('a2', 'a4')]

【讨论】:

  • 您不需要内部 for 循环:interactions.extend((agent, connection) for connection in connections if agent &lt; connection)。我更新了你的答案。
  • 如果我有不同的代理组:a = {a1, a2, a3,..., an}b = {b1, b2, b3,..., bm},它们可以相互操作?
  • 所以这基本上是我上面概述的算法的改进版本?
【解决方案3】:

itertools 救援

>>> from itertools import combinations
>>> agents=['a1','a2','a3','a4','a5']
>>> list(combinations(agents, 2))
[('a1', 'a2'), ('a1', 'a3'), ('a1', 'a4'), ('a1', 'a5'), 
 ('a2', 'a3'), ('a2', 'a4'), ('a2', 'a5'),
 ('a3', 'a4'), ('a3', 'a5'),
 ('a4', 'a5')]
>>> 

【讨论】:

  • 他不想生成所有可能的对。他需要将他的结构转换为不重复的对列表。
  • 这就是上面所做的 - 将结构 agents 转换为不重复的对列表。
  • @NickCraigWood,据我了解,初始结构是这样的:a = { "a1": ["a2", "a3", "a5"], "a2": ["a1", "a3", "a4"], "a3": ["a1", "a2", "a5"], "a4": ["a2"], "a5": ["a1", "a3"], "a6": [] }
  • @NickCraig-Wood,成对交互结构输入可以是任意。我想你提供的例子是一个特例。
  • 我认为您对问题的意图是正确的,但我也认为combinations 将提供所有可能的成对交互的最有效计算,这似乎是 OP 想要的。我会留下这个答案,以防万一其他人同意,否则我会删除它!
【解决方案4】:

据我了解您的问题,您为什么不考虑二维矩阵?在第一阶段,如果两个代理可以相互合作,只需将交叉单元设置为 1。 在第二阶段,只需围绕矩阵设置一个循环并仅在那些具有互连的代理之间计算一些值(即单元格等于 1)。而不是 1 会有一个真正的价值。所以在那种情况下,你不需要做多余的计算。唯一多余的部分是在矩阵中填充计算值两次。

【讨论】:

  • 所以你需要首先填充这个 n x n 矩阵,然后遍历每个单元格以找到值为 1 的单元格,然后为它们执行计算。听起来对我来说工作量很大。此外,每个交互计算的结果应该是两个值,每个交互代理一个。
  • 工作量很大?您不需要填充 NxN 矩阵,只需创建一个零矩阵并将其 i,jj,i 元素设置为 1 或在形成一组代理对时保持不变。然后,是的,您需要遍历矩阵的一半(注意,在您的情况下,矩阵是对称的)。您可以在单元格中存储任何类型的元素。
  • 顺便说一句,我不坚持,矩阵方法是最好的,只是给你另一个观点:)
【解决方案5】:

根据@gnibbler 的回答,我想出了这个:

agents = {
    'a1': ['a2', 'a3', 'a5'],
    'a2': ['a1', 'a3', 'a4'],
    'a3': ['a1', 'a2', 'a5'],
    'a4': ['a2'],
    'a5': ['a1', 'a3'],
    'a6': []
}

pairs = {tuple(sorted((k,v))) for k in agents for v in agents[k]}

仍然需要排序,但仅限于一对。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 2021-01-26
    相关资源
    最近更新 更多