Construct all `r`-tuples with two nonzeros

82 views Asked by At

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?

2

There are 2 answers

0
Stef On BEST ANSWER

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).

0
Kelly Bundy On

Straightforward generalization of your way:

from itertools import combinations, product

val = 7
r = 5
d = 2

for I in combinations(range(r), d):
    for V in product([+val, -val], repeat=d):
        t = [0] * r
        for i, v in zip(I, V):
            t[i] = v
        print(t)

Attempt This Online!