Search code examples
pythonpermutationpython-itertools

Construct all `r`-tuples with two nonzeros


Given an int value val and a tuple length r, I need to create all r-tuples that have d of {+val, -val} and the rest filled up with zeros. With d=2, I can do

val = 7
r = 5

out = []
for i0 in range(r - 1):
    for i1 in range(i0 + 1, r):
        for v0 in [+val, -val]:
            for v1 in [+val, -val]:
                t = [0] * r
                t[i0] = v0
                t[i1] = v1
                print(t)
[7, 7, 0, 0, 0]
[7, -7, 0, 0, 0]
[-7, 7, 0, 0, 0]
[-7, -7, 0, 0, 0]
[7, 0, 7, 0, 0]
# ...

but this already feels messy. It's getting worse for larger d. I looked at itertools combinatoric iterators, but none of those seems to help.

Any hints?


Solution

  • Using:

    from itertools import combinations_with_replacement, chain
    from more_itertools import distinct_permutations
    
    def combs_with_zeroes(val, d, r):
        if r < d: return iter(())
        return chain.from_iterable(
            distinct_permutations(valcomb + (0,)*(r-d))
            for valcomb in combinations_with_replacement((-val, +val), d)
        )
    
    print(list(combs_with_zeroes(val=7, d=1, r=2)))
    # [(-7, 0), (0, -7), (0, 7), (7, 0)]
    
    print(list(combs_with_zeroes(val=7, d=1, r=3)))
    # [(-7, 0, 0), (0, -7, 0), (0, 0, -7), (0, 0, 7), (0, 7, 0), (7, 0, 0)]
    
    print(list(combs_with_zeroes(val=7, d=3, r=4)))
    # [(-7, -7, -7, 0), (-7, -7, 0, -7), (-7, 0, -7, -7), (0, -7, -7, -7), (-7, -7, 0, 7), (-7, -7, 7, 0), (-7, 0, -7, 7), (-7, 0, 7, -7), (-7, 7, -7, 0), (-7, 7, 0, -7), (0, -7, -7, 7), (0, -7, 7, -7), (0, 7, -7, -7), (7, -7, -7, 0), (7, -7, 0, -7), (7, 0, -7, -7), (-7, 0, 7, 7), (-7, 7, 0, 7), (-7, 7, 7, 0), (0, -7, 7, 7), (0, 7, -7, 7), (0, 7, 7, -7), (7, -7, 0, 7), (7, -7, 7, 0), (7, 0, -7, 7), (7, 0, 7, -7), (7, 7, -7, 0), (7, 7, 0, -7), (0, 7, 7, 7), (7, 0, 7, 7), (7, 7, 0, 7), (7, 7, 7, 0)]
    
    print(list(combs_with_zeroes(val=7, d=2, r=5)))
    # [(-7, -7, 0, 0, 0), (-7, 0, -7, 0, 0), (-7, 0, 0, -7, 0), (-7, 0, 0, 0, -7), (0, -7, -7, 0, 0), (0, -7, 0, -7, 0), (0, -7, 0, 0, -7), (0, 0, -7, -7, 0), (0, 0, -7, 0, -7), (0, 0, 0, -7, -7), (-7, 0, 0, 0, 7), (-7, 0, 0, 7, 0), (-7, 0, 7, 0, 0), (-7, 7, 0, 0, 0), (0, -7, 0, 0, 7), (0, -7, 0, 7, 0), (0, -7, 7, 0, 0), (0, 0, -7, 0, 7), (0, 0, -7, 7, 0), (0, 0, 0, -7, 7), (0, 0, 0, 7, -7), (0, 0, 7, -7, 0), (0, 0, 7, 0, -7), (0, 7, -7, 0, 0), (0, 7, 0, -7, 0), (0, 7, 0, 0, -7), (7, -7, 0, 0, 0), (7, 0, -7, 0, 0), (7, 0, 0, -7, 0), (7, 0, 0, 0, -7), (0, 0, 0, 7, 7), (0, 0, 7, 0, 7), (0, 0, 7, 7, 0), (0, 7, 0, 0, 7), (0, 7, 0, 7, 0), (0, 7, 7, 0, 0), (7, 0, 0, 0, 7), (7, 0, 0, 7, 0), (7, 0, 7, 0, 0), (7, 7, 0, 0, 0)]
    

    Note the results become quite long quite fast; the total number of elements is 2**d * (r choose d).