Search code examples
pythoncombinationssequencegray-code

All combinations of n binary values in an order that just one component changes compared to the prior combination


Below is a vector with all the possible combinations of 2 binary values:

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

Now, the question is how I can generate a sequence of the same combinations of 2 binary values that each of them has only one component changed compared to previous one in the sequence such as:

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

Solution

  • What you describe here is basically a Gray counter [wiki]. A counter which binary always changes exactly one value. We can construct Gray counter with arbitrary length with the following procedure:

    def gray_count(n=2):
        d = 0
        lst = 1 << (n-1)
        lbm = 0
        popeven = True
        while lbm < lst:
            yield d
            if popeven:
                d ^= 1
            else:
                lbm = d & ((~d)+1)
                d ^= lbm << 1
            popeven = not popeven
    

    or with less if-cases:

    def gray_count(n=2):
        d = 0
        lst = 1 << (n-1)
        lbm = 0
        while lbm < lst:
            yield d
            d ^= 1
            yield d
            lbm = d & ((~d)+1)
            d ^= lbm << 1
    

    This produces a generator of integers. If we convert these to binary representations, we get a set of bits that each time change one bit:

    >>> list(map(bin, gray_count(2)))
    ['0b0', '0b1', '0b11', '0b10']
    >>> list(map(bin, gray_count(3)))
    ['0b0', '0b1', '0b11', '0b10', '0b110', '0b111', '0b101', '0b100']
    >>> list(map(bin, gray_count(4)))
    ['0b0', '0b1', '0b11', '0b10', '0b110', '0b111', '0b101', '0b100', '0b1100', '0b1101', '0b1111', '0b1110', '0b1010', '0b1011', '0b1001', '0b1000']
    

    So in case we want to convert it to a binary tuple, we can use:

    def to_bin(d, n=None):
        while (d > 0 and n is None) or (n is not None and n > 0):
            yield d&1
            d >>= 1
            n -= 1
    

    So we can then convert it to binary tuples with:

    >>> list(map(tuple, map(partial(to_bin, n=2), gray_count(2))))
    [(0, 0), (1, 0), (1, 1), (0, 1)]
    >>> list(map(tuple, map(partial(to_bin, n=3), gray_count(3))))
    [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1), (1, 0, 1), (0, 0, 1)]
    >>> list(map(tuple, map(partial(to_bin, n=4), gray_count(4))))
    [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (0, 1, 0, 0), (0, 1, 1, 0), (1, 1, 1, 0), (1, 0, 1, 0), (0, 0, 1, 0), (0, 0, 1, 1), (1, 0, 1, 1), (1, 1, 1, 1), (0, 1, 1, 1), (0, 1, 0, 1), (1, 1, 0, 1), (1, 0, 0, 1), (0, 0, 0, 1)]