I have list of sets:
graphs = [{1, 2, 3}, {4, 5}, {6}]
I have to check if input set can be created as sum of sets inside graphs.
For example:
input1 = {1, 2, 3, 6} # answer - True
input2 = {1, 2, 3, 4} # answer - False, because "4" is only a part of another set, only combinations of full sets are required
In other words, there are all combinations of sets inside graphs:
{1, 2, 3}
{4, 5}
{6}
{1, 2, 3, 6}
{1, 2, 3, 4, 5}
{4, 5, 6}
{1, 2, 3, 4, 5, 6}
I need to know, if one of these combinations is equal to input.
How should I correctly iterate through graphs elements to get answer? If graphs is bigger, there would be some problems with finding all the combinations.
I think you are looking at this the wrong way. I think it is better to remove any set that contains an element you cannot use (i.e. remove set {4,5} when you are looking for {1,2,3,4}. Then create union of all other sets and see if this is equal to your input set.
This way you will not need to find all combinations, just a (at most) O(n*len(sets)) elimination step at first.
graphs = [i for i in graphs if i.issubset(input1) ]
check for answer:
result = set().union(*graphs) == input1
You can find all combinations with itertools.combinations, then simply compare the sets:
from itertools import combinations, chain
def check(graphs, inp):
for i in range(1, len(graphs)+1):
for p in combinations(graphs, i):
if set(chain(*p)) == inp:
return True
return False
graphs = [{1, 2, 3}, {4, 5}, {6}]
input1 = {1, 2, 3, 6}
input2 = {1, 2, 3, 4}
print(check(graphs, input1))
print(check(graphs, input2))
Prints:
True
False
Related
I have two lists of sets, let's say:
[{1, 2, 3}, {4, 5}, {6, 7}] and [{1, 2}, {3, 4}, {5, 6, 7}]
No set in the list has the same element and the sum of all sets in both lists is the same.
The function should check if the sets in both lists had the same elements. If there were some differences, put them in another set.
So above example should return:
[{1, 2}, {3}, {4}, {5}, {6, 7}]
I work on large sets so I would need this function to be as effective as possible.
Here is the example code and how I would like it to work:
def mergeSets(x, y):
out = set()
for i in x:
out = out.union(i)
# this allows me to get the set of all elements but here where my mind stops working
# the problem sounds simple but thinking hours I can not think of good algorythm for this issue :(
# I found set.intersection() function but it works on single sets only, not lists of sets
return out
x = mergeSets([{1, 2, 3}, {4, 5}, {6, 7}], [{1, 2}, {3, 4}, {5, 6, 7}])
print(x)
# [{1, 2}, {3}, {4}, {5}, {6, 7}]
x = mergeSets([{1, 2}, {3, 4, 5, 6, 7}, {8}], [{1}, {2, 3, 4}, {5, 6, 7, 8}])
print(x)
# [{1}, {2}, {3, 4}, {5, 6, 7}, {8}]
EDIT: the data doesn't have to be sorted and may be even of different types than integer
EDIT2: the input lists don't have to be sorted so sets may appear in random order
Given that each value occurs in exactly two sets (one per input list), you could collect the index pairs for each value, where an index pair gives an indication in which two sets (at which indexes in both lists) the value occurs.
Unique pairs indicate unique sets in the output, so a dictionary of such pairs can serve to populate the result:
from collections import defaultdict
def merge_sets(lista, listb):
index_in_a = {
val: idx
for idx, elem in enumerate(lista) for val in elem
}
set_by_key = defaultdict(set)
for idx, elem in enumerate(listb):
for val in elem:
set_by_key[(index_in_a[val], idx)].add(val)
return list(set_by_key.values())
This looks O(n) to me.
NB: since the order of iteration over a set is not defined, the order of the output can look a bit mingled, but I assume the order of where sets appear in the output is not significant.
.intersection() works if you iterate over each list, like so:
def merge_sets(lst1, lst2):
final = []
for i in lst1:
for j in lst2:
if len(i.intersection(j)) > 0:
final.append(i.intersection(j))
return final
Using the same input lists you should see the following outputs:
>>> x = merge_sets([{1, 2, 3}, {4, 5}, {6, 7}], [{1, 2}, {3, 4}, {5, 6, 7}])
>>> print(x)
[{1, 2}, {3}, {4}, {5}, {6, 7}]
>>> y = merge_sets([{1, 2}, {3, 4, 5, 6, 7}, {8}], [{1}, {2, 3, 4}, {5, 6, 7, 8}])
>>> print(y)
[{1}, {2}, {3, 4}, {5, 6, 7}, {8}]
This runs in O(n2) time.
You could re-write this using a list comprehension and get the same results:
def merge_sets(lst1, lst2):
final = [i.intersection(j) for i in lst1 for j in lst2 if len(i.intersection(j)) > 0]
return final
To improve the time complexity you could try an approach that does away with the nested for loop. But for now, this works.
I was wondering if there is an algorithm that can return the intersection of all possible combinations of n different lists. My example is the following with n = 3 different lists:
list1 = [1,2,3,4,5]
list2 = [1,3,5]
list3 = [1,2,5]
the outcome should look like this:
list1_2_intersection = [1,3,5]
list1_3_intersection = [1,2,5]
list2_3_intersection = [1,5]
list1_2_3_intersection = [1,5]
I was thinking to first use combination to get all possible combinations of n sets and use that to create intersections using intersection manually. However, since I have 6 different sets this seems very time consuming, which is why I was wondering if there is a more efficient way to compute this. Thanks in advance! :)
If you have all sets in a list, you can use the more-itertools-package (pip install more-itertools), which is based on itertools, to get all combinations of those elements using more_itertools.powerset, as is done in this post.
Then getting the intersection is a matter of using set.intersection as you point out yourself. So a solution can look like this
from more_itertools import powerset
sets = [{1,2,3,4,5},{1,3,5},{1,2,5}]
pwset = powerset(sets)
res = [c[0].intersection(*c[1:]) if len(c)>1 else c for c in pwset]
If you just load or define the sets in an iterable like a list:
my_sets = [
{1, 2, 3, 4, 5},
{1, 3, 5},
{1, 2, 5}
]
my_set_intersection = set.intersection(*my_sets)
print(my_set_intersection)
Of course, the print is just there to show the result, which is in my_set_intersection
If you just have a couple of sets:
set1 = {1, 2, 3, 4, 5},
set2 = {1, 3, 5},
set3 = {1, 2, 5}
intersection_123 = set1 & set2 & set3
# or:
intersection_123 = set.intersection(set1, set2, set3)
For all possible intersections of combinations of all possible sizes of a group of sets:
from itertools import combinations
my_sets = [
{1, 2, 3, 4, 5},
{1, 3, 5},
{1, 2, 5}
]
all_intersections = [set.intersection(*x) for n in range(len(my_sets)) for x in combinations(my_sets, n+1)]
print(all_intersections)
Result:
[{1, 2, 3, 4, 5}, {1, 3, 5}, {1, 2, 5}, {1, 3, 5}, {1, 2, 5}, {1, 5}, {1, 5}]
If you don't like the duplicates (because different combinations of sets can yield the same intersection), you can of course just make the list a set instead.
Note that this is similar to the solution using more_itertools, but I don't like requiring a third party library for something as trivial as generating a powerset (although it may perform better if well-written, which may matter for extremely large sets).
Also note that this leaves out the empty set (the intersection of a combination of size 0), but that's trivial to add of course.
I would like to select only those sets from a list of sets that are subsets of another set, so essentially that:
sets = [{1, 2, 3}, {6, 7, 8}]
s = {1, 2, 3, 4, 5, 6, 7}
list(compress(sets, [sub <= s for sub in sets]))
This works but it feels wrong to first evaluate the list comprehension and afterwards using compress. Is there a way to index sets directly, like:
sets[[sub <= s for sub in sets]] ### This does not work!
Try this:
[i for i in sets if all(j in s for j in i)]
Test:
In [226]: sets = [{1, 2, 3}, {6, 7, 8}, {1,2}, {3}, {7,1}, {7,8}, {8}]
In [227]: s = {1, 2, 3, 4, 5, 6, 7}
In [228]: [i for i in sets if all(j in s for j in i)]
Out[228]: [{1, 2, 3}, {1, 2}, {3}, {1, 7}]
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
How can I improve this code, without computing all possible combinations returned by itertools.product function.
Is there any other solutions to do it efficiently.
This is what I have tried:
import itertools
mylist = [[1,2,3],[1,3,4],[1,2,3]]
k = [set(i) for i in list(itertools.product(*mylist))]
k = sorted(k)
D_all = list(k for k, _ in itertools.groupby(k))
D_all.sort(key=len)
# Finding and displaying the minimum order-split combination
l = len(D_all[0])
print("Minimum number of Distributor Combination found is: {}".format(l))
print("The Possible combinations of {} are: ".format(l))
D_best = []
c = 0
for n,i in enumerate(D_all):
if len(i)<=l:
c +=1
print("{}:{}".format(c,i))
D_best.append(i)
if len(i)>l+1: break
output:
Minimum number of Distributor Combination found is: 1
The Possible combinations of 1 are:
1:{'1'}
2:{'3'}
I believe I have it. You can take advantage of the fact that itertools.product yields its tuples in a deterministic way; you know beforehand when all the singletons would be yielded. You just have to calculate when that will happen.
from itertools import islice, product
from functools import reduce
mylist = [[1, 2, 3], [1, 3, 4], [1, 2, 3]]
stop_cond = (len(mylist[0]) - 1) * reduce(lambda x, y: len(x) * len(y), mylist[1:])
pivot = mylist[0][-1]
stop_cond += reduce(lambda x, y: (x.index(pivot) + 1) * (y.index(pivot) + 1), mylist[1:])
k = [set(item) for item in islice(product(*mylist), stop_cond)]
print(k) #[{1}, {1, 2}, {1, 3}, {1, 3}, {1, 2, 3}, {1, 3}, {1, 4}, {1, 2, 4}, {1, 3, 4}, {1, 2}, {1, 2}, {1, 2, 3}, {1, 2, 3}, {2, 3}, {2, 3}, {1, 2, 4}, {2, 4}, {2, 3, 4}, {1, 3}, {1, 2, 3}, {1, 3}, {1, 3}, {2, 3}, {3}]
In this case, the last singleton would be {3} because 3 appears on the last position of the first list (i call that pivot in the code). To get its singleton you have to get all others first and that takes 3 * 3 yields for each. That is 9 + 9 = 18. Now for the last, you just need to find its index in the remaining lists (in a 1-index based system) and multiply those together.
You have an arbitrary number of sets, for example:
sets = [{1,2,3}, {3,4,5}, {5,6,7}]
You want to see if any value in one set is also in any other set. What is the most efficient way to do this?
Currently I have the following:
index = 0
for set in sets:
for i in range(index + 1, len(sets)):
print set, sets[i], set & sets[i]
index += 1
Which results in:
set([1, 2, 3]) set([3, 4, 5]) set([3])
set([1, 2, 3]) set([5, 6, 7]) set([])
set([3, 4, 5]) set([5, 6, 7]) set([5])
Its a minor tweak but you can let itertools.combinations generate the set pairs for you. This example is python 3 so the representations look a little different, but should work fine in 2.x
>>> import itertools
>>> sets = [{1,2,3}, {3,4,5}, {5,6,7}]
>>> for s1,s2 in itertools.combinations(sets,2):
... print(s1, s2, s1 & s2)
...
{1, 2, 3} {3, 4, 5} {3}
{1, 2, 3} {5, 6, 7} set()
{3, 4, 5} {5, 6, 7} {5}
set.intersection(*list_of_sets)
will unpack your list of sets and find intersection.