以多个步骤填写列表

时间:2014-12-17 20:42:52

标签: python list iteration

让我们假设你有一个y poisitions列表(为了这个问题,为0)。如果y = 10:

[0,0,0,0,0,0,0,0,0,0]

您希望将相邻位置填充到给定值x并将其附加到空列表中。如果x = 4:

[[1,1,1,1,0,0,0,0,0,0], [0,1,1,1,1,0,0,0,0,0], [0,0,1,1,1,1,0,0,0,0], ... , [0,0,0,0,0,0,1,1,1,1]]

我通过这个功能发生了这个:

def permutations(number=4, limit=10):

    perms = [] 

    if type(number) == int:
        a = -1
        b = a + number
        while b < limit:
            a+=1
            b = a + number
            start = [0 for x in range(limit)]
            for i in range(a, b):
                start[i] = 1
            perms.append(start)

这很好,但是如果我想做同样的事情,但传递一个元组而不是一个整数我想输出为:

如果number =(4,3):

[[1,1,1,1,0,1,1,1,0,0], [1,1,1,1,0,0,1,1,1,0], [1,1,1,1,0,0,0,1,1,1],
 [0,1,1,1,1,0,1,1,1,0], [0,1,1,1,1,0,0,1,1,1],
 [0,0,1,1,1,1,0,1,1,1]]

两个1的分组之间的0是必要的,元组的第一个值对应于第一个分组中的1的数量,而元组的第二个值对应于第二个分组中的1的数量。理想情况下,此函数适用于具有2个以上值的元组。

这个想法有点难以接触,所以如果您需要任何澄清,请告诉我。

感谢您的帮助!

5 个答案:

答案 0 :(得分:3)

我能想到的最简单的方法是生成1和0的所有可能组合,并过滤掉所有不具有正确分组长度的组合。

import itertools

def permutations(tup, limit=10):
    for candidate in itertools.product([0,1], repeat=limit):
        segment_lengths = [len(list(b)) for a,b in itertools.groupby(candidate) if a == 1]
        if tup == tuple(segment_lengths):
            yield candidate

for seq in permutations((4, 3), 10):
    print seq

结果:

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

请注意,对于limit的大值,这非常慢 - 它必须评估2 ^个极限候选序列。对于limit = 10来说还不错;只需要评估1024名候选人。但它会迅速增长到数百万甚至更大的范围内。


编辑:受用户2097159优秀评论的启发,这是一种更好的运行时间方法。

import itertools

"""Finds all non-negative integer sequences whose sum equals `total`, and who have `size` elements."""
def possible_sums(total, size):
    if total == 0:
        yield [0]*size
        return
    if size == 1:
        yield [total]
        return
    for i in range(total+1):
        left = [i]
        for right in possible_sums(total-i, size-1):
            yield left + right

"""
combines two lists a and b in order like:
[a[0], b[0], a[1], b[1]...]

"""
def interleave(a,b):
    result = []
    for pair in itertools.izip_longest(a,b):
        for item in pair:
            if item is not None:
                result.append(item)
    return result

"""flattens a list of lists into a one dimensional list"""
def flatten(seq):
    return [x for item in seq for x in item]

def permutations(tup, limit):
    one_segments = [[1]*size for size in tup]
    for i in range(len(tup)-1):
        one_segments[i].append(0)
    remaining_zeroes = limit - sum(tup) - len(tup) + 1
    assert remaining_zeroes >= 0, "not enough room to separate ranges!"
    for gap_sizes in possible_sums(remaining_zeroes, len(tup)+1):
        zero_segments = [[0]*size for size in gap_sizes]
        yield flatten(interleave(zero_segments, one_segments))

for seq in permutations((4, 3), 10):
    print seq

答案 1 :(得分:0)

您可以递归生成所有列表。

F(tup, limit) = 
[1, 1, ...1, 0] combine with all solutions of F(tup[1:], limit - len(tup[1]) - 1)
[0, 1 ,1 , ... 1, 0] combine with all solutions of F(tup[1:], limit - len(tup[1]) - 2)
.
.
.
  1. 如果tup为空,则返回零
  2. 列表
  3. 如果sum(tup)+ len(tup) - 1&gt;限制,返回一个空列表,因为没有解决方案。 例如permutations((4,3,2), 10)将返回[]
  4. 否则,枚举将有多少前缀为零:
    1. 生成前缀列表[0, 0, 0 .. 0, 1, 1, ... 1, 0] 1 s的数量是元组中第一项的值。如果它不是元组的最后一项,则追加0。
    2. 以递归方式为元组中的rest元素调用函数来解决类似的子问题
    3. 将前缀列表与子问题的每个解决方案相结合
  5. 以下是代码:

    def permutations(tup, limit=100):
        if len(tup) <= 0:
            return [[0] * limit]
        minimum_len = sum(tup) + len(tup) - 1
        if minimum_len > limit:
            return []
    
        perms = []
        for prefix_zero in range(0, limit - minimum_len + 1):
            prefix = [0] * prefix_zero + [1] * tup[0]
            if len(tup) > 1:
                prefix += [0]
            suffix_list = permutations(tup[1:], limit - len(prefix))
            perms += [prefix + suffix for suffix in suffix_list] #combine the solutions
        return perms
    

答案 2 :(得分:0)

此解决方案创建所有块的排列(由元组中的每个条目定义的列表),其中包含用于额外填充的零块(长度为1的列表)。

import itertools as it

spec = (1,2,3)

nBlocks = len(spec)
nZeros = 5
totalSize = sum(spec) + nZeros+1-nBlocks

blocks = [[1,]*s + [0,] for s in spec]
zeros = [[0,],]*(nZeros+1-nBlocks)

a = list(it.permutations(blocks + zeros, nZeros+1))
b = [list(it.chain.from_iterable(l))[:-1] for l in a]

for l in b:
    print l

答案 3 :(得分:0)

没有itertools的简单非递归生成器解决方案:

def fill_sequence(sequence, size):
    n_slots = size - len(sequence)
    for start in xrange(n_slots + 1):
        yield [0]*start + sequence + [0]*(n_slots - start)

def all_placements(inner_sizes, outer_size):
    x, y = inner_sizes
    for margin in xrange(1, outer_size - sum(block_sizes) + 1):
        sequence = [1]*x + [0]*margin + [1]*y
        for p in fill_sequence(sequence, outer_size):
            yield p

那样:

>>> list(all_placements((4,3), 10))
[[1, 1, 1, 1, 0, 1, 1, 1, 0, 0],
 [0, 1, 1, 1, 1, 0, 1, 1, 1, 0],
 [0, 0, 1, 1, 1, 1, 0, 1, 1, 1],
 [1, 1, 1, 1, 0, 0, 1, 1, 1, 0],
 [0, 1, 1, 1, 1, 0, 0, 1, 1, 1],
 [1, 1, 1, 1, 0, 0, 0, 1, 1, 1]]

这个想法非常简单。假设您修复了两个块之间的零个数,请将其命名为margin。这会为您提供4 + margin + 3序列。您可以使用您在帖子中采用的方法轻松地将此序列放在较大的零列表中。然后简单地迭代地增加边距,产生所有可能的位置。

答案 4 :(得分:0)

不使用itertools。

我对此的镜头应该相当快,但使用递归生成器(python递归深度限制,我来......)。

# simple test case
seqs = (1, 2, 3)
length = 10

# '0' spots count
zeros = length - (sum(seqs))
# partitions count
partitions = len(seqs) + 1


# first and last can partitions have 0 zeros
# so use a flag when we call the function or check if it's the last partition
def generate_gaps(zeros_left, partition, first=False):
    """ 
    :param zeros_left: how many zeros we can still use
    :param partition: what partition is this
    :param first: is this the first gap
    :return: all possible gaps
    """
    for gap in range((0 if first or partition == 0 else 1), zeros_left + 1):
        if partition == 0:
            if (zeros_left - gap) == 0:
                yield [gap]
        else:
            for rest in generate_gaps(zeros_left - gap, partition - 1):
                yield [gap] + rest

for gaps in generate_gaps(zeros, partitions - 1, True):
    print "using gaps: " + str(gaps)
    # merge lists
    # zip gaps (0's) and sequences (1's) - all but last gap (added to result)
    gaps_seqs = zip(gaps, seqs)
    # expand everything... magic (could be done explicitly trivially).
    result = sum(map(lambda x: [0] * x[0] + [1] * x[1], gaps_seqs)
    # last gap (truncated from zip)
    result = result + [[0] * gaps[-1]], [])
相关问题