【问题标题】:Python: How to generate all combinations of lists of tuples without repeating contents of the tuplePython:如何在不重复元组内容的情况下生成元组列表的所有组合
【发布时间】:2018-03-15 07:56:04
【问题描述】:

我正在研究一个谜题:

给定一个包含键元组的字典:dictionary = {(p,q):n},我需要生成每个组合的新字典列表,这样 p 和 q 都不会在新字典中重复。在生成此字典列表期间或之后,根据使用字典值的计算选择其中一个字典作为所需的字典。

我的意思的例子(但小得多):

dictionary = {(1,1): 1.0, (1,2): 2.0, (1,3): 2.5, (1,4): 5.0, (2,1): 3.5, (2,2): 6.0, (2,3): 4.0, (2,4): 1.0}

变成

listofdictionaries = [{(1,1): 1.0, (2,2): 6.0}, {(1,1): 1.0, (2,3): 4.0}, (1,1): 1.0, (2,4): 1.0}, {(1,2): 2.0, (2,1): 3.5}, {(1,2): 2.0, (2,3): 4.0},

不允许使用像 {(1,1): 1.0, (2,1): 3.5} 这样的字典,因为 q 重复。

现在我的故事是:我是编码新手……但我一直在尝试编写这个脚本来分析我的一些数据。但我也认为这是一个有趣的算法谜题。我写了一些适用于非常小的字典的东西,但是当我输入一个大字典时,运行时间太长(复制如下)。在我的脚本尝试中,我实际上生成了一个元组组合列表,而不是稍后在脚本中用于引用我的主字典。下面我复制一下:

字典元组键是使用两个列表生成的:“ExpList1”和“ExpList2”

#first, I generate all the tuple combinations from my ExpDict dictionary
combos =(itertools.combinations(ExpDict,min(len(ExpList1),len(ExpList2))))

#then I generate a list of only the combinations that don't repeat p or q
uniquecombolist = []
for foo in combos:
    counter = 0
    listofp = []
    listofq = []
    for bar in foo:
        if bar[0] in listofp or bar[1] in listofq:
            counter=+1
            break
        else:
            listofp.append(bar[0])
            listofq.append(bar[1])
    if counter == 0:
        uniquecombolist.append(foo)

生成此列表后,我将一个函数应用于所有字典组合(遍历元组列表并从主字典中调用它们各自的值)并从该函数中选择具有最小结果值的组合。

我还尝试应用该函数,同时通过选择唯一 p、q 的组合进行迭代,然后检查结果值是否小于前一个值,如果是则保留它(这不是生成该列表“uniquecombolist ",我最终只生成了最终的元组列表) - 仍然需要很长时间。

我认为解决方案在于在生成组合时嵌入 p,q-no-repeat 和最终选择函数。我只是在思考如何真正做到这一点时遇到了麻烦。

感谢阅读! 萨拉

编辑:

为了澄清,我为我的代码编写了一个替代方案,它将最终函数(基本上是均方根)合并到对集合中。

`combos =(itertools.combinations(ExpDict,min(len(ExpList1),len(ExpList2))))


prevRMSD = float('inf')
for foo in combos:
    counter = 0
    distanceSUM = 0
    listofp = []
    listofq = []
    for bar in foo:
        if bar[0] in listofp or bar[1] in listofq:
            counter=+1
            break
        else:
            listofp.append(bar[0])
           listofq.append(bar[1])
        distanceSUM = distanceSUM + RMSDdict[bar]
    RMSD = math.sqrt (distanceSUM**2/len(foo))
    if counter == 0 and RMSD< prevRMSD:
        chosencombo = foo
        prevRMSD = RMSD`

因此,如果我可以在集合生成期间合并 RMS 计算并只保留最小的那个,我认为这将解决我的组合问题。

【问题讨论】:

  • 您想生成满足您条件的所有可能的配对集吗?或者n 是较小生成列表长度的可能大小n 对组?
  • @JaredGoguen 每对都是集合中的一个条目。该集合中有 n 个对,因为 p 和 q 不能重复,所以它必须限制在较小的生成列表的大小。我想生成每个可能的集合,给定两个元组对列表(或两个带有元组键的字典)。
  • 我尝试查看 itertools.combinations 的代码,但老实说,我无法充分理解它在我自己的组合条件下工作,甚至是我需要应用的最终功能。我查看了stackoverflow.com/questions/24907913/…,但不幸的是仍然不明白它是如何工作的。就像我在帖子中所说的那样,我对此很陌生(我已经编写了另一个脚本并且从未参加过任何计算机科学课程)所以也许我咬得比我能咀嚼的更多。

标签: python python-2.7 python-3.x combinations itertools


【解决方案1】:

如果我理解您的问题,您会对 (p,q) 对的所有可能组合感兴趣,它们具有唯一的 p 和 q,尊重 p 和 q 的一组给定的可能值。在我的回答中,我假设这些可能的值分别在 list_plist_q 中(我认为这就是你在 ExpList1ExpList2 中所拥有的,对吗?)

min_size = min(len(list_p), len(list_q))

combos_p = itertools.combinations(list_p, min_size)
combos_q = itertools.permutations(list_q, min_size)
prod = itertools.product(combos_p, combos_q)
uniquecombolist = [tuple(zip(i[0], i[1])) for i in prod]

如果这是您要找的,请告诉我。顺便说一句,欢迎来到 SO,好问题!


编辑:

如果您担心您的列表可能会变得庞大,您可以随时使用生成器表达式并对其应用您想要的任何函数,例如,

min_size = min(len(list_p), len(list_q))

combos_p = itertools.combinations(list_p, min_size)
combos_q = itertools.permutations(list_q, min_size)
prod = itertools.product(combos_p, combos_q)
uniquecombo = (tuple(zip(y[0], y[1])) for y in prod) # this is now a generator expression, not a list -- observe the parentheses

def your_function(x):
    # do whatever you want with the values, here I'm just printing and returning
    print(x)
    return x

# now prints the minimum value
print(min(itertools.imap(your_function, uniquecombo)))

当您使用生成器而不是列表时,会根据需要计算值。这里因为我们对最小值感兴趣,所以每个值都会被计算并立即丢弃,除非它是最小值。

【讨论】:

  • 我想我仍然会遇到列表太大而无法确定哪一组元组对是产生最小最终值的问题(在将每个组输入函数之后)。我将编辑上面的帖子以包含我的功能。为了让这篇文章更笼统,我省略了它,但我认为它会帮助读者理解我想要做的事情。
  • 这确实是你想要的,我会试着解释一下我做了什么
  • 它确实优雅地解决了第一轮集合选择!谢谢 :) 让我知道您对我上面的编辑的看法
  • 通用性很好,这是您在 Stack Overflow 上发帖时应该寻求的。我会检查你的编辑。
  • 因此,您似乎担心在应用函数之前生成一个太大的列表。如果是这种情况,您不需要先生成列表,而是使用生成器表达式。我会告诉你如何
【解决方案2】:

此答案假设您尝试使用 |S| 生成集合元素,其中 S 是较小的元组坐标池。较大的池将表示为 L。

因为集合将包含 |S|没有重复元素的对,S 中的每个元素必须恰好出现一次。从这里,匹配 L 的 排列 其中 |S|元素是使用 S 的有序元素选择的。这将详尽且不重复地生成所有请求的集合

注意P(|L|, |S|) 等于|L|!/(|L|-|S|)!

根据元组坐标池的大小,可能有太多排列无法枚举。

复制此枚举的一些代码可能如下所示:

from itertools import permutations 

S, L = range(2), range(4) # or ExpList1, ExpList2
for p in permutations(L, len(S)):
    print(zip(S, p))

总的来说,您的最终代码可能类似于:

S, L = ExpList1, ExpList2
pairset_maker = lambda p: zip(S, p)

if len(S) > len(L):
    S, L = L, S
    pairset_maker = lambda p: zip(p, S)

n = len(S)   
get_perm_value = lambda p: math.sqrt(sum(RMSDdict[t] for t in pairset_maker(p))**2/n)

min_pairset = min(itertools.permutations(L, n), key=get_perm_value)

如果这不能使您达到所需运行时间的一个数量级或两个数量级,那么您可能需要考虑一种不会产生最佳解决方案的算法。

【讨论】:

  • 是的,我已经意识到这一点,这就是为什么我希望在生成组合时应用我的函数......最后我使用一个函数来查找元组集计算最小值,所以如果我可以将该函数合并到生成唯一集的代码中,以便它检查每个新组合以查看它是否小于前一个组合,然后保持两者中较小的一个,我认为这可以工作吗?你怎么看?
  • 我也在寻找组合而不是排列
  • 您所描述的对的组合与上述答案中描述的 L 的排列之间存在一对一的映射。
  • 代码 sn-p 是否不会产生您期望的成对集合?
  • 此外,itertools 和生成器生成的对象,这意味着它们是惰性的,并且在需要之前不会生成元素(即内存中没有浮动的巨大排列列表)。因此,将您的函数应用于元组并仅保留较大的函数不会做任何我不认为的事情。
猜你喜欢
  • 2022-11-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多