Search code examples
pythoncombinationspython-itertools

Combinations of no repeated elements in Python


Itertool.combination code gives me all combination for a target value I have an array

a=[1,1,2,-2,-4] target =0

output I get from:

Itertools.combination is: [(2, -2), (1, 1, -2), (1, 1, 2, -4)].

But the concern is:- Once the number is used, it should not get repeated. Required Output:- [(2,-2)] #As 2,-2 pair nets to 0. Code should not use 2 again to sum:1,1,2 and net that off with -4. [I dont want numbers to get repeated once it is used in one of the pair.]

# Reference code:-

    import itertools
    import numpy as np

    def subset_sum(target, numbers):

        array_num=np.array(numbers)
        for size in xrange(1, len(array_num) + 1):
             for c in itertools.combinations(array_num, size):
                 if sum(c) == target:
                   temp_var.append(c)
                   print "Length of array is ",len(array_num)

        return temp_var

    numbers=[1,1,2,-2,-4]
    target=0
    output=subset_sum(numbers)
    print output

Solution

  • Use this:

    concat = lambda i,j: list(i if type(i) is list else [i])+list(j if type(j) is list else [j])
    reduce(lambda x,y: x if any([u in j for j in (x if type(x) is list else [x]) for u in y]) else concat(x,y), reduce(lambda x,y: concat(x,y), [[[],filter( lambda x:sum(x)==target,list(itertools.combinations(a,u)))] for u in range(2,len(a)+1)]))
    

    Outputs:

    >>> concat = lambda i,j: list(i if type(i) is list else [i])+list(j if type(j) i
    s list else [j])
    >>> a=[1,1,2,-2,-4]
    >>> target=0
    >>> reduce(lambda x,y: x if any([u in j for j in (x if type(x) is list else [x])
    for u in y]) else concat(x,y), reduce(lambda x,y: concat(x,y), [filter( lambda
    x:sum(x)==target,list(itertools.combinations(a,u))) for u in range(2,len(a))]))
    (2, -2)
    >>> a=[5,1,3,-4,-5]
    >>> reduce(lambda x,y: x if any([u in j for j in (x if type(x) is list else [x])
    for u in y]) else concat(x,y), reduce(lambda x,y: concat(x,y), [filter( lambda
    x:sum(x)==target,list(itertools.combinations(a,u))) for u in range(2,len(a))]))
    [(5, -5), (1, 3, -4)]
    >>> a=[1,-1,2,-2,-4]
    >>> reduce(lambda x,y: x if any([u in j for j in (x if type(x) is list else [x])
     for u in y]) else concat(x,y), reduce(lambda x,y: concat(x,y), [filter( lambda
    x:sum(x)==target,list(itertools.combinations(a,u))) for u in range(2,len(a))]))
    [(1, -1), (2, -2)]
    >>>
    

    EDIT: This is because the op changed his intention by virtue of his later comment

    f=lambda x:tuple(map(lambda x:a[x],x)) if type(x)==tuple else map(lambda x:f(x),x)
    f(reduce(lambda x,y: x if any([u in j for j in (x if type(x) is list else [x]) for u in y]) else concat(x,y), reduce(lambda x,y: concat(x,y), [filter(lambda x:sum(f(x))==target,list(itertools.combinations(range(0,len(a)),u))) for u in range(0,len(a)+1)])))
    

    This doesn't exclude already-used values of different indices:

    >>> a=[21197595.75,11885337.56,-11885337.56,-11885337.56,-9312258.19]
    >>> f(reduce(lambda x,y: x if any([u in j for j in (x if type(x) is list else [x
    ]) for u in y]) else concat(x,y), reduce(lambda x,y: concat(x,y), concat([filter
    (lambda x:sum(f(x))==target,list(itertools.combinations(range(0,len(a)),u))) for
     u in range(2,len(a)+1)],[]))))
    [(11885337.56, -11885337.56), (21197595.75, -11885337.56, -9312258.19)]
    >>>