Jeśli nie podoba ci się ta pusta krotka na początku, możesz po prostu zmienić rangeinstrukcję na, range(1, len(s)+1)aby uniknąć kombinacji o długości 0.
To najszybsza odpowiedź, jaką udało mi się znaleźć, porównując inne rozwiązania na tej stronie z tym przy użyciu modułu timeit w Pythonie. Jednak w niektórych przypadkach, jeśli potrzebujesz zmodyfikować wynikowy wynik (np. Łącząc litery w łańcuchy), napisanie własnego przepisu przy użyciu generatorów i utworzenie żądanego wyniku (np. Dodanie dwóch ciągów) może być znacznie szybsze.
Ceasar Bautista
dlaczego jest s = list(iterable)potrzebny?
Jack Stevens,
@JackStevens, ponieważ elementy iteracyjne nie są przewijalne i nie muszą być __len__zaimplementowane; wypróbuj powerset((n for n in range(3)))bez zawijania listy.
hoefling
1
w przypadku dużych strun wymagałoby to dużej ilości pamięci!
NoobEditor
1
@AlexandreHuat: Zakresy to leniwe sekwencje, a nie iteratory. powerset(range(3))działałby dobrze nawet bezs = list(iterable) .
user2357112 obsługuje Monikę w marcu
54
Tutaj jest więcej kodu dla PowerSet. To jest napisane od podstaw:
>>> defpowerset(s):... x = len(s)
... for i inrange(1 << x):
... print [s[j] for j inrange(x) if (i & (1 << j))]
...
>>> powerset([4,5,6])
[]
[4]
[5]
[4, 5]
[6]
[4, 6]
[5, 6]
[4, 5, 6]
Komentarz Marka Rushakoffa ma tutaj zastosowanie: „Jeśli nie podoba ci się ta pusta krotka na początku, on.” Możesz po prostu zmienić instrukcję zakresu na zakres (1, len (s) +1), aby uniknąć kombinacji o długości 0 ”, z wyjątkiem mojego przypadku, gdy zmienisz for i in range(1 << x)na for i in range(1, 1 << x).
Wracając do tego lata później, teraz napisałbym to tak:
defpowerset(s):
x = len(s)
masks = [1 << i for i inrange(x)]
for i inrange(1 << x):
yield [ss for mask, ss inzip(masks, s) if i & mask]
Następnie kod testowy wyglądałby następująco:
print(list(powerset([4, 5, 6])))
Używanie yieldoznacza, że nie musisz obliczać wszystkich wyników w pojedynczym fragmencie pamięci. Zakłada się, że wstępne obliczenie masek poza główną pętlą jest opłacalną optymalizacją.
To jest twórcza odpowiedź. Jednak zmierzyłem go za pomocą timeit, aby porównać go z Markiem Rushakoffem i zauważyłem, że jest znacznie wolniejszy. Aby wygenerować zestaw mocy składający się z 16 elementów 100 razy, moje pomiary wyniosły 0,55 w porównaniu z 15,6.
Ceasar Bautista
19
Jeśli szukasz szybkiej odpowiedzi, właśnie wyszukałem w google „Python power set” i wymyśliłem to: Python Power Set Generator
Oto kopia i wklejanie z kodu na tej stronie:
defpowerset(seq):"""
Returns all the subsets of this set. This is a generator.
"""iflen(seq) <= 1:
yield seq
yield []
else:
for item in powerset(seq[1:]):
yield [seq[0]]+item
yield item
Można tego użyć w następujący sposób:
l = [1, 2, 3, 4]
r = [x for x in powerset(l)]
Teraz r to lista wszystkich potrzebnych elementów, którą można posortować i wydrukować:
W przypadku pustej tablicy jako danych wejściowych powyższy kod zwróciłby się [[][]], aby naprawić to tylko oddzielenie przypadków do sprawdzania długościif len(seq) == 0: yield [] elif len(seq) == 1: yield seq yield []
Ayush
3
Dla porównania zmierzyłem to (za pomocą edycji Ayusha) za pomocą timeit i porównałem to z przepisem na poweret w odpowiedzi Marka Rushakoffa. Na moim komputerze, aby 100 razy wygenerować zestaw 16 elementów, algorytm ten zajął 1,36 sekundy, podczas gdy Rushakoff zajął 0,55.
Ceasar Bautista
Jaka będzie dla tego złożoność czasowa?
CodeQuestor
13
defpowerset(lst):return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
defpowerset(seq):"""
Returns all the subsets of this set. This is a generator.
"""iflen(seq) <= 0:
yield []
else:
for item in powerset(seq[1:]):
yield [seq[0]]+item
yield item
Wiem, że wcześniej dodałem odpowiedź, ale bardzo podoba mi się moja nowa implementacja. Biorę zestaw jako dane wejściowe, ale w rzeczywistości może to być dowolny iterowalny zestaw, a ja zwracam zestaw zestawów, który jest zestawem mocy wejścia. Podoba mi się to podejście, ponieważ jest bardziej zgodne z matematyczną definicją zbioru potęg ( zbiór wszystkich podzbiorów ).
defpower_set(A):"""A is an iterable (list, tuple, set, str, etc)
returns a set which is the power set of A."""
length = len(A)
l = [a for a in A]
ps = set()
for i inrange(2 ** length):
selector = f'{i:0{length}b}'
subset = {l[j] for j, bit inenumerate(selector) if bit == '1'}
ps.add(frozenset(subset))
return ps
Jeśli chcesz dokładnie uzyskać wyniki, które zamieściłeś w swojej odpowiedzi, użyj tego:
Wiadomo, że liczba elementów zestawu mocy jest 2 ** len(A)taka, że w forpętli można było wyraźnie zobaczyć .
Muszę przekonwertować dane wejściowe (najlepiej zestaw) na listę, ponieważ przez zestaw jest to struktura danych unikalnych nieuporządkowanych elementów, a kolejność będzie kluczowa do wygenerowania podzbiorów.
selectorjest kluczem do tego algorytmu. Zwróć uwagę, że selectorma taką samą długość jak zestaw wejściowy i aby było to możliwe, używa łańcucha f z dopełnieniem. Zasadniczo pozwala mi to wybrać elementy, które zostaną dodane do każdego podzbioru podczas każdej iteracji. Powiedzmy, że zestaw wejściowy ma 3 elementy {0, 1, 2}, więc selector przyjmie wartości od 0 do 7 (włącznie), które w systemie binarnym to:
Tak więc każdy bit może służyć jako wskaźnik, czy należy dodać element oryginalnego zestawu, czy nie. Spójrz na liczby binarne i pomyśl o każdej liczbie jako o elemencie super zestawu, co 1oznacza, że jnależy dodać element pod indeksem , a 0to oznacza, że ten element nie powinien być dodawany.
Używam rozumienia zestawu do generowania podzbioru w każdej iteracji i konwertuję ten podzbiór na zestaw, frozensetaby móc go dodać do ps(zestaw potęgowy). W przeciwnym razie nie będę mógł go dodać, ponieważ zestaw w Pythonie składa się tylko z niezmiennych obiektów.
Uproszczenie
Możesz uprościć kod, używając niektórych wyrażeń w Pythonie, dzięki czemu możesz pozbyć się pętli for. Możesz również użyć, zipaby uniknąć używania jindeksu, a kod zakończy się następująco:
defpower_set(A):
length = len(A)
return {
frozenset({e for e, b inzip(A, f'{i:{length}b}') if b == '1'})
for i inrange(2 ** length)
}
Otóż to. W tym algorytmie podoba mi się to, że jest jaśniejszy i bardziej intuicyjny niż inne, ponieważ wygląda całkiem magicznie, itertoolsmimo że działa zgodnie z oczekiwaniami.
Uważam, że następujący algorytm jest bardzo przejrzysty i prosty:
defget_powerset(some_list):"""Returns all subsets of size 0 - len(some_list) for some_list"""iflen(some_list) == 0:
return [[]]
subsets = []
first_element = some_list[0]
remaining_list = some_list[1:]
# Strategy: get all the subsets of remaining_list. For each# of those subsets, a full subset list will contain both# the original subset as well as a version of the subset# that contains first_elementfor partial_subset in get_powerset(remaining_list):
subsets.append(partial_subset)
subsets.append(partial_subset[:] + [first_element])
return subsets
Innym sposobem generowania zestawu mocy jest generowanie wszystkich liczb binarnych, które mają nbity. Jako potęgę ustawiono ilość liczb z ncyframi 2 ^ n. Zasada tego algorytmu polega na tym, że element może być obecny lub nie w podzbiorze, ponieważ cyfra binarna może mieć wartość jeden lub zero, ale nie obie.
defpower_set(items):
N = len(items)
# enumerate the 2 ** N possible combinationsfor i inrange(2 ** N):
combo = []
for j inrange(N):
# test bit jth of integer iif (i >> j) % 2 == 1:
combo.append(items[j])
yield combo
Znalazłem oba algorytmy, kiedy brałem MITx: 6.00.2x Wprowadzenie do myślenia obliczeniowego i nauki o danych, i uważam, że jest to jeden z najłatwiejszych do zrozumienia algorytmów, jakie widziałem.
defget_power_set(s):
power_set=[[]]
for elem in s:
# iterate over the sub sets so farfor sub_set in power_set:
# add a new subset consisting of the subset at hand added elem
power_set=power_set+[list(sub_set)+[elem]]
return power_set
Modyfikowanie zmiennej pętli ( power_set) w pętli, którą zarządza, jest bardzo wątpliwą praktyką. Na przykład, załóżmy, że napisał to zamiast proponowanej zmiennej modyfikacji kodu: power_set += [list(sub_set)+[elem]]. Wtedy pętla się nie kończy.
hughdbrown
3
Chciałem tylko zapewnić najbardziej zrozumiałe rozwiązanie, wersję anty-code-golf.
from itertools import combinations
l = ["x", "y", "z", ]
defpowerset(items):
combo = []
for r inrange(len(items) + 1):
#use a list to coerce a actual list from the combinations generator
combo.append(list(combinations(items,r)))
return combo
l_powerset = powerset(l)
for i, item inenumerate(l_powerset):
print"All sets of length ", i
print item
defpower_set(input):# returns a list of all subsets of the list aif (len(input) == 0):
return [[]]
else:
main_subset = [ ]
for small_subset in power_set(input[1:]):
main_subset += [small_subset]
main_subset += [[input[0]] + small_subset]
return main_subset
print(power_set([0,1,2,3]))
Prostym sposobem byłoby wykorzystanie wewnętrznej reprezentacji liczb całkowitych w ramach arytmetyki dopełnienia do 2.
Binarna reprezentacja liczb całkowitych to {000, 001, 010, 011, 100, 101, 110, 111} dla liczb z przedziału od 0 do 7. Dla wartości licznika w postaci liczby całkowitej, biorąc pod uwagę 1 jako włączenie odpowiedniego elementu do zbioru i „0” jako wykluczenie możemy wygenerować podzbiory na podstawie sekwencji zliczania. Liczby należy generować od 0do, pow(2,n) -1gdzie n jest długością tablicy, tj. Liczbą bitów w reprezentacji binarnej.
Bazującą na niej prostą funkcję generatora podzbiorów można zapisać w następujący sposób. Zasadniczo polega
defsubsets(array):ifnot array:
returnelse:
length = len(array)
for max_int inrange(0x1 << length):
subset = []
for i inrange(length):
if max_int & (0x1 << i):
subset.append(array[i])
yield subset
a następnie może być używany jako
defget_subsets(array):
powerset = []
for i in subsets(array):
powerser.append(i)
return powerset
Testowanie
Dodanie następującego w pliku lokalnym
if __name__ == '__main__':
sample = ['b', 'd', 'f']
for i inrange(len(sample)):
print"Subsets for " , sample[i:], " are ", get_subsets(sample[i:])
daje następujący wynik
Subsets for ['b', 'd', 'f'] are [[], ['b'], ['d'], ['b', 'd'], ['f'], ['b', 'f'], ['d', 'f'], ['b', 'd', 'f']]
Subsets for ['d', 'f'] are [[], ['d'], ['f'], ['d', 'f']]
Subsets for ['f'] are [[], ['f']]
Prawie wszystkie te odpowiedzi używają listraczej niż set, co wydawało mi się trochę oszustwem. Tak więc z ciekawości spróbowałem zrobić naprawdę prostą wersjęset i podsumować ją dla innych ludzi „nowych w Pythonie”.
Zauważyłem, że jest kilka dziwactw związanych z implementacją zestawu w Pythonie . Głównym zaskoczeniem dla mnie była obsługa pustych zestawów. Jest to w przeciwieństwie do implementacji Set Rubiego , w której mogę po prostu zrobić Set[Set[]]i uzyskać Setpusty element zawierający Set, więc początkowo wydawało mi się to trochę zagmatwane.
Aby przejrzeć, robiąc powersetz sets, napotkałem dwa problemy:
aby uzyskać zestaw zestawów set({set()})i set.add(set)nie zadziała, ponieważ set()nie można go hashować
Aby rozwiązać oba problemy, wykorzystałem frozenset(), co oznacza, że nie do końca dostaję to, czego chcę (typ jest dosłownie set), ale korzystam z ogólnego setinterfejsu.
defpowerset(original_set):# below gives us a set with one empty set in it
ps = set({frozenset()})
for member in original_set:
subset = set()
for m in ps:
# to be added into subset, needs to be# frozenset.union(set) so it's hashable
subset.add(m.union(set([member]))
ps = ps.union(subset)
return ps
Poniżej otrzymujemy 2² (16) frozensets poprawnie jako wyjście:
Ponieważ w Pythonie nie ma sposobu, aby mieć s setof setsw, jeśli chcesz zamienić je frozensetna sets, będziesz musiał odwzorować je z powrotem na a list( list(map(set, powerset(set([1,2,3,4]))))
) lub zmodyfikować powyższe.
Tak wiele osób wymyśla tutaj koło na nowo, IMHO to najlepsza odpowiedź, ponieważ może już być w Twoich zależnościach, ponieważ wymaga tego wiele popularnych bibliotek, np. Pytest. libraries.io/pypi/more-itertools/dependents
lorey
1
Pobieranie wszystkich podzbiorów z rekurencją. Szalona jedna linijka
from typing import List
defsubsets(xs: list) -> List[list]:return subsets(xs[1:]) + [x + [xs[0]] for x in subsets(xs[1:])] if xs else [[]]
deffindsubsets(s, n):returnlist(itertools.combinations(s, n))
defallsubsets(s) :
a = []
for x inrange(1,len(s)+1):
a.append(map(set,findsubsets(s,x)))
return a
Odpowiedzi zawierające tylko kod są uważane za niskiej jakości: pamiętaj, aby wyjaśnić, co robi Twój kod i jak rozwiązuje problem. Pomoże to pytającemu i przyszłym czytelnikom, jeśli możesz dodać więcej informacji w swoim poście. Zobacz Wyjaśnianie odpowiedzi opartych na kodzie
Calos
1
Możesz to zrobić w ten sposób:
defpowerset(x):
m=[]
ifnot x:
m.append(x)
else:
A = x[0]
B = x[1:]
for z in powerset(B):
m.append(z)
r = [A] + z
m.append(r)
return m
print(powerset([1, 2, 3, 4]))
Mogę zasugerować, aby publikując rozwiązanie kodu, bądź na tyle uprzejmy, aby szczegółowo wyjaśnić, co robi kod i dlaczego używasz tej lub innej metody do rozwiązania problemu. Nowi programiści nie powinni tylko patrzeć na blok kodu i kopiować / wklejać go, nie wiedząc dokładnie, co robi kod i dlaczego. Dziękujemy i witamy w Stackoverflow.
Yokai,
Naprawdę imponująca i rekurencyjna odpowiedź.
Jan
1
Wiem, że to za późno
Istnieje już wiele innych rozwiązań, ale nadal ...
defpower_set(lst):
pw_set = [[]]
for i inrange(0,len(lst)):
for j inrange(0,len(pw_set)):
ele = pw_set[j].copy()
ele = ele + [lst[i]]
pw_set = pw_set + [ele]
return pw_set
Jest to szalone, ponieważ żadna z tych odpowiedzi nie zapewnia zwrotu rzeczywistego zestawu Pythona. Oto niechlujna implementacja, która zapewni zestaw mocy, który w rzeczywistości jest Pythonem set.
Dobra uwaga, ale OP chce mieć listę zestawów jako wyjście, więc (w Pythonie 3) możesz to zrobić [*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))]; funkcja arg mapmoże być, frozensetjeśli wolisz.
PM 2Ring
0
Oto moja szybka implementacja wykorzystująca kombinacje, ale używająca tylko wbudowanych.
defpowerSet(array):
length = str(len(array))
formatter = '{:0' + length + 'b}'
combinations = []
for i in xrange(2**int(length)):
combinations.append(formatter.format(i))
sets = set()
currentSet = []
for combo in combinations:
for i,val inenumerate(combo):
if val=='1':
currentSet.append(array[i])
sets.add(tuple(sorted(currentSet)))
currentSet = []
return sets
n = int(input())
l = [i for i inrange (1, n + 1)]
for number inrange(2 ** n) :
binary = bin(number)[: 1 : -1]
subset = [l[i] for i inrange(len(binary)) if binary[i] == "1"]
print(set(sorted(subset)) if number > 0else"{}")
Odmianą tego pytania jest ćwiczenie, które widzę w książce „Discovering Computer Science: Interdisciplinary Problems, Principles, and Python Programming. Edycja 2015”. W tym ćwiczeniu 10.2.11 dane wejściowe są po prostu liczbą całkowitą, a wyjściem powinny być zestawy mocy. Oto moje rozwiązanie rekurencyjne (nie używające niczego innego oprócz podstawowego Pythona3)
defpowerSetR(n):assert n >= 0if n == 0:
return [[]]
else:
input_set = list(range(1, n+1)) # [1,2,...n]
main_subset = [ ]
for small_subset in powerSetR(n-1):
main_subset += [small_subset]
main_subset += [ [input_set[-1]] + small_subset]
return main_subset
superset = powerSetR(4)
print(superset)
print("Number of sublists:", len(superset))
Nie spotkałem tej more_itertools.powersetfunkcji i poleciłbym jej użycie. Nie polecam również używania domyślnej kolejności wyjścia z itertools.combinations, często zamiast tego chcesz zminimalizować odległość między pozycjami i posortować podzbiory elementów z mniejszą odległością między nimi nad / przed elementami z większą odległością między nimi.
Zwróć uwagę, że rtutaj pasuje do standardowej notacji dla dolnej części współczynnika dwumianowego , sjest zwykle określany tak, jak nw tekstach matematycznych i kalkulatorach („n Wybierz r”)
defpowerset(iterable):"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 inrange(len(s)+1))
Inne przykłady tutaj podają potęgę of [1,2,3,4]w taki sposób, że dwie krotki są wymienione w porządku leksykograficznym (kiedy wypisujemy liczby jako liczby całkowite). Jeśli napiszę odległość między liczbami obok niej (tj. Różnicę), to pokazuje mój punkt widzenia:
12 ⇒ 113 ⇒ 214 ⇒ 323 ⇒ 124 ⇒ 234 ⇒ 1
Prawidłowa kolejność podzbiorów powinna być taka, która jako pierwsza „wyczerpuje” minimalną odległość, na przykład:
12 ⇒ 123 ⇒ 134 ⇒ 113 ⇒ 224 ⇒ 214 ⇒ 3
Używanie tutaj liczb sprawia, że ta kolejność wygląda ["a","b","c","d"]`` źle '', ale rozważ na przykład litery, które są bardziej zrozumiałe, dlaczego może to być przydatne do uzyskania zestawu uprawnień w tej kolejności:
ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3
Efekt ten jest bardziej wyraźny w przypadku większej liczby elementów i dla moich celów stanowi różnicę między zdolnością do opisania zakresów indeksów zestawu potęgi.
(Na kodach Graya jest dużo napisanych itp. Odnośnie kolejności wyjściowej algorytmów w kombinatoryce, nie uważam tego za problem uboczny).
Właściwie właśnie napisałem dość zaangażowany program, który używał tego szybkiego kodu partycji całkowitoliczbowej do wyświetlania wartości we właściwej kolejności, ale potem odkryłem more_itertools.powerseti dla większości zastosowań prawdopodobnie dobrze jest po prostu użyć tej funkcji w następujący sposób:
from more_itertools import powerset
from numpy import ediff1d
defps_sorter(tup):
l = len(tup)
d = ediff1d(tup).tolist()
return l, d
ps = powerset([1,2,3,4])
ps = sorted(ps, key=ps_sorter)
for x in ps:
print(x)
Pisałem trochę bardziej zaangażować się kod, który będzie drukować PowerSet ładnie (patrz repo na dość funkcje drukowania nie Zamieściliśmy tutaj: print_partitions, print_partitions_by_length, i pprint_tuple).
To wszystko jest dość proste, ale nadal może być przydatne, jeśli potrzebujesz kodu, który pozwoli ci uzyskać bezpośredni dostęp do różnych poziomów zestawu mocy:
from itertools import permutations as permute
from numpy import cumsum
# http://jeromekelleher.net/generating-integer-partitions.html# via# /programming/10035752/elegant-python-code-for-integer-partitioning#comment25080713_10036764defasc_int_partitions(n):
a = [0for i inrange(n + 1)]
k = 1
y = n - 1while k != 0:
x = a[k - 1] + 1
k -= 1while2 * x <= y:
a[k] = x
y -= x
k += 1
l = k + 1while x <= y:
a[k] = x
a[l] = y
yieldtuple(a[:k + 2])
x += 1
y -= 1
a[k] = x + y
y = x + y - 1yieldtuple(a[:k + 1])
# https://stackoverflow.com/a/6285330/2668831defuniquely_permute(iterable, enforce_sort=False, r=None):
previous = tuple()
if enforce_sort: # potential waste of effort (default: False)
iterable = sorted(iterable)
for p in permute(iterable, r):
if p > previous:
previous = p
yield p
defsum_min(p):returnsum(p), min(p)
defpartitions_by_length(max_n, sorting=True, permuting=False):
partition_dict = {0: ()}
for n inrange(1,max_n+1):
partition_dict.setdefault(n, [])
partitions = list(asc_int_partitions(n))
for p in partitions:
if permuting:
perms = uniquely_permute(p)
for perm in perms:
partition_dict.get(len(p)).append(perm)
else:
partition_dict.get(len(p)).append(p)
ifnot sorting:
return partition_dict
for k in partition_dict:
partition_dict.update({k: sorted(partition_dict.get(k), key=sum_min)})
return partition_dict
defprint_partitions_by_length(max_n, sorting=True, permuting=True):
partition_dict = partitions_by_length(max_n, sorting=sorting, permuting=permuting)
for k in partition_dict:
if k == 0:
print(tuple(partition_dict.get(k)), end="")
for p in partition_dict.get(k):
print(pprint_tuple(p), end=" ")
print()
returndefgenerate_powerset(items, subset_handler=tuple, verbose=False):"""
Generate the powerset of an iterable `items`.
Handling of the elements of the iterable is by whichever function is passed as
`subset_handler`, which must be able to handle the `None` value for the
empty set. The function `string_handler` will join the elements of the subset
with the empty string (useful when `items` is an iterable of `str` variables).
"""
ps = {0: [subset_handler()]}
n = len(items)
p_dict = partitions_by_length(n-1, sorting=True, permuting=True)
for p_len, parts in p_dict.items():
ps.setdefault(p_len, [])
if p_len == 0:
# singletonsfor offset inrange(n):
subset = subset_handler([items[offset]])
if verbose:
if offset > 0:
print(end=" ")
if offset == n - 1:
print(subset, end="\n")
else:
print(subset, end=",")
ps.get(p_len).append(subset)
for pcount, partition inenumerate(parts):
distance = sum(partition)
indices = (cumsum(partition)).tolist()
for offset inrange(n - distance):
subset = subset_handler([items[offset]] + [items[offset:][i] for i in indices])
if verbose:
if offset > 0:
print(end=" ")
if offset == n - distance - 1:
print(subset, end="\n")
else:
print(subset, end=",")
ps.get(p_len).append(subset)
if verbose and p_len < n-1:
print()
return ps
Jako przykład napisałem program demonstracyjny CLI, który przyjmuje ciąg znaków jako argument wiersza poleceń:
python string_powerset.py abcdef
⇣
a, b, c, d, e, f
ab, bc, cd, de, ef
ac, bd, ce, df
ad, be, cf
ae, bf
af
abc, bcd, cde, defabd, bce, cdfacd, bde, cefabe, bcfade, beface, bdfabfaefacfadfabcd, bcde, cdefabce, bcdfabde, bcefacde, bdefabcfabefadefabdfacdfacefabcde, bcdefabcdfabcefabdefacdefabcdef
Oto moje rozwiązania, jest podobne (koncepcyjnie) do rozwiązania lmiguelvargasf.
Pozwólcie, że powiem, że - [przedmiot matematyczny] z definicji powerset zawiera pusty zestaw - [osobisty gust], a także, że nie lubię używać frozensetu.
Tak więc wejście jest listą, a wyjście - listą list. Funkcja mogłaby się zamknąć wcześniej, ale podoba mi się, że element potęgi jest uporządkowany leksykograficznie , czyli zasadniczo ładnie.
defpower_set(L):"""
L is a list.
The function returns the power set, but as a list of lists.
"""
cardinality=len(L)
n=2 ** cardinality
powerset = []
for i inrange(n):
a=bin(i)[2:]
subset=[]
for j inrange(len(a)):
if a[-j-1]=='1':
subset.append(L[j])
powerset.append(subset)
#the function could stop here closing with#return powerset
powerset_orderred=[]
for k inrange(cardinality+1):
for w in powerset:
iflen(w)==k:
powerset_orderred.append(w)
return powerset_orderred
Chociaż ten kod może odpowiedzieć na pytanie, zapewnia dodatkowy kontekst dotyczący tego, dlaczego i / lub jak ten kod odpowiada, poprawia jego długoterminową wartość. Rozważ przeczytanie Jak odpowiedzieć i zmodyfikuj swoją odpowiedź, aby ją poprawić.
blurfus
2
@Blurfus jest zawsze dobrą praktyką, ale jest szczególnie ważne, gdy odpowiadasz na pytanie sprzed dziesięciu lat z 28 innymi odpowiedziami. Dlaczego jest to poprawa w stosunku do zaakceptowanej odpowiedzi? Co wnosi ta odpowiedź, czego nie oferują inne odpowiedzi?
Odpowiedzi:
Python
itertools
strona ma dokładnie topowerset
przepis na to:from itertools import chain, combinations def powerset(iterable): "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))
Wynik:
>>> list(powerset("abcd")) [(), ('a',), ('b',), ('c',), ('d',), ('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd'), ('a', 'b', 'c', 'd')]
Jeśli nie podoba ci się ta pusta krotka na początku, możesz po prostu zmienić
range
instrukcję na,range(1, len(s)+1)
aby uniknąć kombinacji o długości 0.źródło
s = list(iterable)
potrzebny?__len__
zaimplementowane; wypróbujpowerset((n for n in range(3)))
bez zawijania listy.powerset(range(3))
działałby dobrze nawet bezs = list(iterable)
.Tutaj jest więcej kodu dla PowerSet. To jest napisane od podstaw:
>>> def powerset(s): ... x = len(s) ... for i in range(1 << x): ... print [s[j] for j in range(x) if (i & (1 << j))] ... >>> powerset([4,5,6]) [] [4] [5] [4, 5] [6] [4, 6] [5, 6] [4, 5, 6]
Komentarz Marka Rushakoffa ma tutaj zastosowanie: „Jeśli nie podoba ci się ta pusta krotka na początku, on.” Możesz po prostu zmienić instrukcję zakresu na zakres (1, len (s) +1), aby uniknąć kombinacji o długości 0 ”, z wyjątkiem mojego przypadku, gdy zmienisz
for i in range(1 << x)
nafor i in range(1, 1 << x)
.Wracając do tego lata później, teraz napisałbym to tak:
def powerset(s): x = len(s) masks = [1 << i for i in range(x)] for i in range(1 << x): yield [ss for mask, ss in zip(masks, s) if i & mask]
Następnie kod testowy wyglądałby następująco:
print(list(powerset([4, 5, 6])))
Używanie
yield
oznacza, że nie musisz obliczać wszystkich wyników w pojedynczym fragmencie pamięci. Zakłada się, że wstępne obliczenie masek poza główną pętlą jest opłacalną optymalizacją.źródło
Jeśli szukasz szybkiej odpowiedzi, właśnie wyszukałem w google „Python power set” i wymyśliłem to: Python Power Set Generator
Oto kopia i wklejanie z kodu na tej stronie:
def powerset(seq): """ Returns all the subsets of this set. This is a generator. """ if len(seq) <= 1: yield seq yield [] else: for item in powerset(seq[1:]): yield [seq[0]]+item yield item
Można tego użyć w następujący sposób:
l = [1, 2, 3, 4] r = [x for x in powerset(l)]
Teraz r to lista wszystkich potrzebnych elementów, którą można posortować i wydrukować:
r.sort() print r [[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4], [1, 3], [1, 3, 4], [1, 4], [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]
źródło
[[][]]
, aby naprawić to tylko oddzielenie przypadków do sprawdzania długościif len(seq) == 0: yield [] elif len(seq) == 1: yield seq yield []
def powerset(lst): return reduce(lambda result, x: result + [subset + [x] for subset in result], lst, [[]])
źródło
Istnieje udoskonalenie zestawu uprawnień:
def powerset(seq): """ Returns all the subsets of this set. This is a generator. """ if len(seq) <= 0: yield [] else: for item in powerset(seq[1:]): yield [seq[0]]+item yield item
źródło
TL; DR (przejdź bezpośrednio do Simplification)
Wiem, że wcześniej dodałem odpowiedź, ale bardzo podoba mi się moja nowa implementacja. Biorę zestaw jako dane wejściowe, ale w rzeczywistości może to być dowolny iterowalny zestaw, a ja zwracam zestaw zestawów, który jest zestawem mocy wejścia. Podoba mi się to podejście, ponieważ jest bardziej zgodne z matematyczną definicją zbioru potęg ( zbiór wszystkich podzbiorów ).
def power_set(A): """A is an iterable (list, tuple, set, str, etc) returns a set which is the power set of A.""" length = len(A) l = [a for a in A] ps = set() for i in range(2 ** length): selector = f'{i:0{length}b}' subset = {l[j] for j, bit in enumerate(selector) if bit == '1'} ps.add(frozenset(subset)) return ps
Jeśli chcesz dokładnie uzyskać wyniki, które zamieściłeś w swojej odpowiedzi, użyj tego:
>>> [set(s) for s in power_set({1, 2, 3, 4})] [{3, 4}, {2}, {1, 4}, {2, 3, 4}, {2, 3}, {1, 2, 4}, {1, 2}, {1, 2, 3}, {3}, {2, 4}, {1}, {1, 2, 3, 4}, set(), {1, 3}, {1, 3, 4}, {4}]
Wyjaśnienie
Wiadomo, że liczba elementów zestawu mocy jest
2 ** len(A)
taka, że wfor
pętli można było wyraźnie zobaczyć .Muszę przekonwertować dane wejściowe (najlepiej zestaw) na listę, ponieważ przez zestaw jest to struktura danych unikalnych nieuporządkowanych elementów, a kolejność będzie kluczowa do wygenerowania podzbiorów.
selector
jest kluczem do tego algorytmu. Zwróć uwagę, żeselector
ma taką samą długość jak zestaw wejściowy i aby było to możliwe, używa łańcucha f z dopełnieniem. Zasadniczo pozwala mi to wybrać elementy, które zostaną dodane do każdego podzbioru podczas każdej iteracji. Powiedzmy, że zestaw wejściowy ma 3 elementy{0, 1, 2}
, więc selector przyjmie wartości od 0 do 7 (włącznie), które w systemie binarnym to:000 # 0 001 # 1 010 # 2 011 # 3 100 # 4 101 # 5 110 # 6 111 # 7
Tak więc każdy bit może służyć jako wskaźnik, czy należy dodać element oryginalnego zestawu, czy nie. Spójrz na liczby binarne i pomyśl o każdej liczbie jako o elemencie super zestawu, co
1
oznacza, żej
należy dodać element pod indeksem , a0
to oznacza, że ten element nie powinien być dodawany.Używam rozumienia zestawu do generowania podzbioru w każdej iteracji i konwertuję ten podzbiór na zestaw,
frozenset
aby móc go dodać dops
(zestaw potęgowy). W przeciwnym razie nie będę mógł go dodać, ponieważ zestaw w Pythonie składa się tylko z niezmiennych obiektów.Uproszczenie
Możesz uprościć kod, używając niektórych wyrażeń w Pythonie, dzięki czemu możesz pozbyć się pętli for. Możesz również użyć,
zip
aby uniknąć używaniaj
indeksu, a kod zakończy się następująco:def power_set(A): length = len(A) return { frozenset({e for e, b in zip(A, f'{i:{length}b}') if b == '1'}) for i in range(2 ** length) }
Otóż to. W tym algorytmie podoba mi się to, że jest jaśniejszy i bardziej intuicyjny niż inne, ponieważ wygląda całkiem magicznie,
itertools
mimo że działa zgodnie z oczekiwaniami.źródło
Uważam, że następujący algorytm jest bardzo przejrzysty i prosty:
def get_powerset(some_list): """Returns all subsets of size 0 - len(some_list) for some_list""" if len(some_list) == 0: return [[]] subsets = [] first_element = some_list[0] remaining_list = some_list[1:] # Strategy: get all the subsets of remaining_list. For each # of those subsets, a full subset list will contain both # the original subset as well as a version of the subset # that contains first_element for partial_subset in get_powerset(remaining_list): subsets.append(partial_subset) subsets.append(partial_subset[:] + [first_element]) return subsets
Innym sposobem generowania zestawu mocy jest generowanie wszystkich liczb binarnych, które mają
n
bity. Jako potęgę ustawiono ilość liczb zn
cyframi2 ^ n
. Zasada tego algorytmu polega na tym, że element może być obecny lub nie w podzbiorze, ponieważ cyfra binarna może mieć wartość jeden lub zero, ale nie obie.def power_set(items): N = len(items) # enumerate the 2 ** N possible combinations for i in range(2 ** N): combo = [] for j in range(N): # test bit jth of integer i if (i >> j) % 2 == 1: combo.append(items[j]) yield combo
Znalazłem oba algorytmy, kiedy brałem MITx: 6.00.2x Wprowadzenie do myślenia obliczeniowego i nauki o danych, i uważam, że jest to jeden z najłatwiejszych do zrozumienia algorytmów, jakie widziałem.
źródło
def get_power_set(s): power_set=[[]] for elem in s: # iterate over the sub sets so far for sub_set in power_set: # add a new subset consisting of the subset at hand added elem power_set=power_set+[list(sub_set)+[elem]] return power_set
Na przykład:
get_power_set([1,2,3])
wydajność
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
źródło
power_set
) w pętli, którą zarządza, jest bardzo wątpliwą praktyką. Na przykład, załóżmy, że napisał to zamiast proponowanej zmiennej modyfikacji kodu:power_set += [list(sub_set)+[elem]]
. Wtedy pętla się nie kończy.Chciałem tylko zapewnić najbardziej zrozumiałe rozwiązanie, wersję anty-code-golf.
from itertools import combinations l = ["x", "y", "z", ] def powerset(items): combo = [] for r in range(len(items) + 1): #use a list to coerce a actual list from the combinations generator combo.append(list(combinations(items,r))) return combo l_powerset = powerset(l) for i, item in enumerate(l_powerset): print "All sets of length ", i print item
Wyniki
Wszystkie zestawy o długości 0
[()]
Wszystkie zestawy o długości 1
[('x',), ('y',), ('z',)]
Wszystkie zestawy o długości 2
[('x', 'y'), ('x', 'z'), ('y', 'z')]
Wszystkie zestawy o długości 3
[('x', 'y', 'z')]
Aby uzyskać więcej informacji, zobacz dokumentację itertools , a także wpis Wikipedii dotyczący zestawów mocy
źródło
Można to zrobić bardzo naturalnie za pomocą
itertools.product
:import itertools def powerset(l): for sl in itertools.product(*[[[], [i]] for i in l]): yield {j for i in sl for j in i}
źródło
Tylko szybkie odświeżenie zestawu mocy!
Oto inny sposób na znalezienie zestawu mocy:
def power_set(input): # returns a list of all subsets of the list a if (len(input) == 0): return [[]] else: main_subset = [ ] for small_subset in power_set(input[1:]): main_subset += [small_subset] main_subset += [[input[0]] + small_subset] return main_subset print(power_set([0,1,2,3]))
pełny kredyt dla źródła
źródło
Prostym sposobem byłoby wykorzystanie wewnętrznej reprezentacji liczb całkowitych w ramach arytmetyki dopełnienia do 2.
Binarna reprezentacja liczb całkowitych to {000, 001, 010, 011, 100, 101, 110, 111} dla liczb z przedziału od 0 do 7. Dla wartości licznika w postaci liczby całkowitej, biorąc pod uwagę 1 jako włączenie odpowiedniego elementu do zbioru i „0” jako wykluczenie możemy wygenerować podzbiory na podstawie sekwencji zliczania. Liczby należy generować od
0
do,pow(2,n) -1
gdzie n jest długością tablicy, tj. Liczbą bitów w reprezentacji binarnej.Bazującą na niej prostą funkcję generatora podzbiorów można zapisać w następujący sposób. Zasadniczo polega
def subsets(array): if not array: return else: length = len(array) for max_int in range(0x1 << length): subset = [] for i in range(length): if max_int & (0x1 << i): subset.append(array[i]) yield subset
a następnie może być używany jako
def get_subsets(array): powerset = [] for i in subsets(array): powerser.append(i) return powerset
Testowanie
Dodanie następującego w pliku lokalnym
if __name__ == '__main__': sample = ['b', 'd', 'f'] for i in range(len(sample)): print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])
daje następujący wynik
Subsets for ['b', 'd', 'f'] are [[], ['b'], ['d'], ['b', 'd'], ['f'], ['b', 'f'], ['d', 'f'], ['b', 'd', 'f']] Subsets for ['d', 'f'] are [[], ['d'], ['f'], ['d', 'f']] Subsets for ['f'] are [[], ['f']]
źródło
Z pustym zestawem, który jest częścią wszystkich podzbiorów, możesz użyć:
def subsets(iterable): for n in range(len(iterable) + 1): yield from combinations(iterable, n)
źródło
Prawie wszystkie te odpowiedzi używają
list
raczej niżset
, co wydawało mi się trochę oszustwem. Tak więc z ciekawości spróbowałem zrobić naprawdę prostą wersjęset
i podsumować ją dla innych ludzi „nowych w Pythonie”.Zauważyłem, że jest kilka dziwactw związanych z implementacją zestawu w Pythonie . Głównym zaskoczeniem dla mnie była obsługa pustych zestawów. Jest to w przeciwieństwie do implementacji Set Rubiego , w której mogę po prostu zrobić
Set[Set[]]
i uzyskaćSet
pusty element zawierającySet
, więc początkowo wydawało mi się to trochę zagmatwane.Aby przejrzeć, robiąc
powerset
zset
s, napotkałem dwa problemy:set()
przyjmuje iterowalne, więcset(set())
zwróci,set()
ponieważ pusty zestaw iterowalny jest pusty (chyba :))set({set()})
iset.add(set)
nie zadziała, ponieważset()
nie można go hashowaćAby rozwiązać oba problemy, wykorzystałem
frozenset()
, co oznacza, że nie do końca dostaję to, czego chcę (typ jest dosłownieset
), ale korzystam z ogólnegoset
interfejsu.def powerset(original_set): # below gives us a set with one empty set in it ps = set({frozenset()}) for member in original_set: subset = set() for m in ps: # to be added into subset, needs to be # frozenset.union(set) so it's hashable subset.add(m.union(set([member])) ps = ps.union(subset) return ps
Poniżej otrzymujemy 2² (16)
frozenset
s poprawnie jako wyjście:In [1]: powerset(set([1,2,3,4])) Out[2]: {frozenset(), frozenset({3, 4}), frozenset({2}), frozenset({1, 4}), frozenset({3}), frozenset({2, 3}), frozenset({2, 3, 4}), frozenset({1, 2}), frozenset({2, 4}), frozenset({1}), frozenset({1, 2, 4}), frozenset({1, 3}), frozenset({1, 2, 3}), frozenset({4}), frozenset({1, 3, 4}), frozenset({1, 2, 3, 4})}
Ponieważ w Pythonie nie ma sposobu, aby mieć s
set
ofset
sw, jeśli chcesz zamienić jefrozenset
naset
s, będziesz musiał odwzorować je z powrotem na alist
(list(map(set, powerset(set([1,2,3,4]))))
) lub zmodyfikować powyższe.źródło
Być może pytanie się starzeje, ale mam nadzieję, że mój kod komuś pomoże.
def powSet(set): if len(set) == 0: return [[]] return addtoAll(set[0],powSet(set[1:])) + powSet(set[1:]) def addtoAll(e, set): for c in set: c.append(e) return set
źródło
Użyj funkcji
powerset()
z pakietumore_itertools
.>>> list(powerset([1, 2, 3])) [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
Jeśli chcesz zestawy, użyj:
list(map(set, powerset(iterable)))
źródło
Pobieranie wszystkich podzbiorów z rekurencją. Szalona jedna linijka
from typing import List def subsets(xs: list) -> List[list]: return subsets(xs[1:]) + [x + [xs[0]] for x in subsets(xs[1:])] if xs else [[]]
Oparty na rozwiązaniu Haskell
subsets :: [a] -> [[a]] subsets [] = [[]] subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs
źródło
NameError: name 'List' is not defined
List
importdef findsubsets(s, n): return list(itertools.combinations(s, n)) def allsubsets(s) : a = [] for x in range(1,len(s)+1): a.append(map(set,findsubsets(s,x))) return a
źródło
Możesz to zrobić w ten sposób:
def powerset(x): m=[] if not x: m.append(x) else: A = x[0] B = x[1:] for z in powerset(B): m.append(z) r = [A] + z m.append(r) return m print(powerset([1, 2, 3, 4]))
Wynik:
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3], [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4]]
źródło
Wiem, że to za późno
Istnieje już wiele innych rozwiązań, ale nadal ...
def power_set(lst): pw_set = [[]] for i in range(0,len(lst)): for j in range(0,len(pw_set)): ele = pw_set[j].copy() ele = ele + [lst[i]] pw_set = pw_set + [ele] return pw_set
źródło
Jest to szalone, ponieważ żadna z tych odpowiedzi nie zapewnia zwrotu rzeczywistego zestawu Pythona. Oto niechlujna implementacja, która zapewni zestaw mocy, który w rzeczywistości jest Pythonem
set
.test_set = set(['yo', 'whatup', 'money']) def powerset( base_set ): """ modified from pydoc's itertools recipe shown above""" from itertools import chain, combinations base_list = list( base_set ) combo_list = [ combinations(base_list, r) for r in range(len(base_set)+1) ] powerset = set([]) for ll in combo_list: list_of_frozensets = list( map( frozenset, map( list, ll ) ) ) set_of_frozensets = set( list_of_frozensets ) powerset = powerset.union( set_of_frozensets ) return powerset print powerset( test_set ) # >>> set([ frozenset(['money','whatup']), frozenset(['money','whatup','yo']), # frozenset(['whatup']), frozenset(['whatup','yo']), frozenset(['yo']), # frozenset(['money','yo']), frozenset(['money']), frozenset([]) ])
Chciałbym jednak zobaczyć lepszą implementację.
źródło
[*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))]
; funkcja argmap
może być,frozenset
jeśli wolisz.Oto moja szybka implementacja wykorzystująca kombinacje, ale używająca tylko wbudowanych.
def powerSet(array): length = str(len(array)) formatter = '{:0' + length + 'b}' combinations = [] for i in xrange(2**int(length)): combinations.append(formatter.format(i)) sets = set() currentSet = [] for combo in combinations: for i,val in enumerate(combo): if val=='1': currentSet.append(array[i]) sets.add(tuple(sorted(currentSet))) currentSet = [] return sets
źródło
Wszystkie podzbiory w zakresie n jako zestaw:
n = int(input()) l = [i for i in range (1, n + 1)] for number in range(2 ** n) : binary = bin(number)[: 1 : -1] subset = [l[i] for i in range(len(binary)) if binary[i] == "1"] print(set(sorted(subset)) if number > 0 else "{}")
źródło
import math def printPowerSet(set,set_size): pow_set_size =int(math.pow(2, set_size)) for counter in range(pow_set_size): for j in range(set_size): if((counter & (1 << j)) > 0): print(set[j], end = "") print("") set = ['a', 'b', 'c'] printPowerSet(set,3)
źródło
Odmianą tego pytania jest ćwiczenie, które widzę w książce „Discovering Computer Science: Interdisciplinary Problems, Principles, and Python Programming. Edycja 2015”. W tym ćwiczeniu 10.2.11 dane wejściowe są po prostu liczbą całkowitą, a wyjściem powinny być zestawy mocy. Oto moje rozwiązanie rekurencyjne (nie używające niczego innego oprócz podstawowego Pythona3)
def powerSetR(n): assert n >= 0 if n == 0: return [[]] else: input_set = list(range(1, n+1)) # [1,2,...n] main_subset = [ ] for small_subset in powerSetR(n-1): main_subset += [small_subset] main_subset += [ [input_set[-1]] + small_subset] return main_subset superset = powerSetR(4) print(superset) print("Number of sublists:", len(superset))
A wynik jest
[[], [4], [3], [4, 3], [2], [4, 2], [3, 2], [4, 3, 2], [1], [4, 1 ], [3, 1], [4, 3, 1], [2, 1], [4, 2, 1], [3, 2, 1], [4, 3, 2, 1]] Liczba podlisty: 16
źródło
Nie spotkałem tej
more_itertools.powerset
funkcji i poleciłbym jej użycie. Nie polecam również używania domyślnej kolejności wyjścia zitertools.combinations
, często zamiast tego chcesz zminimalizować odległość między pozycjami i posortować podzbiory elementów z mniejszą odległością między nimi nad / przed elementami z większą odległością między nimi.Strona z
itertools
przepisami pokazuje, że używachain.from_iterable
r
tutaj pasuje do standardowej notacji dla dolnej części współczynnika dwumianowego ,s
jest zwykle określany tak, jakn
w tekstach matematycznych i kalkulatorach („n Wybierz r”)def powerset(iterable): "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))
Inne przykłady tutaj podają potęgę of
[1,2,3,4]
w taki sposób, że dwie krotki są wymienione w porządku leksykograficznym (kiedy wypisujemy liczby jako liczby całkowite). Jeśli napiszę odległość między liczbami obok niej (tj. Różnicę), to pokazuje mój punkt widzenia:12 ⇒ 1 13 ⇒ 2 14 ⇒ 3 23 ⇒ 1 24 ⇒ 2 34 ⇒ 1
Prawidłowa kolejność podzbiorów powinna być taka, która jako pierwsza „wyczerpuje” minimalną odległość, na przykład:
12 ⇒ 1 23 ⇒ 1 34 ⇒ 1 13 ⇒ 2 24 ⇒ 2 14 ⇒ 3
Używanie tutaj liczb sprawia, że ta kolejność wygląda
["a","b","c","d"]
`` źle '', ale rozważ na przykład litery, które są bardziej zrozumiałe, dlaczego może to być przydatne do uzyskania zestawu uprawnień w tej kolejności:ab ⇒ 1 bc ⇒ 1 cd ⇒ 1 ac ⇒ 2 bd ⇒ 2 ad ⇒ 3
Efekt ten jest bardziej wyraźny w przypadku większej liczby elementów i dla moich celów stanowi różnicę między zdolnością do opisania zakresów indeksów zestawu potęgi.
(Na kodach Graya jest dużo napisanych itp. Odnośnie kolejności wyjściowej algorytmów w kombinatoryce, nie uważam tego za problem uboczny).
Właściwie właśnie napisałem dość zaangażowany program, który używał tego szybkiego kodu partycji całkowitoliczbowej do wyświetlania wartości we właściwej kolejności, ale potem odkryłem
more_itertools.powerset
i dla większości zastosowań prawdopodobnie dobrze jest po prostu użyć tej funkcji w następujący sposób:from more_itertools import powerset from numpy import ediff1d def ps_sorter(tup): l = len(tup) d = ediff1d(tup).tolist() return l, d ps = powerset([1,2,3,4]) ps = sorted(ps, key=ps_sorter) for x in ps: print(x)
⇣
() (1,) (2,) (3,) (4,) (1, 2) (2, 3) (3, 4) (1, 3) (2, 4) (1, 4) (1, 2, 3) (2, 3, 4) (1, 2, 4) (1, 3, 4) (1, 2, 3, 4)
Pisałem trochę bardziej zaangażować się kod, który będzie drukować PowerSet ładnie (patrz repo na dość funkcje drukowania nie Zamieściliśmy tutaj:
print_partitions
,print_partitions_by_length
, ipprint_tuple
).pset_partitions.py
To wszystko jest dość proste, ale nadal może być przydatne, jeśli potrzebujesz kodu, który pozwoli ci uzyskać bezpośredni dostęp do różnych poziomów zestawu mocy:
from itertools import permutations as permute from numpy import cumsum # http://jeromekelleher.net/generating-integer-partitions.html # via # /programming/10035752/elegant-python-code-for-integer-partitioning#comment25080713_10036764 def asc_int_partitions(n): a = [0 for i in range(n + 1)] k = 1 y = n - 1 while k != 0: x = a[k - 1] + 1 k -= 1 while 2 * x <= y: a[k] = x y -= x k += 1 l = k + 1 while x <= y: a[k] = x a[l] = y yield tuple(a[:k + 2]) x += 1 y -= 1 a[k] = x + y y = x + y - 1 yield tuple(a[:k + 1]) # https://stackoverflow.com/a/6285330/2668831 def uniquely_permute(iterable, enforce_sort=False, r=None): previous = tuple() if enforce_sort: # potential waste of effort (default: False) iterable = sorted(iterable) for p in permute(iterable, r): if p > previous: previous = p yield p def sum_min(p): return sum(p), min(p) def partitions_by_length(max_n, sorting=True, permuting=False): partition_dict = {0: ()} for n in range(1,max_n+1): partition_dict.setdefault(n, []) partitions = list(asc_int_partitions(n)) for p in partitions: if permuting: perms = uniquely_permute(p) for perm in perms: partition_dict.get(len(p)).append(perm) else: partition_dict.get(len(p)).append(p) if not sorting: return partition_dict for k in partition_dict: partition_dict.update({k: sorted(partition_dict.get(k), key=sum_min)}) return partition_dict def print_partitions_by_length(max_n, sorting=True, permuting=True): partition_dict = partitions_by_length(max_n, sorting=sorting, permuting=permuting) for k in partition_dict: if k == 0: print(tuple(partition_dict.get(k)), end="") for p in partition_dict.get(k): print(pprint_tuple(p), end=" ") print() return def generate_powerset(items, subset_handler=tuple, verbose=False): """ Generate the powerset of an iterable `items`. Handling of the elements of the iterable is by whichever function is passed as `subset_handler`, which must be able to handle the `None` value for the empty set. The function `string_handler` will join the elements of the subset with the empty string (useful when `items` is an iterable of `str` variables). """ ps = {0: [subset_handler()]} n = len(items) p_dict = partitions_by_length(n-1, sorting=True, permuting=True) for p_len, parts in p_dict.items(): ps.setdefault(p_len, []) if p_len == 0: # singletons for offset in range(n): subset = subset_handler([items[offset]]) if verbose: if offset > 0: print(end=" ") if offset == n - 1: print(subset, end="\n") else: print(subset, end=",") ps.get(p_len).append(subset) for pcount, partition in enumerate(parts): distance = sum(partition) indices = (cumsum(partition)).tolist() for offset in range(n - distance): subset = subset_handler([items[offset]] + [items[offset:][i] for i in indices]) if verbose: if offset > 0: print(end=" ") if offset == n - distance - 1: print(subset, end="\n") else: print(subset, end=",") ps.get(p_len).append(subset) if verbose and p_len < n-1: print() return ps
Jako przykład napisałem program demonstracyjny CLI, który przyjmuje ciąg znaków jako argument wiersza poleceń:
⇣
a, b, c, d, e, f ab, bc, cd, de, ef ac, bd, ce, df ad, be, cf ae, bf af abc, bcd, cde, def abd, bce, cdf acd, bde, cef abe, bcf ade, bef ace, bdf abf aef acf adf abcd, bcde, cdef abce, bcdf abde, bcef acde, bdef abcf abef adef abdf acdf acef abcde, bcdef abcdf abcef abdef acdef abcdef
źródło
Jeśli chcesz mieć określoną długość podzbiorów, możesz to zrobić w ten sposób:
from itertools import combinations someSet = {0, 1, 2, 3} ([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])
Bardziej ogólnie, dla podzbiorów o dowolnej długości można modyfikować arugowanie zakresu. Wynik jest
[(), (0,), (1,), (2,), (3,), (0, 1), (0, 2), (0, 3), (1, 2), (1 , 3), (2, 3), (0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3), (0, 1, 2, 3 )]
źródło
Oto moje rozwiązania, jest podobne (koncepcyjnie) do rozwiązania lmiguelvargasf.
Pozwólcie, że powiem, że - [przedmiot matematyczny] z definicji powerset zawiera pusty zestaw - [osobisty gust], a także, że nie lubię używać frozensetu.
Tak więc wejście jest listą, a wyjście - listą list. Funkcja mogłaby się zamknąć wcześniej, ale podoba mi się, że element potęgi jest uporządkowany leksykograficznie , czyli zasadniczo ładnie.
def power_set(L): """ L is a list. The function returns the power set, but as a list of lists. """ cardinality=len(L) n=2 ** cardinality powerset = [] for i in range(n): a=bin(i)[2:] subset=[] for j in range(len(a)): if a[-j-1]=='1': subset.append(L[j]) powerset.append(subset) #the function could stop here closing with #return powerset powerset_orderred=[] for k in range(cardinality+1): for w in powerset: if len(w)==k: powerset_orderred.append(w) return powerset_orderred
źródło
def powerset(some_set): res = [(a,b) for a in some_set for b in some_set] return res
źródło