Как найти все подмножества набора ровно из n элементов?

Я пишу программу на Python и понял, что задача, которую мне нужно решить, требует от меня, учитывая набор S с элементами n (| S | = n), проверить функцию на всех возможных подмножествах определенного порядка m ( т.е. с количеством элементов мм>). Использовать ответ для получения частичного решения, а затем повторить попытку со следующим порядком m = m + 1, пока m = n.

Я собираюсь написать решение в форме:

def findsubsets(S, m):
    subsets = set([])
    ...
    return subsets

Но зная Python, я ожидал, что решение уже существует.

Как лучше всего этого добиться?

scipy.misc.comb(S, m) дает количество получаемых подмножеств. В конечном итоге вам следует выполнить проверку, прежде чем выполнять свой код, поскольку количество подмножеств S размера m очень быстро становится очень большим.
Martin Thoma 20.02.2015 20:19

Буквально у меня была такая же проблема, я решил написать ее сам, а потом понял, что для этого должна существовать библиотека Python!

Srini 10.11.2016 21:23
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
77
2
125 967
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

itertools.combinations - ваш друг, если у вас Python 2.6 или выше. В противном случае проверьте ссылку на реализацию эквивалентной функции.

import itertools
def findsubsets(S,m):
    return set(itertools.combinations(S, m))

S: Набор, для которого вы хотите найти подмножества
m: количество элементов в подмножестве

я бы не возвращал набор, а просто возвращал итератор (или просто использовал комбинацию () без определения findubsets () ...)

user3850 17.12.2008 17:23

@hop OP особенно просил наборы. Если опустить приведение набора, то можно будет повторяться в разном порядке, например: (1,2,3), (2,3,1), (3,1,2) ...

James Bradbury 10.02.2016 17:30

@ Джеймс Брэдбери: Простите, я не понимаю, что вы имеете в виду. Вы путаете это с перестановками?

user3850 10.02.2016 17:58

Вот функция, которая дает вам все подмножества целых чисел [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 и в другом (более позднем) ответе здесь.

alexis 06.11.2013 16:47

Именованные лямбды - плохая практика. Вместо этого используйте def.

wjandrea 23.01.2020 03:09

@wjandrea это однострочный.

Ekrem Dinçel 08.09.2020 13:16

@ Ковальски, что ты имеешь в виду?

wjandrea 08.09.2020 15:34

@Kowalski Да, конечно, это было однострочно. Что ты имеешь в виду?

wjandrea 08.09.2020 20:40

@wjandrea nevermind, IMHO не всегда плохо использовать именованные лямбды.

Ekrem Dinçel 08.09.2020 20:41

Вот некоторый псевдокод - вы можете вырезать одни и те же рекурсивные вызовы, сохраняя значения для каждого вызова по ходу и перед рекурсивным вызовом, проверяя, присутствует ли уже значение вызова.

В следующем алгоритме будут все подмножества, за исключением пустого набора.

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 и алгоритмическое объяснение

Ori 23.02.2018 16:32

$ 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)

unify 16.03.2019 04:45
>>>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']]

объясните, почему это сделало то, о чем просят.

jwenting 17.12.2019 13:03

Просто попробуйте, и вы увидите, сделает ли он то, о чем просил

Mohamed Moawia 17.12.2019 15:29

Другое решение с использованием рекурсии:

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

Начиная с пустого подмножества в списке вывода. На каждом шаге мы учитываем новое целое число и генерируем новые подмножества из существующих.

Другие вопросы по теме