Question

The following code generates all possible combinations using 0 and 1, where four digits have to be used.

import itertools
for i in itertools.product([0, 1], repeat=4):
    print i

Output:

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

I would like to be able to set an offset for the for loop. Example:

import itertools
offSet = 10
for i in itertools.product([0, 1], repeat=4):
    # Some code that applies the offset
    print i

Which would then output:

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

How can I apply a such offset to this for loop?

Note: The code being used is simplified. As I am actually using a very large value for repeat, performance matters. I cannot afford the possible combinations before the offset to be calculated.

Was it helpful?

Solution

How about this:

In [29]: offSet = 10

In [30]: repeat = 4

In [31]: for i in xrange(offSet, 2**repeat):
    print tuple(int(x) for x in bin(i)[2:])
   ....:     
(1, 0, 1, 0)
(1, 0, 1, 1)
(1, 1, 0, 0)
(1, 1, 0, 1)
(1, 1, 1, 0)
(1, 1, 1, 1)

OTHER TIPS

Here's a definition that takes a binary (in Python, represented by a string as in '0b1010101') (or an easy human readable and writable version that leaves out the 0b part) and returns the range in the form of an iterator of tuples of strings (which is created lazily). You can materialize the range with a list().

def binrange(start, stop=None):
    '''
    given bin strings return iterator of zeros and ones
    e.g. 
    >>> list(binrange(bin(8))) # bin(8) returns '0b1000'
    [(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 0, 0), (0, 1, 0, 1), (0, 1, 1, 0), (0, 1, 1, 1)]
    >>> list(binrange('10', bin(4))) # bin(4) returns '0b100'
    [(0, 1, 0), (0, 1, 1)]
    '''
    if stop is None:
        start, stop = '0', start
    start = start[2:] if start.startswith('0b') else start
    stop = stop[2:] if stop.startswith('0b') else stop
    length = len(stop)
    for i in xrange(long(start, 2), long(stop, 2)): # in Python3, use range(), not xrange()
        yield tuple(int(j) for j in ('{0:b}'.format(i).zfill(length)))
        # above line replaces the following commented line
        # yield tuple(int(j) for j in ('{0:{fill}{align}{width}b}'.format(i, fill=0, align='>', width=length)))

and

print(list(binrange(bin(1000**3), bin(1000**3+3))))
print(list(binrange('1000')))
print(list(binrange('0b100')))

prints out:

[(1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0), (1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1), (1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0)]
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 0, 0), (0, 1, 0, 1), (0, 1, 1, 0), (0, 1, 1, 1)]
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1)]

You could use islice:

from itertools import product, islice

i = islice(product([0, 1], repeat=4), 10, None) 
print list(i)

This still fails with respect to:

I cannot afford the possible combinations before the offset to be calculated.

This isn't really what iterators are for. At any rate, you're actually just trying to count in binary, and the other answers here will work

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top