所有排列的列表,但没有相反的数字

时间:2012-05-29 16:33:52

标签: python list permutation

我需要创建一个所有排列的列表,但不包括那些符号变化相同的排列。

例如,从序列

[-2, -1, 1, 2]

我会获得所有这些排列:

[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]

目前我使用以下代码:

permutation_items = []
permutations = itertools.permutations(range_items, items)
permutation_item = list(permutations)

例如range_items = [-2, -1, 1, 2]items = 2

然后消除我使用的所有相反重复

for element in permutation_items:
    flag=0
    for j in element:
        if ((j in element) & ((j*-1) in element)):
            flag = 1
            break
    if flag == 0:
        all_solutions.append(element)

我认为这不是最好的方法,因为首先我创建一个包含所有排列的列表然后我删除那些我不想要的,你能建议一个更好的方法吗?另外,因为如果我需要创建一个包含10个或更多数字的排列列表,它就变得非常大......

你认为我对这些尺寸会有一些问题吗?

请注意:有了这些排列,我需要做进一步的操作(我需要找到给出所有可能的数字对的最小排列数),所以我认为我需要将它们存储在一个变量中,因为在我的算法结束我需要将结果存储在一个文件中。

......好的,你的回答非常好,我喜欢你的兴趣...现在,如果我用我的变量'range_items'列出30个元素(正面和负面),代码使用的时间是非常大,我想问你一个多线程的解决方案(所以我可以在一个有很多内核的集群中加载代码)......这样可行吗?

5 个答案:

答案 0 :(得分:10)

您基本上是在询问如何合并permutationproduct。以下比拒绝更有效(也更简单):您只生成一次所有排列,然后旋转符号。它在时间O(N!)和空间O(1)方面是渐近最优的:

def plusAndMinusPermutations(items):
    for p in permutations(items):
        for signs in product([-1,1], repeat=len(items)):
            yield [a*sign for a,sign in zip(p,signs)]

(使用itertools作为OP)

演示:

>>> list( plusAndMinusPermutations([1,2]) )
[
 [-1, -2], 
 [-1, 2], 
 [1, -2],
 [1, 2],
 [-2, -1],
 [-2, 1],
 [2, -1],
 [2, 1]
]

这更有效率factorial(N)! (假设您使用它的长度大于2。)

或者,我们可以按相反的顺序组合它们(如果您愿意,可以将list映射到元组上):

def plusAndMinusPermutations(items):
    for signed in product(*[[-a,a] for a in items]):
        for p in permutations(signed):
            yield p

>>> list( plusAndMinusPermutations([1,2]) )
[
 (-1, -2), 
 (-2, -1), 
 (-1, 2), 
 (2, -1), 
 (1, -2), 
 (-2, 1), 
 (1, 2), 
 (2, 1)
]

编辑以响应OP编辑:

  

我需要找到给出所有可能的数字对的最小排列数 --OP

我不确定这是什么意思,但根据你的措辞,你几乎肯定不需要做任何这些。只需使用现有方法强制解决0到10之间的数字问题,然后将结果输入http://oeis.org/,您可能会找到一个明确的公式。

答案 1 :(得分:6)

以下使用与您的代码相同的拒绝方法,但效率更高:

(s for s in itertools.permutations(l, 2) if len(set(map(abs, s))) == len(s))

其中l是序列。

唯一棘手的问题是len(set(map(abs, s))) == len(s)。它将置换的所有元素的绝对值放入集合中,并确保集合与置换的大小相同。

为了加快速度,您可以将len(s)替换为排列的长度(上例中的2)。

我能想到的唯一算法改进是从原始序列中删除重复的数字。是否能为您带来更多收益取决于您是否希望首先获得重复。

答案 2 :(得分:1)

我想了一下,我想你会喜欢这个:

from collections import defaultdict
from itertools import permutations, product

def make_groups(seq):
    found = defaultdict(set)
    for num in seq:
        found[abs(num)].add(num)
    return [list(v) for v in found.itervalues()]

def selective_permutations(seq, r=None):
    for g in permutations(make_groups(seq), r):
        for p in product(*g):
            yield p

它会输入您的输入序列 - 例如[-2, -1, 0, 1, 2] - 并按互斥值对其进行分组 - 所以[[-2, 2], [-1, 1], [0]]

然后它会在群组上运行permutations - 因此您将获得[[-2, 2], [-1, 1]] - 然后针对结果群组运行product,产生[[-2, -1], [-2, 1], [2, -1], [2, 1]],这是我们在寻找什么。

它尊重r参数(序列长度),并在平衡和非平衡序列上做最佳效率的工作 - 例如:

for p in selective_permutations([-3,-2,1,2], 2):
    print p

结果

(1, 2)
(1, -2)
(1, -3)
(2, 1)
(-2, 1)
(2, -3)
(-2, -3)
(-3, 1)
(-3, 2)
(-3, -2)

无需丢弃任何组合。

希望有所帮助! ; - )

答案 3 :(得分:0)

假设您只想成对数字(如果不是你必须对递归做一些事情),并且序列中没有相同的数字

def make_perm(sequens):
    perm_s = []
    for e1 in sequens:
        for e2 in sequens:
            if abs(e1) != abs(e2):
               perm_s += [[e1,e2]] 

    return perm_s

print make_perm([-2,-1,1,2])

输出:

[[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]]

此解决方案处理不同的项目长度。

def perm(list_to_perm,perm_l,items):
    if len(perm_l) == items:
        print perm_l
    else:
        for i in  list_to_perm:

            if i not in perm_l:
                if -i not in perm_l:
                    perm(list_to_perm,list(perm_l) +[i],items)


a = [-2,-1,1,2]
perm(a,[],2)

输出:

[-2, -1]
[-2, 1]
[-1, -2]
[-1, 2]
[1, -2]
[1, 2]
[2, -1]
[2, 1]

答案 4 :(得分:0)

这看起来有点奇怪。我还在学习python。我正在复制代码,所以顺序更自然。可能有捷径。事实证明这是Rosalind.info问题的答案。我不喜欢这些代码,它有点吵,但它有效。

    def signedPerm(x):
        if len(x) == 1:
            yield x
            x[0]*=-1
            yield x
        else:
            for i in range(len(x)):
            for s in [1, -1]:
            y=[x[i]*s]
            for j in signedPerm(x[:i]+x[i+1:]):
              yield y+j

然后致电


    [x for x in signedPerm(list(set([abs(y) for y in [-1, -2, 1, 4]])))]