Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to use itertools with conditions for sets and arbitrary length?

I'm trying to create a list of subsets that add to a particular sum (e.g. 12), but with a limit on the occurrences of some of the terms.

The base set to iterate on is {1,2,3,4,5,6,7,8}. I'm trying to find every subset of those elements that sums to 12, regardless of length of the output, but with the condition that {1,2,3} may only be used twice before the set reduces to {4,5,6,7,8}.

Example valid outputs: 165; 1254; 48; 75 Example invalid output: 1317

Is it possible to use itertools.product() expanded in this way? I figured a form of Cartesian product would be necessary, so I tried to use it with the repeat parameter given a range and bind it with the conditionals that set_B below cannot occur more than twice before the range of acceptable inclusions can only be selected from set_C.

from itertools import product
set_A = {1,2,3,4,5,6,7,8}
set_B = {1,2,3}
set_C = {4,5,6,7,8}

finish = (
    subsets for subsets in product(set_A, 1 < repeat < 5) if count(set_B) < 3 and sum(subsets) = 12
         else:
             subsets for subsets in product(set_C, 1 < repeat < 5) if sum(subsets) = 12
     )

print("\n", finish)

I was hoping it would produce a full list of valid outputs as in the question (any subset of set_A that equals 12, but with the limitation that members of set_B occurring fewer than 3 times), but I'm not sure of the syntax for some of the functions, so I just get a syntax error. The syntax error occurs on line 7 (pretty much where I would expect it would). Changing the repeat parameter to a set number still results in "Invalid syntax."

like image 460
wwwm Avatar asked Nov 14 '25 15:11

wwwm


1 Answers

Use powerset from Itertools Recipes and a list comprehension filtered by the "sum of 12" requirement and the "fewer than 3 from set_B" requirement.

For example:

from itertools import chain, combinations

set_A = {1,2,3,4,5,6,7,8}
set_B = {1,2,3}

def powerset(iterable):
    "Subsequences of the iterable from shortest to longest."
    # powerset([1,2,3]) → () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

finish = [x for x in powerset(set_A) if sum(x) == 12 and set_B - set(x)]

print(*finish, sep='\n')

Output:

(4, 8)
(5, 7)
(1, 3, 8)
(1, 4, 7)
(1, 5, 6)
(2, 3, 7)
(2, 4, 6)
(3, 4, 5)
(1, 2, 4, 5)

Note that a non-empty difference set_B - set(x) means <3 of set_B is in set(x) and is evaluated as true.

like image 102
Mark Tolonen Avatar answered Nov 17 '25 04:11

Mark Tolonen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!