new itertools functions in Python 2.6

Raymond Hettinger python at rcn.com
Tue Jul 15 02:44:56 EDT 2008


On Jul 14, 1:26 pm, Mensanator <mensana... at aol.com> wrote:
> ##  Combinations with replacement
> ##  -----------------------------
> ##  aaa aab aac aad aae abb abc abd abe acc acd ace
> ##  add ade aee bbb bbc bbd bbe bcc bcd bce bdd bde
> ##  bee ccc ccd cce cdd cde cee ddd dde dee eee
> ##
> ##  actual words: 35    (m+n-1)!/(n!(m-1)!) words: 35
>
> Although it works, it's somewhat slow as we have to iterate
> over the entire Cartesian Product and the filter list(x)==sorted(x)
> has got to be expensive (it's slower than the nested loop algorithm).
>
> Is there a better way to get Combinations With Replacement
> using itertools?

There may a technique to start with the combinations without
replacement and then grow the result by repeating elements:

   result = set(combinations('abcde', 3))
   # transform 'ab ac ad ae bc bd be cd ce de' into 'aab abb aac
acc ...'
   for c in combinations('abcde', 2):
       # transform 'ab' --> 'aab abb'
       for i in range(2):
           result.add(c[:i] + c[i]*1 + c[i:])
   for c in combinations('abcde', 1):
       for i in range(1):
           # 'a' --> 'aaa'
           result.add(c[:i] + c[i]*2 + c[i:])

If you generalize the above, it may solve the problem using itertools
as a starting point.

Alternatively, it's not too hard to transform the pure python version
given in the docs to one that supports combinations with replacement:

def combinations_with_replacement(iterable, r):
    pool = tuple(iterable)
    n = len(pool)
    indices = [0] * r
    yield tuple(pool[i] for i in indices)
    while 1:
        for i in reversed(range(r)):
            if indices[i] != n - 1:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1]
        yield tuple(pool[i] for i in indices)


Raymond



More information about the Python-list mailing list