简体   繁体   中英

combining permutations of nested sets

I have 2 sets of sets, I need to get all permutations of their combination (as in union) into a single set such that no 2 sets within it contains duplicate elements (you can assume each parent set doesn't have sets with duplicate elements itself), while I can remove only the 2nd level sets, not the elements themselves, for example those:

[(a,b),(c),(d)] and [(a),(b,c),(e)]

would get me:

[(a,b),(c),(d),(e)],[(a),(b,c),(d),(e)],[(a),(c),(d),(e)]

one set from one parent set could give me a subset of sets from the other parent set that are incompatible (ie contain one or more common elements), keeping each of those in the second set would give me a new subset of sets that I can remove from the first and so on.. running this recursively and checking for repetitions is pretty time intensive...

is there a better way to approach this ? what tools I can use ? (currently doing this in python)

PS a small code example of what I mean, again, I'm not looking to fix bugs, just asking for a neater approach:

def combine(S1, S2, incompatible_set, parent_set_pairs):

    new_S2 = None
    new_incompatible_sets = set()

    for s2 in S2:
        if incompatible_set & s2:
            new_incompatible_sets.add(s2)
            if not S2:
                S2 = S2.copy()
            new_S2.remove(s2)
    parent_set_pairs.add((S1, new_S2)) # here it should look for a new incompatible set in S1 and apply the function again..
    for new_incompatible_set in new_incompatible_sets:
        new_parent_set_pairs = combine(S2, S1, new_incompatible_set,
                                 parent_set_pairs)
        for pair1 in new_parent_set_pairs:
            for pair2 in parent_set_pairs:
                if pair1[0]|pair1[1] not in pair2[0]|pair2[1]
            parent_set_pairs.add(new_parent_set_pairs)

    return parent_set_pairs

Here is an itertools-based approach which takes two lists of disjoint sets and returns all maximal combinations of disjoints sets drawn from the two lists subject to the constraint that the sets in the combinations are disjoint:

from itertools import combinations

def maxDisjoints(A,B):
    n = len(A)
    combos = set()
    for i in range(n+1):
        for partA in combinations(A,i):
            A_covered = set().union(*partA)
            filteredB = [s for s in B if A_covered & s == set()]
            B_covered = set().union(*filteredB)
            filteredA = [s for s in A if B_covered & s == set()]
            #filteredA is a superset of partA
            #freeze and record:
            combos.add(tuple(frozenset(s) for s in filteredA + filteredB))
    #unfreeze and return as list of lists of sets:       
    return [[set(f) for f in t] for t in combos]

To test it:

A = [{'a','b'},{'c'},{'d'}]
B = [{'a'},{'b','c'},{'e'}]
for p in jointPartitions(A,B):
    print(p)

Output:

[{'c'}, {'d'}, {'a'}, {'e'}]
[{'d'}, {'a'}, {'b', 'c'}, {'e'}]
[{'b', 'a'}, {'c'}, {'d'}, {'e'}]

As a technical annoyance, neither sets nor lists are hashable so it is impossible to maintain a set of lists of sets to filter out redundancies (since the algorithm generates the same maximal combinations more than once). Thus I had to turn the lists of sets into tuples of frozen sets to temporarily record them, only to reverse the process at the end.

This is a brute-force approach. It iterates through all 2^n subsets of A , extending each one to a maximal combination in the only way possible. If there is a discrepancy between the size of A and the size of B, pass the shorter of the two lists first (or tweak the code so that it does this automatically). This approach is reasonable for smallish collections but won't scale well. I suspect that the problem itself is NP-hard, so there is a limit to how much efficiency you can expect (which isn't to say that this algorithm is optimal).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM