วิธีรับชุดองค์ประกอบของรายการทั้งหมดที่เป็นไปได้?


423

ฉันมีรายการที่มีตัวเลข 15 ตัวและฉันต้องเขียนโค้ดบางอย่างที่สร้างชุดค่าผสมทั้งหมด 32,768 ตัว

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

สิ่งเดียวที่เกิดขึ้นกับฉันก็คือการวนซ้ำทศนิยมจำนวนเต็ม 1–32768 และแปลงให้เป็นเลขฐานสองและใช้การแทนเลขฐานสองเป็นตัวกรองเพื่อเลือกตัวเลขที่เหมาะสม

ไม่มีใครรู้วิธีที่ดีกว่า? ใช้map()บางที


9
ผู้อ่านควรทราบว่ารายการที่เป็นเอกลักษณ์นั้นเป็นสิ่งที่สำคัญอย่างยิ่งหรือไม่เนื่องจากอัลกอริธึมจำนวนมากจะเกินจำนวนเซตย่อยบางส่วน (เช่น 'abccc' -> ['', 'a', 'b', 'c', 'c' . 'C', 'AC', 'AC', 'AC' ... ] เกิดการแก้ปัญหาที่ง่ายคือการผลักเพียงองค์ประกอบทั้งหมดในชุดก่อนที่จะได้รับการเปลี่ยนลำดับของพวกเขา.
ninjagecko

@ninjagecko การใช้ห้องสมุด Set ไม่ได้มีประสิทธิภาพเนื่องจากแต่ละรายการเป็น O (n) ที่ดีที่สุด ดังนั้นการเพิ่มฟังก์ชั่น n เข้าไปในเซตจึงเป็นจริง O (n ^ 2)!
Scott Biggs

จากการอ่านคำถามอย่างละเอียดดูเหมือนว่า OP กำลังขอPowerSetจากรายการหมายเลข 15 ของเขาไม่ใช่ชุดค่าผสมทั้งหมด ฉันคิดว่านี่อาจเป็นสาเหตุที่คำตอบอยู่ทั่วสถานที่
Scott Biggs

@Scott Biggs: คุณแน่ใจหรือไม่ว่าคุณสนใจ Python ที่นี่? การตั้งค่าการแทรกและการค้นหาเป็นกรณีโดยเฉลี่ย O (1) พวกเขาเหมือนพจนานุกรม พวกเขาใช้ hashing Python ไม่มีไลบรารี่ชุดพิเศษ (อยู่ในไลบรารี่มาตรฐาน) เรากำลังแทรกตัวเลขที่นี่ไม่ใช่ฟังก์ชัน (มันจะยังไม่มีประสิทธิภาพในการใช้หน่วยความจำ O (2 ^ n) วิธีที่เหมาะสมสำหรับผู้ที่ต้องการชุดค่าผสมมากกว่าการใช้ powerset เป็นการใช้งานแบบเรียกซ้ำแบบง่าย ๆ หรือproductอื่น ๆ )
ninjagecko

คำตอบ:


466

ดูที่itertools.combinations :

itertools.combinations(iterable, r)

ส่งคืนความยาว r องค์ประกอบขององค์ประกอบจากอินพุต iterable

ชุดค่าผสมจะถูกปล่อยออกมาในลำดับการจัดเรียงพจนานุกรม ดังนั้นหากอินพุต iterable ถูกเรียงลำดับ tuples รวมกันจะถูกสร้างขึ้นในลำดับที่เรียงลำดับ

ตั้งแต่ 2.6 รวมแบตเตอรี่!


31
คุณสามารถแสดงรายการทั้งหมดได้ list(itertools.combinations(iterable, r))
silgon

1
มีอะไรบ้างที่ไม่ต้องการrนั่นก็คือการรวมกันของความยาวขององค์ประกอบใด ๆ ก็ตาม
mLstudent33

630

คำตอบนี้พลาดด้านหนึ่ง: OP ขอชุดค่าผสมทั้งหมด ... ไม่ใช่แค่ชุดของความยาว "r"

ดังนั้นคุณต้องวนซ้ำทุกความยาว "L":

import itertools

stuff = [1, 2, 3]
for L in range(0, len(stuff)+1):
    for subset in itertools.combinations(stuff, L):
        print(subset)

หรือ - หากคุณต้องการได้รับความโกลาหล (หรืองอสมองของใครก็ตามที่อ่านรหัสของคุณหลังจากคุณ) - คุณสามารถสร้างเครือข่ายของเครื่องกำเนิดไฟฟ้า "ชุดผสม ()" และย้ำผ่าน:

from itertools import chain, combinations
def all_subsets(ss):
    return chain(*map(lambda x: combinations(ss, x), range(0, len(ss)+1)))

for subset in all_subsets(stuff):
    print(subset)

42
ขอบคุณสำหรับการสนับสนุน! ในสัปดาห์ที่ผ่านมาตั้งแต่ฉันโพสต์คำตอบข้างต้นฉันพบว่าชื่อของแนวคิดสำหรับสิ่งที่ Ben กำลังมองหาคือ "powerset" ของชุดเดิม 15 รายการ อันที่จริงมีการใช้งานตัวอย่างในหน้าเอกสาร "itertools" python แบบมาตรฐาน: docs.python.org/library/itertools.html (grep สำหรับ "powerset")
Dan H

38
สำหรับทุกคนที่อ่านข้อมูลนี้: powerset()ฟังก์ชันตัวสร้างในส่วนสูตรอาหารของitertoolsเอกสารนั้นง่ายกว่าอาจใช้หน่วยความจำน้อยกว่าและมีแนวโน้มเร็วกว่าการนำไปใช้ที่แสดงไว้ที่นี่
martineau

เป็นไปได้หรือไม่ที่จะสร้างชุดค่าผสมทั้งหมดตามลำดับพจนานุกรม?
guik

@ guik: ฉันแน่ใจว่า 99% ที่itertools.combinationsรักษาลำดับรายการในรายการที่ให้ผลตอบแทน ดังนั้นหากอินพุตถูกจัดเรียงแบบ lexically ดังนั้นแต่ละเอาต์พุตจะเป็นเช่นกัน
Dan H

ใช่itertools.combinationsสร้างชุดค่าผสมของ k ใน n ตามลำดับพจนานุกรม แต่ไม่ใช่ชุดค่าผสมทั้งหมดจนถึง k ในจำนวน n powersetสร้างชุดค่าผสมทั้งหมดไม่เกิน k แต่ไม่เรียงตามคำศัพท์เท่าที่ฉันเข้าใจ: powerset ([1,2]) -> [(), (1,), (2,), (1, 2)] . ไม่ควรจะเป็น: [(), (1,), (1, 2), (2,)]?
guik

52

นี่คือซับหนึ่งขี้เกียจใช้ itertools ด้วย:

from itertools import compress, product

def combinations(items):
    return ( set(compress(items,mask)) for mask in product(*[[0,1]]*len(items)) )
    # alternative:                      ...in product([0,1], repeat=len(items)) )

แนวคิดหลักที่อยู่เบื้องหลังคำตอบนี้: มีการรวมกัน 2 ^ N - เช่นเดียวกับจำนวนของสตริงความยาว N สำหรับแต่ละไบนารีสตริงคุณเลือกองค์ประกอบทั้งหมดที่สอดคล้องกับ "1"

items=abc * mask=###
 |
 V
000 -> 
001 ->   c
010 ->  b
011 ->  bc
100 -> a
101 -> a c
110 -> ab
111 -> abc

สิ่งที่ต้องพิจารณา:

  • สิ่งนี้ต้องการให้คุณสามารถเรียกlen(...)ใช้items(วิธีแก้ปัญหา: ถ้าitemsเป็นสิ่งที่ทำซ้ำได้เช่นเครื่องกำเนิดให้เปลี่ยนเป็นรายการแรกด้วยitems=list(_itemsArg))
  • สิ่งนี้ต้องการให้ลำดับของการวนซ้ำบนitemsไม่สุ่ม (วิธีแก้ปัญหา: ไม่เสียสติ)
  • สิ่งนี้ต้องการให้ไอเท็มนั้นมีเอกลักษณ์หรืออย่างอื่น{2,2,1}และ{2,1,1}ทั้งคู่จะยุบลงไป{2,1}(การแก้ไข: ใช้collections.Counterแทนการดรอปอินsetเป็นมัลติเซ็ต ... ถึงแม้ว่าคุณอาจจำเป็นต้องใช้ในภายหลังtuple(sorted(Counter(...).elements()))หากคุณต้องการแฮช)

การสาธิต

>>> list(combinations(range(4)))
[set(), {3}, {2}, {2, 3}, {1}, {1, 3}, {1, 2}, {1, 2, 3}, {0}, {0, 3}, {0, 2}, {0, 2, 3}, {0, 1}, {0, 1, 3}, {0, 1, 2}, {0, 1, 2, 3}]

>>> list(combinations('abcd'))
[set(), {'d'}, {'c'}, {'c', 'd'}, {'b'}, {'b', 'd'}, {'c', 'b'}, {'c', 'b', 'd'}, {'a'}, {'a', 'd'}, {'a', 'c'}, {'a', 'c', 'd'}, {'a', 'b'}, {'a', 'b', 'd'}, {'a', 'c', 'b'}, {'a', 'c', 'b', 'd'}]

46

ในความคิดเห็นภายใต้ upvoted ขอคำตอบจาก @Dan H, เอ่ยถึงทำของpowerset()สูตรในitertoolsเอกสาร -including หนึ่งโดยแดนตัวเอง อย่างไรก็ตามจนถึงขณะนี้ยังไม่มีใครโพสต์ไว้เป็นคำตอบ เนื่องจากอาจเป็นหนึ่งในวิธีที่ดีกว่าถ้าไม่ใช่วิธีที่ดีที่สุดในการแก้ปัญหา - และได้รับการสนับสนุนเล็กน้อยจากผู้แสดงความคิดเห็นคนอื่นจึงแสดงไว้ด้านล่าง ฟังก์ชันสร้างชุดค่าผสมที่เป็นเอกลักษณ์ทั้งหมดขององค์ประกอบรายการของทุกความยาวที่เป็นไปได้ (รวมถึงองค์ประกอบที่มีศูนย์และองค์ประกอบทั้งหมด)

หมายเหตุ : หากเป้าหมายที่แตกต่างอย่างละเอียดคือการได้รับการรวมกันขององค์ประกอบที่ไม่ซ้ำกันให้เปลี่ยนบรรทัดs = list(iterable)เป็นs = list(set(iterable))เพื่อกำจัดองค์ประกอบที่ซ้ำกัน โดยไม่คำนึงถึงความจริงที่ว่าในiterableที่สุดก็กลายเป็นlistวิธีการที่จะทำงานร่วมกับเครื่องกำเนิดไฟฟ้า (ไม่เหมือนคำตอบอื่น ๆ อีกมากมาย)

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)  # allows duplicate elements
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

stuff = [1, 2, 3]
for i, combo in enumerate(powerset(stuff), 1):
    print('combo #{}: {}'.format(i, combo))

เอาท์พุท:

combo #1: ()
combo #2: (1,)
combo #3: (2,)
combo #4: (3,)
combo #5: (1, 2)
combo #6: (1, 3)
combo #7: (2, 3)
combo #8: (1, 2, 3)

การlist()แปลงครั้งแรกคืออะไร?
AMC

@Alexander: เพื่อให้ความยาวของ iterable ได้รับการพิจารณา
martineau

36

นี่คือการใช้การเรียกซ้ำ:

>>> import copy
>>> def combinations(target,data):
...     for i in range(len(data)):
...         new_target = copy.copy(target)
...         new_data = copy.copy(data)
...         new_target.append(data[i])
...         new_data = data[i+1:]
...         print new_target
...         combinations(new_target,
...                      new_data)
...                      
... 
>>> target = []
>>> data = ['a','b','c','d']
>>> 
>>> combinations(target,data)
['a']
['a', 'b']
['a', 'b', 'c']
['a', 'b', 'c', 'd']
['a', 'b', 'd']
['a', 'c']
['a', 'c', 'd']
['a', 'd']
['b']
['b', 'c']
['b', 'c', 'd']
['b', 'd']
['c']
['c', 'd']
['d']

สิ่งนี้สามารถแก้ไขเพื่อส่งคืนรายการแทนที่จะพิมพ์หรือไม่?
James Vickery

@JamesVickery ใช่คุณอาจมีลักษณะอย่างใดอย่างหนึ่งที่ทำรายการนอกของฟังก์ชั่นและผนวกกับที่หรือ (ดีกว่า) ทำให้การทำงานของเครื่องกำเนิดไฟฟ้าได้ดูที่ 'ผลตอบแทน' คำหลัก :)
Dangercrow

new_data = copy.copy(data)- แถวนี้ซ้ำซ้อนเท่าที่ฉันเห็นมันไม่มีผลอะไรเลย
Dmitriy Fialkovskiy

31

ซับเดี่ยวนี้ให้ชุดค่าผสมทั้งหมด (ระหว่าง0และnรายการถ้ารายการ / ชุดเดิมมีnองค์ประกอบที่แตกต่างกัน) และใช้วิธีเนทีฟitertools.combinations:

Python 2

from itertools import combinations

input = ['a', 'b', 'c', 'd']

output = sum([map(list, combinations(input, i)) for i in range(len(input) + 1)], [])

Python 3

from itertools import combinations

input = ['a', 'b', 'c', 'd']

output = sum([list(map(list, combinations(input, i))) for i in range(len(input) + 1)], [])

ผลลัพธ์จะเป็น:

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

ลองออนไลน์:

http://ideone.com/COghfX


นี่คือการเปลี่ยนแปลง
AdHominem

15
@AdHominem: ไม่มันไม่ใช่ มันคือรายการของชุดค่าผสมทั้งหมด ['b', 'a']พีชคณิตจะรวมถึงเช่น
naught101

TypeError: can only concatenate list (not "map") to list
0x48piraj

@ 0x48piraj: ขอบคุณสำหรับการสังเกตฉันแก้ไขคำตอบของฉันแล้ว!
Mathieu Rodic

21

ฉันเห็นด้วยกับ Dan H ว่า Ben ขอชุดค่าผสมทั้งหมด itertools.combinations()ไม่ได้ให้ชุดค่าผสมทั้งหมด

ปัญหาอื่นคือถ้าอินพุตที่ iterable มีขนาดใหญ่อาจเป็นการดีกว่าถ้าส่งคืนตัวสร้างแทนทุกอย่างในรายการ:

iterable = range(10)
for s in xrange(len(iterable)+1):
  for comb in itertools.combinations(iterable, s):
    yield comb

1
ตัวอย่างที่ดี ฉันรักเครื่องกำเนิดไฟฟ้า ... และฉันรัก Python ที่มีพวกเขา! ตัวอย่างนี้มีเพียงชุดค่าผสม () วัตถุหนึ่งชุดในแต่ละครั้งและให้ชุดค่าผสมหนึ่งชุดต่อครั้ง (บางทีคุณต้องการเพิ่ม def block รอบ ๆ นี้ - เป็นตัวอย่างการใช้งาน) โปรดทราบว่าการติดตั้ง (ด้วย chain (), ที่ระบุด้านบน) ของฉันนั้นไม่ได้เลวร้ายไปกว่านี้: มันเป็นความจริงที่สร้างกำเนิด len ทั้งหมด (iterable) ครั้งเดียว ... แต่มันไม่ได้สร้างชุดค่าผสมทั้งหมด 2 ** len (iterable) ในครั้งเดียวเช่นเดียวกับ - เพื่อความเข้าใจของฉัน - โซ่ "ใช้" ตัวกำเนิดไฟฟ้าแรกก่อนที่จะวาดจากชุดที่ตามมา
Dan H

18

นี่เป็นวิธีการที่สามารถถ่ายโอนไปยังภาษาการเขียนโปรแกรมทั้งหมดที่รองรับการเรียกซ้ำ(ไม่มี itertools ไม่มีผลตอบแทนไม่มีรายการเข้าใจ) :

def combs(a):
    if len(a) == 0:
        return [[]]
    cs = []
    for c in combs(a[1:]):
        cs += [c, c+[a[0]]]
    return cs

>>> combs([1,2,3,4,5])
[[], [1], [2], [2, 1], [3], [3, 1], [3, 2], ..., [5, 4, 3, 2, 1]]

อา! การใช้งานที่ดีฉันรู้จัก HEAD = a [0], TAIL = a [1:] จาก Prolog หรือรถยนต์ = a [0], cdr = a [1:] จาก Lisp ฉันสงสัยว่าเราสามารถใช้บันทึกช่วยจำที่นี่ ...
Javier Ruiz

จริง การแบ่งส่วนรายการคือ O (k) โดยที่ k คือความยาวของชิ้น ฉันเดาว่าใคร ๆ ก็สามารถเร่งความเร็วได้โดยการค้นหาในแผนที่ซึ่งจะทำให้ O (1) ทำงานได้ทุกอย่าง แต่ครั้งแรก โปรดทราบว่าการใช้งานนี้ไม่ควรอ้างอิงสำหรับประสิทธิภาพ สำหรับการใช้งานที่ดีกว่านั้นมีอยู่ การติดตั้งนี้ใช้เพื่อความเรียบง่ายและความสะดวกในการพกพาสำหรับภาษาอื่น
Jonathan R

14

คุณสามารถสร้างรายการทั้งหมดในหลามโดยใช้รหัสง่าย ๆ นี้

import itertools

a = [1,2,3,4]
for i in xrange(0,len(a)+1):
   print list(itertools.combinations(a,i))

ผลลัพธ์จะเป็น:

[()]
[(1,), (2,), (3,), (4,)]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
[(1, 2, 3, 4)]

ข้อผิดพลาดในรหัสนี้: ไม่ส่งคืนชุดที่ว่างเปล่า อาจหมายถึง xrange (0, ... ) แต่ยังไม่ได้ทดสอบ แก้ไข : ฉันไปข้างหน้าและแก้ไขคำตอบของคุณเพื่อแก้ไข
ninjagecko

13

ฉันคิดว่าฉันจะเพิ่มฟังก์ชั่นนี้สำหรับผู้ที่ต้องการคำตอบโดยไม่ต้องนำเข้า itertools หรือไลบรารีพิเศษอื่น ๆ

def powerSet(items):
    """
    Power set generator: get all possible combinations of a list’s elements

    Input:
        items is a list
    Output:
        returns 2**n combination lists one at a time using a generator 

    Reference: edx.org 6.00.2x Lecture 2 - Decision Trees and dynamic programming
    """

    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

การใช้เครื่องสร้างอัตราผลตอบแทนอย่างง่าย:

for i in powerSet([1,2,3,4]):
    print (i, ", ",  end="")

เอาต์พุตจากตัวอย่างการใช้งานด้านบน:

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


ฉันคิดว่านี่เป็นทางออกที่เรียบร้อยมาก
greentec

8

นี่เป็นอีกวิธีหนึ่ง (หนึ่งซับ) ที่เกี่ยวข้องกับการใช้itertools.combinationsฟังก์ชั่น แต่ที่นี่เราใช้ความเข้าใจแบบสองรายการ (ตรงข้ามกับลูปหรือผลรวม):

def combs(x):
    return [c for i in range(len(x)+1) for c in combinations(x,i)]

การสาธิต:

>>> combs([1,2,3,4])
[(), 
 (1,), (2,), (3,), (4,), 
 (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), 
 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4), 
 (1, 2, 3, 4)]

5
from itertools import permutations, combinations


features = ['A', 'B', 'C']
tmp = []
for i in range(len(features)):
    oc = combinations(features, i + 1)
    for c in oc:
        tmp.append(list(c))

เอาท์พุต

[
 ['A'],
 ['B'],
 ['C'],
 ['A', 'B'],
 ['A', 'C'],
 ['B', 'C'],
 ['A', 'B', 'C']
]

4

ด้านล่างเป็น "คำตอบ recursive มาตรฐาน" คล้ายกับคำตอบอื่น ๆ ที่คล้ายhttps://stackoverflow.com/a/23743696/711085 (เราไม่ต้องกังวลเกี่ยวกับการใช้พื้นที่สแต็คเนื่องจากไม่มีวิธีที่เราสามารถประมวลผลการเปลี่ยนลำดับ N!)

มันเข้าชมองค์ประกอบทุกอย่างและนำหรือทิ้ง (เราสามารถเห็นความสำคัญเชิงซ้อนจาก ^ 2N โดยตรงจากอัลกอริทึมนี้)

def combs(xs, i=0):
    if i==len(xs):
        yield ()
        return
    for c in combs(xs,i+1):
        yield c
        yield c+(xs[i],)

การสาธิต:

>>> list( combs(range(5)) )
[(), (0,), (1,), (1, 0), (2,), (2, 0), (2, 1), (2, 1, 0), (3,), (3, 0), (3, 1), (3, 1, 0), (3, 2), (3, 2, 0), (3, 2, 1), (3, 2, 1, 0), (4,), (4, 0), (4, 1), (4, 1, 0), (4, 2), (4, 2, 0), (4, 2, 1), (4, 2, 1, 0), (4, 3), (4, 3, 0), (4, 3, 1), (4, 3, 1, 0), (4, 3, 2), (4, 3, 2, 0), (4, 3, 2, 1), (4, 3, 2, 1, 0)]

>>> list(sorted( combs(range(5)), key=len))
[(), 
 (0,), (1,), (2,), (3,), (4,), 
 (1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3), 
 (2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1), (4, 3, 2), 
 (3, 2, 1, 0), (4, 2, 1, 0), (4, 3, 1, 0), (4, 3, 2, 0), (4, 3, 2, 1), 
 (4, 3, 2, 1, 0)]

>>> len(set(combs(range(5))))
32

2

ใช้ความเข้าใจในรายการ:

def selfCombine( list2Combine, length ):
    listCombined = str( ['list2Combine[i' + str( i ) + ']' for i in range( length )] ).replace( "'", '' ) \
                     + 'for i0 in range(len( list2Combine ) )'
    if length > 1:
        listCombined += str( [' for i' + str( i ) + ' in range( i' + str( i - 1 ) + ', len( list2Combine ) )' for i in range( 1, length )] )\
            .replace( "', '", ' ' )\
            .replace( "['", '' )\
            .replace( "']", '' )

    listCombined = '[' + listCombined + ']'
    listCombined = eval( listCombined )

    return listCombined

list2Combine = ['A', 'B', 'C']
listCombined = selfCombine( list2Combine, 2 )

ผลผลิตจะเป็น:

['A', 'A']
['A', 'B']
['A', 'C']
['B', 'B']
['B', 'C']
['C', 'C']

4
ข้อเสนอนี้มีไว้เพื่อสร้างชุดสตริง?!?! อีกาศักดิ์สิทธิ์ .... และ: มันไม่ได้ส่งคืนพาวเวอร์เซ็ต แต่กลับมีบางอย่างเช่นชุดค่าผสม _with_replacement () (ดูdocs.python.org/library/… )
Dan H

สิ่งนี้ทำเช่นเดียวกันกับcombination_with_replacement ()แต่อย่างน้อยในกล่องของฉันสิ่งนี้จะทำงานเร็วกว่าitertoolsเล็กน้อย ฉันสามารถพูดอะไรได้ฉันชอบความเข้าใจในรายการ
zmk

1
ขอบคุณสำหรับคำตอบ! สิ่งที่เกี่ยวกับการสร้างรายการรวมกับรายการที่กลับรายการเช่น ['A', 'A'], ['A', 'B'], ['A', 'C'], ['B', 'A'], [ 'B', 'B'], ['B', 'C'], ['C', 'A'], ['C', 'B'] และ ['C', 'C'] ซึ่งรวมถึง ทุกอย่าง?
Karyo

น่าสนใจมาก แต่งูใหญ่ของฉันไม่เข้าใจการทำรายละเอียดปลีกย่อยที่นี่ มีบางสิ่งที่พิเศษเกี่ยวกับการใช้ listCombined ในขอบเขตที่แตกต่างกันและความจริงที่ว่า for for loop นั้นทั้งหมดในหนึ่งบรรทัด ฉันพยายามที่จะพอร์ตนี้เพื่อ Java ด้วยโชคเล็กน้อย
Scott Biggs

2

รหัสนี้ใช้อัลกอริทึมอย่างง่ายพร้อมรายการซ้อนกัน ...

# FUNCTION getCombos: To generate all combos of an input list, consider the following sets of nested lists...
#
#           [ [ [] ] ]
#           [ [ [] ], [ [A] ] ]
#           [ [ [] ], [ [A],[B] ],         [ [A,B] ] ]
#           [ [ [] ], [ [A],[B],[C] ],     [ [A,B],[A,C],[B,C] ],                   [ [A,B,C] ] ]
#           [ [ [] ], [ [A],[B],[C],[D] ], [ [A,B],[A,C],[B,C],[A,D],[B,D],[C,D] ], [ [A,B,C],[A,B,D],[A,C,D],[B,C,D] ], [ [A,B,C,D] ] ]
#
#  There is a set of lists for each number of items that will occur in a combo (including an empty set).
#  For each additional item, begin at the back of the list by adding an empty list, then taking the set of
#  lists in the previous column (e.g., in the last list, for sets of 3 items you take the existing set of
#  3-item lists and append to it additional lists created by appending the item (4) to the lists in the
#  next smallest item count set. In this case, for the three sets of 2-items in the previous list. Repeat
#  for each set of lists back to the initial list containing just the empty list.
#

def getCombos(listIn = ['A','B','C','D','E','F'] ):
    listCombos = [ [ [] ] ]     # list of lists of combos, seeded with a list containing only the empty list
    listSimple = []             # list to contain the final returned list of items (e.g., characters)

    for item in listIn:
        listCombos.append([])   # append an emtpy list to the end for each new item added
        for index in xrange(len(listCombos)-1, 0, -1):  # set the index range to work through the list
            for listPrev in listCombos[index-1]:        # retrieve the lists from the previous column
                listCur = listPrev[:]                   # create a new temporary list object to update
                listCur.append(item)                    # add the item to the previous list to make it current
                listCombos[index].append(listCur)       # list length and append it to the current list

                itemCombo = ''                          # Create a str to concatenate list items into a str
                for item in listCur:                    # concatenate the members of the lists to create
                    itemCombo += item                   # create a string of items
                listSimple.append(itemCombo)            # add to the final output list

    return [listSimple, listCombos]
# END getCombos()

ดังนั้นสิ่งที่รหัสนี้ดูเหมือนจะทำคือส่งคืน [listOfCombinations, listOfCombinationsGroupedBySize] น่าเสียดายที่เมื่อรันด้วยอินพุทตัวอย่างมันให้ 63 องค์ประกอบมากกว่า 64; ดูเหมือนว่าจะไม่มีชุดว่าง (ในกรณีนี้คือสตริงว่าง"")
ninjagecko

2

ฉันรู้ว่ามันไกลมากขึ้นในทางปฏิบัติที่จะใช้ itertools ที่จะได้รับทั้งหมดรวมกัน แต่คุณสามารถบรรลุนี้ส่วนหนึ่งที่มีความเข้าใจในรายการเท่านั้นดังนั้นหากคุณเกิดขึ้นกับความปรารถนาที่ได้รับคุณต้องการรหัสเป็นจำนวนมาก

สำหรับการรวมกันของสองคู่:

    lambda l: [(a, b) for i, a in enumerate(l) for b in l[i+1:]]


และสำหรับการรวมกันของสามคู่มันง่ายอย่างนี้:

    lambda l: [(a, b, c) for i, a in enumerate(l) for ii, b in enumerate(l[i+1:]) for c in l[i+ii+2:]]


ผลลัพธ์จะเหมือนกับการใช้ itertools.combinations:

import itertools
combs_3 = lambda l: [
    (a, b, c) for i, a in enumerate(l) 
    for ii, b in enumerate(l[i+1:]) 
    for c in l[i+ii+2:]
]
data = ((1, 2), 5, "a", None)
print("A:", list(itertools.combinations(data, 3)))
print("B:", combs_3(data))
# A: [((1, 2), 5, 'a'), ((1, 2), 5, None), ((1, 2), 'a', None), (5, 'a', None)]
# B: [((1, 2), 5, 'a'), ((1, 2), 5, None), ((1, 2), 'a', None), (5, 'a', None)]

2

โดยไม่ต้องใช้ itertools:

def combine(inp):
    return combine_helper(inp, [], [])


def combine_helper(inp, temp, ans):
    for i in range(len(inp)):
        current = inp[i]
        remaining = inp[i + 1:]
        temp.append(current)
        ans.append(tuple(temp))
        combine_helper(remaining, temp, ans)
        temp.pop()
    return ans


print(combine(['a', 'b', 'c', 'd']))

2

นี่คือสองการใช้งานของ itertools.combinations

รายการที่ส่งคืนรายการ

def combinations(lst, depth, start=0, items=[]):
    if depth <= 0:
        return [items]
    out = []
    for i in range(start, len(lst)):
        out += combinations(lst, depth - 1, i + 1, items + [lst[i]])
    return out

หนึ่งส่งคืนเครื่องกำเนิดไฟฟ้า

def combinations(lst, depth, start=0, prepend=[]):
    if depth <= 0:
        yield prepend
    else:
        for i in range(start, len(lst)):
            for c in combinations(lst, depth - 1, i + 1, prepend + [lst[i]]):
                yield c

โปรดทราบว่าการให้ฟังก์ชั่นผู้ช่วยแก่ผู้ที่ได้รับการแนะนำนั้นเป็นเพราะอาร์กิวเมนต์การเติมหน้าเป็นแบบคงที่และจะไม่เปลี่ยนแปลงกับการโทรทุกครั้ง

print([c for c in combinations([1, 2, 3, 4], 3)])
# [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]

# get a hold of prepend
prepend = [c for c in combinations([], -1)][0]
prepend.append(None)

print([c for c in combinations([1, 2, 3, 4], 3)])
# [[None, 1, 2, 3], [None, 1, 2, 4], [None, 1, 3, 4], [None, 2, 3, 4]]

นี่เป็นกรณีที่ตื้นมาก แต่ควรจะปลอดภัยกว่าขออภัย


2

วิธีการเกี่ยวกับ .. ใช้สตริงแทนรายการ แต่สิ่งเดียวกัน .. สามารถใช้สตริงเหมือนรายการใน Python:

def comb(s, res):
    if not s: return
    res.add(s)
    for i in range(0, len(s)):
        t = s[0:i] + s[i + 1:]
        comb(t, res)

res = set()
comb('game', res) 

print(res)

2

การรวมกันจาก itertools

import itertools
col_names = ["aa","bb", "cc", "dd"]
all_combinations = itertools.chain(*[itertools.combinations(col_names,i+1) for i,_ in enumerate(col_names)])
print(list(all_combinations))

ขอบคุณ


2

หากไม่มี itertoolsใน Python 3 คุณสามารถทำสิ่งนี้:

def combinations(arr, carry):
    for i in range(len(arr)):
        yield carry + arr[i]
        yield from combinations(arr[i + 1:], carry + arr[i])

ในตอนแรก carry = "".


2

3 ฟังก์ชั่น:

  1. การรวมกันของรายการองค์ประกอบ n ทั้งหมด
  2. การรวมกันทั้งหมดของรายการองค์ประกอบ n ที่คำสั่งไม่แตกต่างกัน
  3. พีชคณิตทั้งหมด
import sys

def permutations(a):
    return combinations(a, len(a))

def combinations(a, n):
    if n == 1:
        for x in a:
            yield [x]
    else:
        for i in range(len(a)):
            for x in combinations(a[:i] + a[i+1:], n-1):
                yield [a[i]] + x

def combinationsNoOrder(a, n):
    if n == 1:
        for x in a:
            yield [x]
    else:
        for i in range(len(a)):
            for x in combinationsNoOrder(a[:i], n-1):
                yield [a[i]] + x

if __name__ == "__main__":
    for s in combinations(list(map(int, sys.argv[2:])), int(sys.argv[1])):
        print(s)

ฉันชอบสิ่งนี้มาก !!! ขอบคุณ!!! ฟังก์ชั่น combinatorics ของ Python ค่อนข้างจะแปลกไปเล็กน้อย ในฟังก์ชั่น "ชุดค่าผสม" ทางคณิตศาสตร์จะเป็นชุดรูปแบบและชุด "ชุดค่าผสมไม่มีคำสั่ง" เป็นชุดค่าผสมจริง ฉันเดาว่าจะทำให้ผู้คนสับสนกับหลามที่มาจากสาขาวิชาคณิตศาสตร์เช่นเดียวกับที่ฉันทำในครั้งนี้ ยังไงก็ตามทางออกที่ดีขอบคุณมาก ๆ !
Đumić Branislav

1

นี่คือการดำเนินการของฉัน

    def get_combinations(list_of_things):
    """gets every combination of things in a list returned as a list of lists

    Should be read : add all combinations of a certain size to the end of a list for every possible size in the
    the list_of_things.

    """
    list_of_combinations = [list(combinations_of_a_certain_size)
                            for possible_size_of_combinations in range(1,  len(list_of_things))
                            for combinations_of_a_certain_size in itertools.combinations(list_of_things,
                                                                                         possible_size_of_combinations)]
    return list_of_combinations

1
การใช้งานของคุณมีวิธีการแก้อะไรที่ดีกว่าการติดตั้งใช้งานก่อนหน้านี้
user1767754

0

คุณยังสามารถใช้ฟังก์ชั่นpowersetจากmore_itertoolsแพ็คเกจยอดเยี่ยม

from more_itertools import powerset

l = [1,2,3]
list(powerset(l))

# [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

นอกจากนี้เรายังสามารถตรวจสอบว่ามันเป็นไปตามความต้องการของ OP

from more_itertools import ilen

assert ilen(powerset(range(15))) == 32_768

-1
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
    return
indices = range(r)
yield tuple(pool[i] for i in indices)
while True:
    for i in reversed(range(r)):
        if indices[i] != i + n - r:
            break
    else:
        return
    indices[i] += 1
    for j in range(i+1, r):
        indices[j] = indices[j-1] + 1
    yield tuple(pool[i] for i in indices)


x = [2, 3, 4, 5, 1, 6, 4, 7, 8, 3, 9]
for i in combinations(x, 2):
    print i

1
ถ้าฉันถูกต้องนี่เป็นรหัสที่ถูกคัดลอกมาจากเอกสารของไพธ อน [ docs.python.org/3.6/library/itertools.html ] ถ้าเป็นเช่นนั้นโปรดอ้างอิงแหล่งที่มา
GabrielChu

วิธีการที่น่าสนใจ
pelos

-1

หากใครบางคนกำลังมองหารายการที่กลับรายการเหมือนฉัน:

stuff = [1, 2, 3, 4]

def reverse(bla, y):
    for subset in itertools.combinations(bla, len(bla)-y):
        print list(subset)
    if y != len(bla):
        y += 1
        reverse(bla, y)

reverse(stuff, 1)

-1
flag = 0
requiredCals =12
from itertools import chain, combinations

def powerset(iterable):
    s = list(iterable)  # allows duplicate elements
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

stuff = [2,9,5,1,6]
for i, combo in enumerate(powerset(stuff), 1):
    if(len(combo)>0):
        #print(combo , sum(combo))
        if(sum(combo)== requiredCals):
            flag = 1
            break
if(flag==1):
    print('True')
else:
    print('else')
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.