Я пишу программу на Python и понял, что задача, которую мне нужно решить, требует от меня, учитывая набор S с элементами n (| S | = n), проверить функцию на всех возможных подмножествах определенного порядка m ( т.е. с количеством элементов
Я собираюсь написать решение в форме:
def findsubsets(S, m):
subsets = set([])
...
return subsets
Но зная Python, я ожидал, что решение уже существует.
Как лучше всего этого добиться?
Буквально у меня была такая же проблема, я решил написать ее сам, а потом понял, что для этого должна существовать библиотека Python!






itertools.combinations - ваш друг, если у вас Python 2.6 или выше. В противном случае проверьте ссылку на реализацию эквивалентной функции.
import itertools
def findsubsets(S,m):
return set(itertools.combinations(S, m))
S: Набор, для которого вы хотите найти подмножества
m: количество элементов в подмножестве
я бы не возвращал набор, а просто возвращал итератор (или просто использовал комбинацию () без определения findubsets () ...)
@hop OP особенно просил наборы. Если опустить приведение набора, то можно будет повторяться в разном порядке, например: (1,2,3), (2,3,1), (3,1,2) ...
@ Джеймс Брэдбери: Простите, я не понимаю, что вы имеете в виду. Вы путаете это с перестановками?
Вот функция, которая дает вам все подмножества целых чисел [0..n], а не только подмножества заданной длины:
from itertools import combinations, chain
def allsubsets(n):
return list(chain(*[combinations(range(n), ni) for ni in range(n+1)]))
так например
>>> allsubsets(3)
[(), (0,), (1,), (2,), (0, 1), (0, 2), (1, 2), (0, 1, 2)]
Полезная формула, но используйте chain.from_iterable вместо потенциально очень длинного набора звезд. И какой смысл перебирать комбинации в список ([ ... ]), разворачивать, объединять в итератор (chain), а затем превращать в список очередной раз? PS. лучший рецепт находится в документации itertools и в другом (более позднем) ответе здесь.
Именованные лямбды - плохая практика. Вместо этого используйте def.
@wjandrea это однострочный.
@ Ковальски, что ты имеешь в виду?
@Kowalski Да, конечно, это было однострочно. Что ты имеешь в виду?
@wjandrea nevermind, IMHO не всегда плохо использовать именованные лямбды.
Вот некоторый псевдокод - вы можете вырезать одни и те же рекурсивные вызовы, сохраняя значения для каждого вызова по ходу и перед рекурсивным вызовом, проверяя, присутствует ли уже значение вызова.
В следующем алгоритме будут все подмножества, за исключением пустого набора.
list * subsets(string s, list * v) {
if (s.length() == 1) {
list.add(s);
return v;
}
else
{
list * temp = subsets(s[1 to length-1], v);
int length = temp->size();
for(int i=0;i<length;i++) {
temp.add(s[0]+temp[i]);
}
list.add(s[0]);
return temp;
}
}
Так, например, если s = "123", тогда вывод будет:
1
2
3
12
13
23
123
Использование канонической функции для получения powerset со страницы рецепт itertools:
from itertools import chain, combinations
def powerset(iterable):
"""
powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
"""
xs = list(iterable)
# note we return an iterator rather than a list
return chain.from_iterable(combinations(xs,n) for n in range(len(xs)+1))
Используется как:
>>> list(powerset("abc"))
[(), ('a',), ('b',), ('c',), ('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]
>>> list(powerset(set([1,2,3])))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
сопоставить с наборами, если хотите, чтобы вы могли использовать объединение, пересечение и т. д.:
>>> map(set, powerset(set([1,2,3])))
[set([]), set([1]), set([2]), set([3]), set([1, 2]), set([1, 3]), set([2, 3]), set([1, 2, 3])]
>>> reduce(lambda x,y: x.union(y), map(set, powerset(set([1,2,3]))))
set([1, 2, 3])
Без использования itertools:
В Python 3 вы можете использовать yield from для добавления метода генератора подмножества во встроенный класс set:
class SetWithSubset(set):
def subsets(self):
s1 = []
s2 = list(self)
def recfunc(i=0):
if i == len(s2):
yield frozenset(s1)
else:
yield from recfunc(i + 1)
s1.append(s2[ i ])
yield from recfunc(i + 1)
s1.pop()
yield from recfunc()
Например, приведенный ниже фрагмент работает должным образом:
x = SetWithSubset({1,2,3,5,6})
{2,3} in x.subsets() # True
set() in x.subsets() # True
x in x.subsets() # True
x|{7} in x.subsets() # False
set([5,3]) in x.subsets() # True - better alternative: set([5,3]) < x
len(x.subsets()) # 32
отличное использование yield from и алгоритмическое объяснение
$ python -c "import itertools; a=[2,3,5,7,11]; print sum([list(itertools.combinations(a, i)) for i in range(len(a)+1)], [])"
[(), (2,), (3,), (5,), (7,), (11,), (2, 3), (2, 5), (2, 7), (2, 11), (3, 5), (3, 7), (3, 11), (5, 7), (5, 11), (7, 11), (2, 3, 5), (2, 3, 7), (2, 3, 11), (2, 5, 7), (2, 5, 11), (2, 7, 11), (3, 5, 7), (3, 5, 11), (3, 7, 11), (5, 7, 11), (2, 3, 5, 7), (2, 3, 5, 11), (2, 3, 7, 11), (2, 5, 7, 11), (3, 5, 7, 11), (2, 3, 5, 7, 11)]
Вот один изящный способ с простым для понимания алгоритмом.
import copy
nums = [2,3,4,5]
subsets = [[]]
for n in nums:
prev = copy.deepcopy(subsets)
[k.append(n) for k in subsets]
subsets.extend(prev)
print(subsets)
print(len(subsets))
# [[2, 3, 4, 5], [3, 4, 5], [2, 4, 5], [4, 5], [2, 3, 5], [3, 5], [2, 5], [5],
# [2, 3, 4], [3, 4], [2, 4], [4], [2, 3], [3], [2], []]
# 16 (2^len(nums))
Должен быть k.append(n) вместо k.extend(n)
>>>Set = ["A", "B","C","D"]
>>>n = 2
>>>Subsets=[[i for i,s in zip(Set, status) if int(s) ] for status in [(format(bit,'b').zfill(len(Set))) for bit in range(2**len(Set))] if sum(map(int,status)) == n]
>>>Subsets
[['C', 'D'], ['B', 'D'], ['B', 'C'], ['A', 'D'], ['A', 'C'], ['A', 'B']]
объясните, почему это сделало то, о чем просят.
Просто попробуйте, и вы увидите, сделает ли он то, о чем просил
Другое решение с использованием рекурсии:
def subsets(nums: List[int]) -> List[List[int]]:
n = len(nums)
output = [[]]
for num in nums:
output += [curr + [num] for curr in output]
return output
Начиная с пустого подмножества в списке вывода. На каждом шаге мы учитываем новое целое число и генерируем новые подмножества из существующих.
scipy.misc.comb(S, m)дает количество получаемых подмножеств. В конечном итоге вам следует выполнить проверку, прежде чем выполнять свой код, поскольку количество подмножеств S размера m очень быстро становится очень большим.