จะรับเซตย่อยทั้งหมดได้อย่างไร? (powerset)


109

ให้ชุด

{0, 1, 2, 3}

ฉันจะสร้างชุดย่อยได้อย่างไร:

[set(),
 {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}]

แอพพลิเคชั่นของชุดเพาเวอร์คืออะไร?
X10D

คำตอบ:


151

itertoolsหน้า Python มีpowersetสูตรสำหรับสิ่งนี้:

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

เอาท์พุต:

>>> 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')]

หากคุณไม่ชอบทูเปิลว่างในตอนต้นคุณสามารถเปลี่ยนrangeคำสั่งrange(1, len(s)+1)เพื่อหลีกเลี่ยงการรวมกันเป็น 0 ความยาว


1
นี่เป็นคำตอบที่เร็วที่สุดที่ฉันสามารถหาได้เปรียบเทียบโซลูชันอื่น ๆ ในหน้านี้กับโซลูชันนี้โดยใช้โมดูล timeit ของ Python อย่างไรก็ตามในบางกรณีหากคุณต้องการแก้ไขผลลัพธ์ที่ได้ (เช่นการรวมตัวอักษรเพื่อสร้างสตริง) การเขียนสูตรอาหารที่กำหนดเองโดยใช้เครื่องกำเนิดไฟฟ้าและสร้างผลลัพธ์ที่คุณต้องการ (เช่นการรวมสองสตริงเข้าด้วยกัน) อาจเร็วกว่ามาก
Ceasar Bautista

ทำไมถึงs = list(iterable)ต้องการ?
Jack Stevens

@JackStevens เนื่องจาก iterables ไม่สามารถย้อนกลับได้และไม่จำเป็นต้อง__len__ใช้งาน ลองใช้powerset((n for n in range(3)))โดยไม่ต้องตัดรายการ
hoefling

1
สำหรับสตริงขนาดใหญ่สิ่งนี้จะกินหน่วยความจำมาก!
NoobEditor

1
@AlexandreHuat: ช่วงเป็นลำดับที่ขี้เกียจไม่ใช่ตัวทำซ้ำ powerset(range(3))จะทำงานได้ดีแม้จะไม่มี s = list(iterable)
user2357112 รองรับ Monica

54

นี่คือรหัสเพิ่มเติมสำหรับ powerset สิ่งนี้เขียนขึ้นตั้งแต่ต้น:

>>> 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]

ความคิดเห็นของ Mark Rushakoff สามารถใช้ได้ที่นี่: "ถ้าคุณไม่ชอบทูเปิลว่างที่จุดเริ่มต้นให้เปิด" คุณสามารถเปลี่ยนคำสั่ง range เป็น range (1, len (s) +1) เพื่อหลีกเลี่ยงการผสมความยาว 0 " ยกเว้นในกรณีของคุณเปลี่ยนไปfor i in range(1 << x)for i in range(1, 1 << x)


ย้อนกลับไปหลายปีต่อมาตอนนี้ฉันจะเขียนแบบนี้:

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]

จากนั้นรหัสทดสอบจะมีลักษณะดังนี้พูดว่า:

print(list(powerset([4, 5, 6])))

การใช้yieldหมายความว่าคุณไม่จำเป็นต้องคำนวณผลลัพธ์ทั้งหมดในหน่วยความจำชิ้นเดียว การคำนวณมาสก์อย่างแม่นยำนอกลูปหลักถือว่าเป็นการเพิ่มประสิทธิภาพที่คุ้มค่า


6
นี่คือคำตอบที่สร้างสรรค์ อย่างไรก็ตามฉันวัดโดยใช้ timeit เพื่อเปรียบเทียบกับ Mark Rushakoff และสังเกตเห็นว่ามันช้ากว่าอย่างเห็นได้ชัด ในการสร้างชุดพลังงาน 16 รายการ 100 ครั้งการวัดของฉันคือ 0.55 เทียบกับ 15.6
Ceasar Bautista

19

หากคุณกำลังมองหาคำตอบอย่างรวดเร็วฉันเพิ่งค้นหา "python power set" ใน google และได้สิ่งนี้: Python Power Set Generator

นี่คือการคัดลอกวางจากโค้ดในหน้านั้น:

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

สามารถใช้ได้ดังนี้:

 l = [1, 2, 3, 4]
 r = [x for x in powerset(l)]

ตอนนี้ r คือรายการขององค์ประกอบทั้งหมดที่คุณต้องการและสามารถจัดเรียงและพิมพ์ได้:

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]]

1
ในกรณีที่อาร์เรย์ว่างเป็นอินพุตโค้ดด้านบนจะส่งกลับ[[][]]เพื่อแก้ไขว่าเพียงแค่แยกกรณีสำหรับการตรวจสอบความยาวif len(seq) == 0: yield [] elif len(seq) == 1: yield seq yield []
Ayush

3
สำหรับการอ้างอิงฉันวัดสิ่งนี้ (ด้วยการแก้ไขของ Ayush) โดยใช้ timeit และเปรียบเทียบกับสูตร poweret ในคำตอบของ Mark Rushakoff บนเครื่องของฉันในการสร้างชุดพาวเวอร์ของ 16 รายการ 100 ครั้งอัลกอริทึมนี้ใช้เวลา 1.36 วินาทีในขณะที่ Rushakoff ใช้เวลา 0.55
Ceasar Bautista

อะไรคือความซับซ้อนของเวลาสำหรับสิ่งนี้?
CodeQuestor



8

TL; DR (ไปที่ Simplification โดยตรง)

ฉันรู้ว่าฉันได้เพิ่มคำตอบก่อนหน้านี้แล้ว แต่ฉันชอบการใช้งานใหม่ของฉันมาก ฉันกำลังตั้งค่าเป็นอินพุต แต่จริงๆแล้วมันสามารถทำซ้ำได้และฉันกำลังส่งคืนชุดหนึ่งซึ่งเป็นชุดพลังงานของอินพุต ฉันชอบแนวทางนี้เพราะสอดคล้องกับนิยามทางคณิตศาสตร์ของเซตกำลัง ( เซตของเซตย่อยทั้งหมด ) มากกว่า

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

หากคุณต้องการผลลัพธ์ที่คุณโพสต์ไว้ในคำตอบของคุณให้ใช้สิ่งนี้:

>>> [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}]

คำอธิบาย

เป็นที่ทราบกันดีว่าจำนวนองค์ประกอบของชุดพลัง2 ** len(A)นั้นสามารถมองเห็นได้ชัดเจนในforลูป

ฉันจำเป็นต้องแปลงอินพุต (เป็นชุดที่ดีที่สุด) เป็นรายการเพราะโดยชุดเป็นโครงสร้างข้อมูลขององค์ประกอบที่ไม่ได้เรียงลำดับที่ไม่ซ้ำกันและลำดับจะมีความสำคัญอย่างยิ่งในการสร้างชุดย่อย

selectorเป็นกุญแจสำคัญในอัลกอริทึมนี้ โปรดทราบว่าselectorมีความยาวเท่ากับชุดอินพุตและเพื่อให้เป็นไปได้ให้ใช้ f-string ที่มีช่องว่างภายใน โดยพื้นฐานแล้วสิ่งนี้ช่วยให้ฉันสามารถเลือกองค์ประกอบที่จะเพิ่มลงในแต่ละส่วนย่อยระหว่างการทำซ้ำแต่ละครั้ง สมมติว่าชุดอินพุตมี 3 องค์ประกอบ{0, 1, 2}ดังนั้นตัวเลือกจะรับค่าระหว่าง 0 ถึง 7 (รวม) ซึ่งในไบนารีคือ:

000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7

ดังนั้นแต่ละบิตสามารถใช้เป็นตัวบ่งชี้ได้ว่าควรเพิ่มองค์ประกอบของชุดเดิมหรือไม่ ดูเลขฐานสองและคิดว่าตัวเลขแต่ละตัวเป็นองค์ประกอบของชุดซุปเปอร์ซึ่ง1หมายความว่าjควรเพิ่มองค์ประกอบที่ดัชนีและ0หมายความว่าไม่ควรเพิ่มองค์ประกอบนี้

ฉันใช้ความเข้าใจที่ตั้งไว้เพื่อสร้างชุดย่อยในการวนซ้ำแต่ละครั้งและฉันแปลงชุดย่อยนี้เป็นชุดย่อยfrozensetเพื่อที่ฉันจะได้เพิ่มลงในps (ชุดพลังงาน) มิฉะนั้นฉันจะไม่สามารถเพิ่มได้เนื่องจากชุดใน Python ประกอบด้วยวัตถุที่ไม่เปลี่ยนรูปเท่านั้น

การทำให้เข้าใจง่าย

คุณสามารถลดความซับซ้อนของโค้ดโดยใช้ความเข้าใจ python เพื่อกำจัดสิ่งเหล่านั้นสำหรับลูป คุณยังสามารถใช้zipเพื่อหลีกเลี่ยงการใช้jดัชนีและรหัสจะลงเอยดังต่อไปนี้:

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

แค่นั้นแหละ. สิ่งที่ฉันชอบของอัลกอริทึมนี้คือชัดเจนและใช้งานง่ายกว่าแบบอื่นเพราะมันดูน่าอัศจรรย์มากที่ต้องพึ่งพาitertoolsแม้ว่ามันจะทำงานได้ตามที่คาดไว้ก็ตาม


7

ฉันพบว่าอัลกอริทึมต่อไปนี้ชัดเจนและเรียบง่าย:

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

อีกวิธีหนึ่งในการสร้าง powerset คือการสร้างเลขฐานสองทั้งหมดที่มีnบิต ในฐานะที่เป็นอำนาจกำหนดจำนวนของจำนวนที่มีหลักคือn 2 ^ nหลักการของอัลกอริทึมนี้คือองค์ประกอบอาจมีอยู่หรือไม่มีในเซตย่อยเนื่องจากเลขฐานสองอาจเป็นหนึ่งหรือศูนย์ แต่ไม่ใช่ทั้งสองอย่าง

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

ฉันพบอัลกอริทึมทั้งสองเมื่อฉันใช้ MITx: 6.00.2x Introduction to Computational Thinking and Data Science และฉันคิดว่ามันเป็นหนึ่งในอัลกอริทึมที่ง่ายที่สุดในการทำความเข้าใจที่ฉันเคยเห็น


5
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

ตัวอย่างเช่น:

get_power_set([1,2,3])

ผลผลิต

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

2
การแก้ไขตัวแปรลูป ( power_set) ในลูปที่ควบคุมเป็นแนวทางปฏิบัติที่น่าสงสัยมาก power_set += [list(sub_set)+[elem]]ตัวอย่างเช่นสมมติว่าคุณเขียนนี้แทนรหัสตัวแปรการปรับเปลี่ยนที่นำเสนอ: จากนั้นลูปจะไม่สิ้นสุด
hughdbrown

3

ฉันแค่อยากจะมอบโซลูชันที่เข้าใจได้ง่ายที่สุดนั่นคือเวอร์ชันแอนตี้โค้ดกอล์ฟ

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

ผลลัพธ์

ทุกชุดความยาว 0

[()]

ความยาวทุกชุด 1

[('x',), ('y',), ('z',)]

ความยาวทุกชุด 2

[('x', 'y'), ('x', 'z'), ('y', 'z')]

ความยาวทุกชุด 3

[('x', 'y', 'z')]

สำหรับข้อมูลเพิ่มเติมโปรดดูเอกสาร itertoolsรวมถึงรายการวิกิพีเดียในชุดไฟ


3

สิ่งนี้สามารถทำได้อย่างเป็นธรรมชาติด้วย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}

1
คำตอบที่สวยหรูที่สุดสำหรับคำถามนี้
Arthur B.

2

เพียงแค่ทบทวนชุดพลังงานอย่างรวดเร็ว!

ชุดกำลังของเซต X เป็นเพียงเซตของเซตย่อยทั้งหมดของ X รวมทั้งเซตว่าง

ตัวอย่างชุด X = (a, b, c)

ชุดพลังงาน = {{a, b, c}, {a, b}, {a, c}, {b, c}, {a}, {b}, {c}, {}}

นี่คืออีกวิธีหนึ่งในการค้นหาชุดพลังงาน:

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

เครดิตเต็มไปยังแหล่งที่มา


1

วิธีง่ายๆก็คือการใช้ประโยชน์จากการแทนค่าภายในของจำนวนเต็มภายใต้เลขคณิตเสริม 2

การแทนค่าเลขฐานสองของจำนวนเต็มคือ {000, 001, 010, 011, 100, 101, 110, 111} สำหรับตัวเลขตั้งแต่ 0 ถึง 7 สำหรับค่าตัวนับจำนวนเต็มให้พิจารณา 1 เป็นการรวมองค์ประกอบที่เกี่ยวข้องในคอลเลกชันและ '0' เนื่องจากการยกเว้นเราสามารถสร้างชุดย่อยตามลำดับการนับ ต้องสร้างตัวเลขจาก0ถึงpow(2,n) -1ที่ n คือความยาวของอาร์เรย์เช่นจำนวนบิตในการแทนค่าฐานสอง

ฟังก์ชัน Subset Generatorอย่างง่ายสามารถเขียนได้ดังต่อไปนี้ โดยพื้นฐานแล้วมันต้องอาศัย

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

จากนั้นสามารถใช้เป็น

def get_subsets(array):
    powerset = []
    for i in subsets(array):
        powerser.append(i)
    return powerset

การทดสอบ

การเพิ่มสิ่งต่อไปนี้ในไฟล์ภายในเครื่อง

if __name__ == '__main__':
    sample = ['b',  'd',  'f']

    for i in range(len(sample)):
        print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])

ให้ผลลัพธ์ต่อไปนี้

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']]

สิ่งนี้อาจใช้ไม่ได้จริงเกี่ยวกับความสามารถในการบำรุงรักษาหรือความสามารถในการอ่านได้ แต่มันทำให้ฉันรู้สึกแย่ ขอบคุณสำหรับการแบ่งปันวิธีแก้ปัญหาที่ชาญฉลาด!
lorey

1

ด้วยชุดว่างซึ่งเป็นส่วนหนึ่งของชุดย่อยทั้งหมดคุณสามารถใช้:

def subsets(iterable):
    for n in range(len(iterable) + 1):
        yield from combinations(iterable, n)

1

คำตอบเหล่านี้เกือบทั้งหมดใช้listมากกว่าsetซึ่งให้ความรู้สึกเหมือนเป็นการโกงสำหรับฉัน ด้วยความอยากรู้อยากเห็นฉันจึงพยายามทำเวอร์ชันง่ายๆอย่างแท้จริงsetและสรุปสำหรับคน "ใหม่สำหรับ Python" คนอื่น ๆ

ผมพบว่ามีความแปลกประหลาดคู่ในการจัดการกับงูหลามของการดำเนินงานชุด ความประหลาดใจหลักสำหรับฉันคือการจัดการชุดที่ว่างเปล่า สิ่งนี้ตรงกันข้ามกับการใช้งาน Setของ Ruby ซึ่งฉันสามารถทำได้Set[Set[]]และได้รับSetหนึ่งที่ว่างเปล่าSetดังนั้นฉันจึงพบว่าในตอนแรกมันสับสนเล็กน้อย

เพื่อตรวจสอบในการทำpowersetกับsets ฉันพบปัญหาสองประการ:

  1. set()สามารถทำซ้ำได้ดังนั้นset(set())จะกลับมาset() เนื่องจากชุดว่างที่ทำซ้ำได้ว่างเปล่า (ฉันเดา :))
  2. เพื่อให้ได้ชุดของชุดset({set()})และใช้set.add(set)งานไม่ได้เนื่องจากset() ไม่สามารถซักได้

เพื่อแก้ปัญหาทั้งสองอย่างฉันใช้ประโยชน์frozenset()ซึ่งหมายความว่าฉันไม่ค่อยได้รับสิ่งที่ต้องการ (ประเภทคือตัวอักษรset) แต่ใช้ประโยชน์จากsetจำนวนเต็มโดยรวม

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

ด้านล่างเราจะได้2² (16) frozensets อย่างถูกต้องเป็นเอาต์พุต:

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

ในฐานะที่มีวิธีการที่จะมีไม่มีsetของsetในหลามถ้าคุณต้องการที่จะเปิดเหล่านี้frozensets เข้าไปsets, คุณจะต้อง map พวกเขากลับเข้ามาในlist( list(map(set, powerset(set([1,2,3,4])))) ) หรือปรับเปลี่ยนดังกล่าวข้างต้น


1

บางทีคำถามอาจจะเก่าไป แต่ฉันหวังว่ารหัสของฉันจะช่วยใครสักคนได้

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

อี, เรียกซ้ำ! =)
étale-cohomology

อาจไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุด แต่ก็น่าสนใจเสมอที่จะเห็นวิธีการเรียกซ้ำ!
Lisandro Di Meo

1

ใช้ฟังก์ชั่นจากแพคเกจpowerset()more_itertools

ให้ส่วนย่อยที่เป็นไปได้ทั้งหมดของการทำซ้ำ

>>> list(powerset([1, 2, 3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

หากคุณต้องการชุดให้ใช้:

list(map(set, powerset(iterable)))

1
ผู้คนจำนวนมากคิดค้นล้อใหม่ที่นี่ IMHO นี่เป็นคำตอบที่ดีที่สุดเนื่องจากอาจอยู่ในการอ้างอิงของคุณแล้วเนื่องจากไลบรารีทั่วไปหลายแห่งต้องการเช่น pytest libraries.io/pypi/more-itertools/dependents
lorey

1

รับชุดย่อยทั้งหมดด้วยการเรียกซ้ำ หนึ่งซับบ้าตูด

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 [[]]

ขึ้นอยู่กับโซลูชันของ Haskell

subsets :: [a] -> [[a]]
subsets [] = [[]]
subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs

NameError: name 'List' is not defined
4LegsDrivenCat

@ 4LegsDrivenCat ฉันได้เพิ่มListการนำเข้า
Paweł Rubin

1
def 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

คำตอบที่ใช้โค้ดอย่างเดียวถือว่ามีคุณภาพต่ำ: อย่าลืมให้คำอธิบายว่าโค้ดของคุณใช้ทำอะไรและแก้ไขปัญหาได้อย่างไร จะช่วยให้ผู้ถามและผู้อ่านในอนาคตทั้งสองอย่างหากคุณสามารถเพิ่มข้อมูลเพิ่มเติมในโพสต์ของคุณได้ ดูการอธิบายคำตอบแบบใช้รหัสทั้งหมด
Calos

1

คุณสามารถทำได้ดังนี้:

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

เอาท์พุต:

[[], [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]]

1
ฉันขอแนะนำว่าเมื่อโพสต์โซลูชันโค้ดขอให้มีความกรุณาพอที่จะให้คำอธิบายโดยละเอียดเกี่ยวกับสิ่งที่โค้ดกำลังทำอยู่และเหตุใดคุณจึงใช้วิธีนี้หรือวิธีนั้นในการแก้ปัญหา ผู้เขียนโค้ดใหม่ไม่ควรดูแค่บล็อกโค้ดแล้วคัดลอก / วางโดยไม่รู้ว่าโค้ดนั้นทำอะไรและทำไม ขอขอบคุณและยินดีต้อนรับสู่ Stackoverflow
Yokai

คำตอบที่น่าประทับใจและเป็นซ้ำ
John

1

ฉันรู้ว่านี่มันสายเกินไป

มีวิธีแก้ปัญหาอื่น ๆ อีกมากมาย แต่ยัง ...

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

0

นี่เป็นเรื่องปกติเนื่องจากไม่มีคำตอบใดที่ให้ผลตอบแทนของชุด Python จริง นี่คือการใช้งานที่ยุ่งเหยิงซึ่งจะให้พาวเวอร์เซ็ตที่เป็น Python 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([]) ])

ฉันอยากเห็นการใช้งานที่ดีขึ้นแม้ว่า


จุดดี แต่ OP ต้องการรายชื่อของชุดเป็นผลผลิตดังนั้น (ในหลาม 3) คุณสามารถทำ[*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))]; อาร์กิวเมนต์mapสามารถเป็นได้frozensetถ้าคุณต้องการ
PM 2Ring

0

นี่คือการใช้งานอย่างรวดเร็วของฉันโดยใช้ชุดค่าผสม แต่ใช้เฉพาะในตัว

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

0

ชุดย่อยทั้งหมดในช่วง n เป็นชุด:

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 "{}")


0

รูปแบบของคำถามคือแบบฝึกหัดที่ฉันเห็นในหนังสือ "Discovering Computer Science: Interdisciplinary Problems, Principles and Python Programming. 2015 edition" ในแบบฝึกหัด 10.2.11 อินพุตเป็นเพียงตัวเลขจำนวนเต็มและเอาต์พุตควรเป็นชุดกำลัง นี่คือวิธีแก้ปัญหาแบบวนซ้ำของฉัน (ไม่ได้ใช้อย่างอื่นนอกจาก python3 พื้นฐาน)

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

และผลลัพธ์คือ

[[], [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]] จำนวน รายการย่อย: 16


0

ฉันไม่เจอmore_itertools.powersetฟังก์ชั่นนี้และขอแนะนำให้ใช้ ฉันขอแนะนำว่าอย่าใช้ลำดับเริ่มต้นของผลลัพธ์จากitertools.combinationsบ่อยครั้งที่คุณต้องการลดระยะทางระหว่างตำแหน่งและจัดเรียงชุดย่อยของรายการที่มีระยะห่างสั้นกว่าระหว่างด้านบน / ก่อนรายการที่มีระยะห่างระหว่างกันมากขึ้น

itertoolsสูตรหน้าแสดงให้เห็นว่าจะใช้chain.from_iterable

  • โปรดทราบว่าrนี่จะตรงกับสัญกรณ์มาตรฐานสำหรับส่วนล่างของค่าสัมประสิทธิ์ทวินามที่sมักจะเรียกว่าnในวิชาคณิตศาสตร์ตำราและเครื่องคิดเลข (“n เลือก 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))

ตัวอย่างอื่น ๆ ที่นี่ให้พาวเวอร์เซต[1,2,3,4]ในลักษณะที่ 2-tuples แสดงรายการในลำดับ "พจนานุกรม" (เมื่อเราพิมพ์ตัวเลขเป็นจำนวนเต็ม) ถ้าฉันเขียนระยะห่างระหว่างตัวเลขข้างๆ (เช่นความแตกต่าง) จะแสดงจุดของฉัน:

121
132
143
231
242
341

ลำดับที่ถูกต้องสำหรับเซ็ตย่อยควรเป็นลำดับที่ 'หมด' ระยะทางที่น้อยที่สุดก่อนเช่น:

121
231
341
132
242
143

การใช้ตัวเลขที่นี่ทำให้ลำดับนี้ดู 'ผิด' แต่ลองพิจารณาตัวอักษรที่["a","b","c","d"]ชัดเจนกว่าว่าเหตุใดจึงมีประโยชน์ในการรับชุดพาวเวอร์ตามลำดับนี้:

ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3

เอฟเฟกต์นี้เด่นชัดขึ้นเมื่อมีไอเท็มมากขึ้นและสำหรับจุดประสงค์ของฉันมันสร้างความแตกต่างระหว่างความสามารถในการอธิบายช่วงของดัชนีของพาวเวอร์เซตอย่างมีความหมาย

(มีเขียนโค้ดเกรย์ไว้มากมายเป็นต้นสำหรับลำดับเอาต์พุตของอัลกอริทึมใน Combinatorics ฉันไม่เห็นว่าเป็นปัญหาด้านข้าง)

จริงๆแล้วฉันเพิ่งเขียนโปรแกรมที่เกี่ยวข้องพอสมควรซึ่งใช้รหัสพาร์ติชันจำนวนเต็มอย่างรวดเร็วนี้เพื่อส่งออกค่าตามลำดับที่เหมาะสม แต่จากนั้นฉันก็ค้นพบmore_itertools.powersetและสำหรับการใช้งานส่วนใหญ่อาจเป็นเรื่องปกติที่จะใช้ฟังก์ชันนั้นดังนี้:

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)

ผมเขียนโค้ดบางส่วนร่วมมากขึ้นซึ่งจะพิมพ์ powerset อย่าง (ดู repo สำหรับฟังก์ชั่นการพิมพ์สวยฉันไม่ได้รวมอยู่ที่นี่: print_partitions, print_partitions_by_lengthและpprint_tuple)

ทั้งหมดนี้ค่อนข้างง่าย แต่ก็ยังอาจมีประโยชน์หากคุณต้องการโค้ดบางอย่างที่จะช่วยให้คุณเข้าถึงระดับต่างๆของพาวเวอร์เซ็ตได้โดยตรง:

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

ตัวอย่างเช่นฉันเขียนโปรแกรมสาธิต CLI ซึ่งใช้สตริงเป็นอาร์กิวเมนต์บรรทัดคำสั่ง:

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, 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

0

หากคุณต้องการความยาวเฉพาะของชุดย่อยคุณสามารถทำได้ดังนี้:

from itertools import combinations
someSet = {0, 1, 2, 3}
([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])

โดยทั่วไปสำหรับชุดย่อยความยาวของอนุญาโตตุลาการคุณสามารถปรับเปลี่ยนการจัดเก็บช่วง ผลลัพธ์คือ

[(), (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 )]


0

นี่คือคำตอบของฉันมันคล้ายกัน (ในเชิงแนวคิด) กับคำตอบของ lmiguelvargasf

ให้ฉันบอกว่า - [รายการคณิตศาสตร์] โดยการกำหนดพาวเวอร์เซ็ตจะมีเซตว่าง - [รสนิยมส่วนตัว] และฉันไม่ชอบใช้โฟรเซ่น

ดังนั้นอินพุตจึงเป็นรายการและผลลัพธ์จะเป็นลิสต์รายการ ฟังก์ชั่นนี้สามารถปิดได้ก่อนหน้านี้ แต่ฉันชอบองค์ประกอบของการตั้งค่าพลังงานเป็นคำสั่งตามศัพท์ซึ่งโดยพื้นฐานแล้วหมายถึงอย่างดี

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

-1
def powerset(some_set):
    res = [(a,b) for a in some_set for b in some_set]
    return res

6
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีที่รหัสนี้ตอบคำถามช่วยเพิ่มมูลค่าในระยะยาว ลองอ่านวิธีการตอบและแก้ไขคำตอบของคุณเพื่อปรับปรุง
เบลอฟัส

2
สิ่งที่ @blurfus ถือเป็นแนวทางปฏิบัติที่ดีเสมอมา แต่มีความสำคัญอย่างยิ่งเมื่อคุณตอบคำถามเก่าแก่กว่าทศวรรษพร้อมคำตอบอื่น ๆ อีก 28 ข้อ เหตุใดจึงเป็นการปรับปรุงมากกว่าคำตอบที่ยอมรับ คำตอบนี้มีส่วนทำให้คำตอบอื่นไม่มีอะไร
Jeremy Caney
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.