简体   繁体   中英

Iterate over lists without two duplicates in a row

I would like to iterate over lists of integers in a similar way to:

itertools.product(range(n), repeat=5)

If n = 3 this gives:

[(0, 0, 0, 0, 0),
 (0, 0, 0, 0, 1),
 (0, 0, 0, 0, 2),
 (0, 0, 0, 1, 0),
 (0, 0, 0, 1, 1),
 (0, 0, 0, 1, 2),
 (0, 0, 0, 2, 0),
 (0, 0, 0, 2, 1),
 (0, 0, 0, 2, 2),
 (0, 0, 1, 0, 0),
[...]

However I want only those tuples that don't have the same number twice in a row. So (0,0,1,0,0) would be excluded as would many others.

How can you do this?

It's probably more efficient to generate the sequences without consecutive duplicates yourself, rather than generating all of the sequences with itertools.product and filtering them. I'd use a recursive generator like this:

def gen(seq, n, prefix=()):
    if n == 0:
        yield prefix
        return

    for x in seq:
        if not prefix or x != prefix[-1]:
             yield from gen(seq, n-1, prefix+(x,))

example output:

>>> list(gen(range(3), 5))
[(0, 1, 0, 1, 0),
 (0, 1, 0, 1, 2),
 (0, 1, 0, 2, 0),
 (0, 1, 0, 2, 1),
 (0, 1, 2, 0, 1),
 (0, 1, 2, 0, 2),
 (0, 1, 2, 1, 0),
 (0, 1, 2, 1, 2),
 (0, 2, 0, 1, 0),
 (0, 2, 0, 1, 2),
 (0, 2, 0, 2, 0),
 (0, 2, 0, 2, 1),
 (0, 2, 1, 0, 1),
 (0, 2, 1, 0, 2),
 (0, 2, 1, 2, 0),
 (0, 2, 1, 2, 1),
 (1, 0, 1, 0, 1),
 (1, 0, 1, 0, 2),
 (1, 0, 1, 2, 0),
 (1, 0, 1, 2, 1),
 (1, 0, 2, 0, 1),
 (1, 0, 2, 0, 2),
 (1, 0, 2, 1, 0),
 (1, 0, 2, 1, 2),
 (1, 2, 0, 1, 0),
 (1, 2, 0, 1, 2),
 (1, 2, 0, 2, 0),
 (1, 2, 0, 2, 1),
 (1, 2, 1, 0, 1),
 (1, 2, 1, 0, 2),
 (1, 2, 1, 2, 0),
 (1, 2, 1, 2, 1),
 (2, 0, 1, 0, 1),
 (2, 0, 1, 0, 2),
 (2, 0, 1, 2, 0),
 (2, 0, 1, 2, 1),
 (2, 0, 2, 0, 1),
 (2, 0, 2, 0, 2),
 (2, 0, 2, 1, 0),
 (2, 0, 2, 1, 2),
 (2, 1, 0, 1, 0),
 (2, 1, 0, 1, 2),
 (2, 1, 0, 2, 0),
 (2, 1, 0, 2, 1),
 (2, 1, 2, 0, 1),
 (2, 1, 2, 0, 2),
 (2, 1, 2, 1, 0),
 (2, 1, 2, 1, 2)]

Try:

output = [
          lst for lst in itertools.product(range(3), repeat=3) 
          if not any(i==j for i, j in zip(lst[:-1],lst[1:]))
         ]

You can use a comprensive list with a guard:

n = 3
repeat = 5
[l for l in itertools.product(range(n), repeat=repeat) if not any(l[n-1] == l[n] for n in range(1, repeat))]

This will filter out any line with at least one value identical to the previous one.

If you want to use higher-level functions you can check that the number of groups in a groupby is that same as the repeat.

n = 3
r = 5
out_gen = filter(
    lambda x: len(tuple(itertools.groupby(x)))==r, 
    itertools.product(range(n), repeat=r)
)

Here's another take on the recursive generator approach that is based on excluding the value from the calling recursion rather than passing down the whole result:

def comb(A,n,x=None):
    yield from ([v]+r for v in A if v!=x for r in comb(A,n-1,v)) if n else [[]]

Output:

for p in comb(range(3),5):print(p)
[0, 1, 0, 1, 0]
[0, 1, 0, 1, 2]
[0, 1, 0, 2, 0]
[0, 1, 0, 2, 1]
[0, 1, 2, 0, 1]
[0, 1, 2, 0, 2]
[0, 1, 2, 1, 0]
[0, 1, 2, 1, 2]
[0, 2, 0, 1, 0]
[0, 2, 0, 1, 2]
[0, 2, 0, 2, 0]
[0, 2, 0, 2, 1]
[0, 2, 1, 0, 1]
[0, 2, 1, 0, 2]
[0, 2, 1, 2, 0]
[0, 2, 1, 2, 1]
[1, 0, 1, 0, 1]
[1, 0, 1, 0, 2]
[1, 0, 1, 2, 0]
[1, 0, 1, 2, 1]
[1, 0, 2, 0, 1]
[1, 0, 2, 0, 2]
[1, 0, 2, 1, 0]
[1, 0, 2, 1, 2]
[1, 2, 0, 1, 0]
[1, 2, 0, 1, 2]
[1, 2, 0, 2, 0]
[1, 2, 0, 2, 1]
[1, 2, 1, 0, 1]
[1, 2, 1, 0, 2]
[1, 2, 1, 2, 0]
[1, 2, 1, 2, 1]
[2, 0, 1, 0, 1]
[2, 0, 1, 0, 2]
[2, 0, 1, 2, 0]
[2, 0, 1, 2, 1]
[2, 0, 2, 0, 1]
[2, 0, 2, 0, 2]
[2, 0, 2, 1, 0]
[2, 0, 2, 1, 2]
[2, 1, 0, 1, 0]
[2, 1, 0, 1, 2]
[2, 1, 0, 2, 0]
[2, 1, 0, 2, 1]
[2, 1, 2, 0, 1]
[2, 1, 2, 0, 2]
[2, 1, 2, 1, 0]
[2, 1, 2, 1, 2]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM