Python:如何在不重复元组内容的情况下生成元组列表的所有组合

时间:2017-10-04 02:31:40

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

我正在处理一个谜语:

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

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

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计算并且只保留最小的计算,我认为这将解决我的组合问题。

2 个答案:

答案 0 :(得分:1)

这个答案假设您正在尝试使用| 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)

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

答案 1 :(得分:1)

如果我理解你的问题,你会对所有可能的对(p,q)的组合感兴趣,其中p和q与p和q的给定的一组可能值有关。在我的回答中,我假设这些可能的值分别位于list_plist_q中(我认为这是ExpList1ExpList2中的内容,我是对的吗?)< / p>

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)))

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