Python:生成所有长度为N的唯一排序的列表

时间:2018-10-22 11:43:22

标签: python arrays sorting generator permutation

我想详尽地分析用于对小型数组进行排序的子例程,并需要一种方法来生成特定长度的所有唯一排序的数组。 在Python中,该列表将以非负整数作为元素,并且在可能的情况下最好使用最小整数。例如,N = 3:

Drawable drawable = RoundedBitmapDrawableFactory.create(context.getResources(), bitmap);

     //set imageview XML - call your image view
    imageView.setImageDrawable(drawable);

[[0,0,0], [0,0,1], [0,1,0], [0,1,1], [0,1,2], [0,2,1], [1,0,0], [1,0,1], [1,0,2], [1,1,0], [1,2,0], [2,0,1], [2,1,0]] [1,1,1]不属于上面的列表,因为[2,2,0][0,0,0]在使用较小的整数时分别具有相同的相对顺序。

3 个答案:

答案 0 :(得分:2)

这是以下各项的组合:(a)查找列表[k_1, ..., k_n],以使每个k_i等于k_(i-1)k_(i-1)+1,以及(b)查找这些列表的唯一排列列表。

第一个可以使用递归函数完成:

def combinations(n, k=0):
    if n > 1:
        yield from ([k] + res for i in (0, 1)
                              for res in combinations(n-1, k+i))
    else:
        yield [k]

对于具有n个元素的列表,将有2^(n-1)个这样的组合:

>>> list(combinations(2))
[[0, 0], [0, 1]]
>>> list(combinations(3))
[[0, 0, 0], [0, 0, 1], [0, 1, 1], [0, 1, 2]]
>>> list(combinations(4))
[[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 1], [0, 0, 1, 2], [0, 1, 1, 1], [0, 1, 1, 2], [0, 1, 2, 2], [0, 1, 2, 3]]

使用itertools.permutations组合并过滤出重复项以得到最终结果:

import itertools
def all_combinations(n):
    return (x for combs in combinations(n)
              for x in set(itertools.permutations(combs)))

示例:

>>> list(all_combinations(3))
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 0), (0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
>>> sum(1 for _ in all_combinations(4))
75
>>> sum(1 for _ in all_combinations(5))
541

注意:生成 all n!排列,然后过滤重复项,即使对于n稍大的值,也可能非常浪费。有一些更聪明的方法可以生成仅可用于代替itertools.permutations的唯一排列,例如,参见herehere

答案 1 :(得分:1)

您可以遍历该范围的笛卡尔积,对于每个元素,请使用相对顺序作为键,并将该对(相对顺序,元组)存储在字典中,最后返回已排序的:

def uniquely_ordered_list(n=3):
    def key(o):
        relative = ['equal'] + ['less' if a < b else ('greater' if a > b else 'equal') for a, b in product(o, repeat=2)]
        return tuple(relative)

    found = {}
    for ordering in product(range(n), repeat=n):
        if key(ordering) not in found:
            found[key(ordering)] = ordering

    return sorted(found.values())

输出

(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 2, 1)
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)

更新

如@tobias_k所建议,您可以将以下函数用作键:

def key(o):
    sign = lambda x: x / abs(x) if x else x
    return tuple([0] + [sign(a - b) for a, b in product(o, repeat=2)])

答案 2 :(得分:1)

这是另一种解决方案:

import numpy as np
from itertools import product, combinations

def rord(lst):
    ''' Maps the relative order of a list 'lst' to a unique string of 0, 1, and 2.

    Relative order is computed by converting the list 'sgns' of all 
    the values sgn(lst[i]-lst[j])+1, for i<j, i,j = 0,..., n-1,
    to a string.

    E.g. the lists [0, 0, 1], [0, 0, 2] and [1, 1, 2] have the same rord = '100'
    because lst[0] = lst[1], lst[0] < lst[1], lst[1] < lst[2] for all
    of them, so sgns = [1, 0, 0]
    '''
    sgns = np.sign([tup[0]-tup[1] for tup in combinations(lst, 2)]) + 1
    return ''.join(str(e) for e in sgns)  # return sgns.tostring() is faster


def uniq_rord_lst(n):
    '''Returns n-length sequences of integers 0,... n-1, with unique relative
    order. E.g. for n=2 returns [(0, 0), (0, 1), (1, 0)].
    '''
    seen_ro = set()
    result = []
    for comb in product(range(n), repeat=n):
        ro = rord(comb)
        if ro not in seen_ro:
            seen_ro.add(ro)
            result.append(comb)
    return result

示例:

>>> uniq_rord_lst(2)
[(0, 0), (0, 1), (1, 0)]

>>> uniq_rord_lst(3)
[(0, 0, 0),
 (0, 0, 1),
 (0, 1, 0),
 (0, 1, 1),
 (0, 1, 2),
 (0, 2, 1),
 (1, 0, 0),
 (1, 0, 1),
 (1, 0, 2),
 (1, 1, 0),
 (1, 2, 0),
 (2, 0, 1),
 (2, 1, 0)]

更新:更快的

def uniq_rord_lst(n):
    seen_ro = set()
    result = []
    for comb in product(range(n), repeat=n):
        ro = tuple(sorted(comb).index(x) for x in comb)
        if ro not in seen_ro:
            seen_ro.add(ro)
            result.append(comb)           
    return result