วิธีสร้างพีชคณิตทั้งหมดของรายการ?


592

คุณจะสร้างพีชคณิตทั้งหมดของรายการใน Python โดยไม่ขึ้นกับประเภทขององค์ประกอบในรายการได้อย่างไร

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

permutations([])
[]

permutations([1])
[1]

permutations([1, 2])
[1, 2]
[2, 1]

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

5
ฉันเห็นด้วยกับคำตอบแบบเรียกซ้ำ - ยอมรับวันนี้ อย่างไรก็ตามสิ่งนี้ยังคงค้างอยู่ที่นั่นเป็นปัญหาวิทยาศาสตร์คอมพิวเตอร์ขนาดใหญ่ คำตอบที่ยอมรับแก้ปัญหานี้ด้วยความซับซ้อนแบบทวีคูณ (2 ^ NN = len (รายการ)) แก้ปัญหา (หรือพิสูจน์ว่าคุณทำไม่ได้) ในเวลาพหุนาม :) ดู "ปัญหาพนักงานขายที่เดินทาง"
FlipMcF

37
@FlipMcF มันจะเป็นเรื่องยากที่จะ "แก้ปัญหา" ในเวลาพหุนามเนื่องจากมันต้องใช้เวลาแฟกทอเรียลในการระบุเอาต์พุต ... ดังนั้นไม่มันเป็นไปไม่ได้
โทมัส

คำตอบ:


488

เริ่มต้นกับงูหลาม 2.6 (และถ้าคุณอยู่ในหลาม 3) คุณมีมาตรฐานห้องสมุดitertools.permutationsเครื่องมือสำหรับการนี้:

import itertools
list(itertools.permutations([1, 2, 3]))

หากคุณกำลังใช้เก่าหลาม (<2.6)ด้วยเหตุผลบางอย่างหรือแค่อยากรู้ว่าวิธีการทำงานที่นี่เป็นหนึ่งในวิธีที่ดีที่นำมาจาก http://code.activestate.com/recipes/252178/ :

def all_perms(elements):
    if len(elements) <=1:
        yield elements
    else:
        for perm in all_perms(elements[1:]):
            for i in range(len(elements)):
                # nb elements[0:1] works in both string and list contexts
                yield perm[:i] + elements[0:1] + perm[i:]

itertools.permutationsคู่ของวิธีการทางเลือกที่มีการระบุไว้ในเอกสารประกอบของ นี่คือหนึ่ง:

def permutations(iterable, r=None):
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = range(n)
    cycles = range(n, n-r, -1)
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return

และอีกอย่างขึ้นอยู่กับitertools.product:

def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)

14
และการแก้ปัญหานี้ recursive อื่น ๆ ที่มีอันตรายที่อาจเกิดจากการกินขึ้นแรมถ้ารายการ permutated เป็นพอใหญ่
บอริส Gorelik

3
พวกเขายังไปถึงขีด จำกัด การเรียกซ้ำ (และตาย) ด้วยรายการขนาดใหญ่
dbr

58
bgbg, dbr: มันใช้เครื่องกำเนิดไฟฟ้าดังนั้นฟังก์ชั่นของมันเองจะไม่กินความทรงจำ เหลือไว้สำหรับคุณเกี่ยวกับวิธีใช้ตัววนซ้ำที่ส่งคืนโดย all_perms (กล่าวว่าคุณสามารถเขียนการวนซ้ำแต่ละครั้งลงในดิสก์และไม่ต้องกังวลเกี่ยวกับหน่วยความจำ) ฉันรู้ว่าโพสต์นี้เก่า แต่ฉันเขียนข้อความนี้เพื่อประโยชน์ของทุกคนที่อ่านตอนนี้ นอกจากนี้วิธีที่ดีที่สุดคือใช้ itertools.permutations () ตามที่หลายคนชี้
Jagtesh Chadha

18
ไม่ได้เป็นเพียงกำเนิด มันใช้เครื่องกำเนิดไฟฟ้าแบบซ้อนกันซึ่งแต่ละอันให้ผลผลิตก่อนหน้านี้กับ call stack ในกรณีที่ไม่ชัดเจน ใช้หน่วยความจำ O (n) ซึ่งดี
cdunn2001

1
PS: ผมคงมันกับแทนfor i in range(len(elements)) for i in range(len(elements)+1)ในความเป็นจริงองค์ประกอบที่แยกออกelements[0:1]สามารถอยู่ในตำแหน่งที่แตกต่างในผลที่ได้ไม่ได้len(elements) len(elements)+1
Eric O Lebigot

339

และในPython 2.6เป็นต้นไป:

import itertools
itertools.permutations([1,2,3])

(คืนค่าเป็นตัวสร้างใช้list(permutations(l))เพื่อส่งคืนเป็นรายการ)


15
ทำงานใน Python 3 ด้วย
เมื่อ

10
โปรดสังเกตว่ามีrพารามิเตอร์อยู่เช่นitertools.permutations([1,2,3], r=2)ซึ่งจะสร้างการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดโดยเลือก 2 องค์ประกอบ:[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
toto_tico

277

รหัสต่อไปนี้ที่มี Python 2.6 ขึ้นไปเท่านั้น

ก่อนอื่นนำเข้าitertools:

import itertools

การเรียงสับเปลี่ยน (เรื่องสั่งซื้อ):

print list(itertools.permutations([1,2,3,4], 2))
[(1, 2), (1, 3), (1, 4),
(2, 1), (2, 3), (2, 4),
(3, 1), (3, 2), (3, 4),
(4, 1), (4, 2), (4, 3)]

การรวมกัน (คำสั่งไม่สำคัญ):

print list(itertools.combinations('123', 2))
[('1', '2'), ('1', '3'), ('2', '3')]

ผลิตภัณฑ์คาร์ทีเซียน (มีหลาย iterables):

print list(itertools.product([1,2,3], [4,5,6]))
[(1, 4), (1, 5), (1, 6),
(2, 4), (2, 5), (2, 6),
(3, 4), (3, 5), (3, 6)]

ผลิตภัณฑ์คาร์ทีเซียน (มีหนึ่งซ้ำและตัวเอง):

print list(itertools.product([1,2], repeat=3))
[(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2),
(2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)]


`รายการพิมพ์ (itertools.permutations ([1,2,3,4], 2)) ^` SyntaxError: ไวยากรณ์ที่ไม่ถูกต้อง 'เพิ่งเริ่มใช้ VS Code ฉันทำอะไรผิด ตัวชี้ชี้ใต้ "t" ของ "list"
gus

39
def permutations(head, tail=''):
    if len(head) == 0: print tail
    else:
        for i in range(len(head)):
            permutations(head[0:i] + head[i+1:], tail+head[i])

เรียกว่า:

permutations('abc')

เหตุใดจึงพิมพ์ tail แล้วส่งคืน None ทำไมไม่กลับหางแทนล่ะ ทำไมไม่คืนสิ่งใดอีกล่ะ?
bugmenot123

30
#!/usr/bin/env python

def perm(a, k=0):
   if k == len(a):
      print a
   else:
      for i in xrange(k, len(a)):
         a[k], a[i] = a[i] ,a[k]
         perm(a, k+1)
         a[k], a[i] = a[i], a[k]

perm([1,2,3])

เอาท์พุท:

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

ในขณะที่ฉันกำลังแลกเปลี่ยนเนื้อหาของรายการจึงจำเป็นต้องมีประเภทลำดับที่ไม่แน่นอนเป็นอินพุต เช่นperm(list("ball"))จะใช้งานได้และperm("ball")จะไม่เป็นเพราะคุณไม่สามารถเปลี่ยนสตริงได้

การดำเนินการนี้หลามเป็นแรงบันดาลใจขั้นตอนวิธีการที่นำเสนอในหนังสือคอมพิวเตอร์อัลกอริทึมโดยฮอ Sahni และ Rajasekeran


ฉันถือว่า k คือความยาวหรือการเรียงสับเปลี่ยน สำหรับ k = 2 เอาต์พุต [1, 2, 3] ไม่ควรจะเป็น (1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3, 2)?
Konstantinos Monachopoulos

k คือดัชนีขององค์ประกอบที่คุณต้องการแลกเปลี่ยน
sf8193

22

โซลูชันนี้ใช้ตัวกำเนิดเพื่อหลีกเลี่ยงการเก็บรักษาพีชคณิตทั้งหมดในหน่วยความจำ:

def permutations (orig_list):
    if not isinstance(orig_list, list):
        orig_list = list(orig_list)

    yield orig_list

    if len(orig_list) == 1:
        return

    for n in sorted(orig_list):
        new_list = orig_list[:]
        pos = new_list.index(n)
        del(new_list[pos])
        new_list.insert(0, n)
        for resto in permutations(new_list[1:]):
            if new_list[:1] + resto <> orig_list:
                yield new_list[:1] + resto

16

ในสไตล์การใช้งาน

def addperm(x,l):
    return [ l[0:i] + [x] + l[i:]  for i in range(len(l)+1) ]

def perm(l):
    if len(l) == 0:
        return [[]]
    return [x for y in perm(l[1:]) for x in addperm(l[0],y) ]

print perm([ i for i in range(3)])

ผลลัพธ์:

[[0, 1, 2], [1, 0, 2], [1, 2, 0], [0, 2, 1], [2, 0, 1], [2, 1, 0]]

15

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

def permute_in_place(a):
    a.sort()
    yield list(a)

    if len(a) <= 1:
        return

    first = 0
    last = len(a)
    while 1:
        i = last - 1

        while 1:
            i = i - 1
            if a[i] < a[i+1]:
                j = last - 1
                while not (a[i] < a[j]):
                    j = j - 1
                a[i], a[j] = a[j], a[i] # swap the values
                r = a[i+1:last]
                r.reverse()
                a[i+1:last] = r
                yield list(a)
                break
            if i == first:
                a.reverse()
                return

if __name__ == '__main__':
    for n in range(5):
        for a in permute_in_place(range(1, n+1)):
            print a
        print

    for a in permute_in_place([0, 0, 1, 1, 1]):
        print a
    print

15

วิธีที่ค่อนข้างชัดเจนในความคิดของฉันอาจจะยัง:

def permutList(l):
    if not l:
            return [[]]
    res = []
    for e in l:
            temp = l[:]
            temp.remove(e)
            res.extend([[e] + r for r in permutList(temp)])

    return res

11
list2Perm = [1, 2.0, 'three']
listPerm = [[a, b, c]
            for a in list2Perm
            for b in list2Perm
            for c in list2Perm
            if ( a != b and b != c and a != c )
            ]
print listPerm

เอาท์พุท:

[
    [1, 2.0, 'three'], 
    [1, 'three', 2.0], 
    [2.0, 1, 'three'], 
    [2.0, 'three', 1], 
    ['three', 1, 2.0], 
    ['three', 2.0, 1]
]

2
ในขณะที่มันสร้างผลลัพธ์ที่ต้องการทางเทคนิคคุณกำลังแก้ไขบางสิ่งที่อาจเป็น O (n lg n) ใน O (n ^ n) - ไม่มีประสิทธิภาพ "เล็กน้อย" สำหรับชุดใหญ่
James

3
@James: ฉันสับสนเล็กน้อยโดย O (n log n) ที่คุณให้: จำนวนการเรียงสับเปลี่ยนคือ n !, ซึ่งใหญ่กว่า O (n log n) อยู่แล้วมาก ดังนั้นฉันไม่สามารถเห็นวิธีการแก้ปัญหาอาจเป็น O (n log n) อย่างไรก็ตามมันเป็นความจริงว่าวิธีนี้อยู่ใน O (n ^ n) ซึ่งมีขนาดใหญ่กว่า n! เท่าที่ชัดเจนจากการประมาณของ Stirling
Eric O Lebigot

9

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

from math import factorial
def permutations(l):
    permutations=[]
    length=len(l)
    for x in xrange(factorial(length)):
        available=list(l)
        newPermutation=[]
        for radix in xrange(length, 0, -1):
            placeValue=factorial(radix-1)
            index=x/placeValue
            newPermutation.append(available.pop(index))
            x-=index*placeValue
        permutations.append(newPermutation)
    return permutations

permutations(range(3))

เอาท์พุท:

[[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]

วิธีนี้ไม่ใช่แบบเรียกซ้ำ แต่มันช้าลงเล็กน้อยบนคอมพิวเตอร์ของฉันและ xrange ทำให้เกิดข้อผิดพลาดเมื่อ n! ใหญ่เกินไปที่จะแปลงเป็นจำนวนเต็ม C ที่ยาว (n = 13 สำหรับฉัน) มันก็เพียงพอแล้วเมื่อฉันต้องการมัน แต่มันก็ไม่ได้โง่เงียบโดยการยิงยาว


3
สวัสดียินดีต้อนรับสู่ Stack Overflow แม้ว่าการโพสต์วิธีการเดรัจฉานบังคับมีข้อดี แต่ถ้าคุณไม่คิดว่าโซลูชันของคุณดีกว่าโซลูชันที่ยอมรับคุณอาจไม่ควรโพสต์ (โดยเฉพาะคำถามเก่าที่มีคำตอบอยู่แล้ว)
Hannele

1
จริง ๆ แล้วฉันกำลังมองหาวิธีการที่ไม่ใช่ห้องสมุดที่โหดร้ายดังนั้นขอบคุณ!
Jay Taylor

8

โปรดทราบว่าอัลกอริทึมนี้มีn factorialความซับซ้อนของเวลาโดยที่nความยาวของรายการอินพุต

พิมพ์ผลการเรียกใช้:

global result
result = [] 

def permutation(li):
if li == [] or li == None:
    return

if len(li) == 1:
    result.append(li[0])
    print result
    result.pop()
    return

for i in range(0,len(li)):
    result.append(li[i])
    permutation(li[:i] + li[i+1:])
    result.pop()    

ตัวอย่าง:

permutation([1,2,3])

เอาท์พุท:

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

8

เราสามารถย้ำผ่านองค์ประกอบแรกของการเปลี่ยนลำดับแต่ละครั้งได้เช่นเดียวกับคำตอบของ tzwenn อย่างไรก็ตามมีประสิทธิภาพมากขึ้นในการเขียนวิธีนี้:

def all_perms(elements):
    if len(elements) <= 1:
        yield elements  # Only permutation possible = no permutation
    else:
        # Iteration over the first element in the result permutation:
        for (index, first_elmt) in enumerate(elements):
            other_elmts = elements[:index]+elements[index+1:]
            for permutation in all_perms(other_elmts): 
                yield [first_elmt] + permutation

การแก้ปัญหานี้เป็นเรื่องเกี่ยวกับ 30% เร็วขึ้นเห็นได้ชัดว่าต้องขอบคุณ recursion สิ้นสุดที่แทนlen(elements) <= 1 0นอกจากนี้ยังมีหน่วยความจำที่มีประสิทธิภาพมากขึ้นเนื่องจากใช้ฟังก์ชันตัวสร้าง (ผ่านyield) เช่นเดียวกับในโซลูชันของ Riccardo Reyes


6

นี่คือแรงบันดาลใจจากการใช้งาน Haskell โดยใช้ความเข้าใจในรายการ:

def permutation(list):
    if len(list) == 0:
        return [[]]
    else:
        return [[x] + ys for x in list for ys in permutation(delete(list, x))]

def delete(list, item):
    lc = list[:]
    lc.remove(item)
    return lc

6

การใช้งานปกติ (ไม่มีผลตอบแทน - จะทำทุกอย่างในหน่วยความจำ):

def getPermutations(array):
    if len(array) == 1:
        return [array]
    permutations = []
    for i in range(len(array)): 
        # get all perm's of subarray w/o current item
        perms = getPermutations(array[:i] + array[i+1:])  
        for p in perms:
            permutations.append([array[i], *p])
    return permutations

การใช้งานผลผลิต:

def getPermutations(array):
    if len(array) == 1:
        yield array
    else:
        for i in range(len(array)):
            perms = getPermutations(array[:i] + array[i+1:])
            for p in perms:
                yield [array[i], *p]

แนวคิดพื้นฐานคือการดูองค์ประกอบทั้งหมดในอาร์เรย์สำหรับตำแหน่งที่ 1 และจากนั้นในตำแหน่งที่ 2 จะครอบคลุมองค์ประกอบที่เหลือทั้งหมดโดยไม่มีองค์ประกอบที่เลือกสำหรับที่ 1 ฯลฯ คุณสามารถทำสิ่งนี้ด้วยการเรียกซ้ำโดยที่ เกณฑ์การหยุดได้รับไปยังอาร์เรย์ของ 1 องค์ประกอบ - ซึ่งในกรณีที่คุณส่งคืนอาร์เรย์นั้น

ป้อนคำอธิบายรูปภาพที่นี่


สิ่งนี้ใช้ไม่ได้กับฉัน _> ValueError: ตัวถูกดำเนินการไม่สามารถออกอากาศพร้อมกับรูปร่าง (0,) (2,) , สำหรับบรรทัดนี้:perms = getPermutations(array[:i] + array[i+1:])
RK1

@ RK1 อินพุตคืออะไร?
David Refaeli

ฉันผ่านnumpyแถวลำดับ _> getPermutations(np.array([1, 2, 3]))ฉันเห็นว่ามันใช้งานได้กับรายการเพิ่งสับสนเนื่องจาก func arg คือarray:)
RK1

@ RK1 ดีใจที่ได้ผลเป็นรายการ :-) เป็นคำหลักใน python ดังนั้นจึงไม่ใช่ความคิดที่ดีที่จะเรียกคำหลักในพารามิเตอร์ของคุณเนื่องจากมันจะ "เงา" ดังนั้นฉันจึงใช้อาร์เรย์คำเนื่องจากนี่คือฟังก์ชันการทำงานที่แท้จริงของรายการที่ฉันกำลังใช้ - อาร์เรย์ของพวกเขาอย่างที่ชอบ ฉันเดาว่าถ้าฉันจะเขียนเอกสารฉันจะชี้แจง นอกจากนี้ฉันเชื่อว่าคำถาม "สัมภาษณ์" พื้นฐานควรได้รับการแก้ไขหากไม่มีแพ็คเกจภายนอกเช่นคำถาม
David Refaeli

ฮ่าฮ่าเป็นเรื่องจริงใช่แล้วก็พยายามใช้ด้วยnumbaและโลภด้วยความเร็วดังนั้นพยายามใช้มันเฉพาะกับnumpyอาร์เรย์
RK1

4

สำหรับประสิทธิภาพโซลูชันที่มีแรงบันดาลใจจากKnuth (p22):

from numpy import empty, uint8
from math import factorial

def perms(n):
    f = 1
    p = empty((2*n-1, factorial(n)), uint8)
    for i in range(n):
        p[i, :f] = i
        p[i+1:2*i+1, :f] = p[:i, :f]  # constitution de blocs
        for j in range(i):
            p[:i+1, f*(j+1):f*(j+2)] = p[j+1:j+i+2, :f]  # copie de blocs
        f = f*(i+1)
    return p[:n, :]

การคัดลอกหน่วยความจำขนาดใหญ่ช่วยประหยัดเวลา - เร็วกว่าlist(itertools.permutations(range(n)): 20x :

In [1]: %timeit -n10 list(permutations(range(10)))
10 loops, best of 3: 815 ms per loop

In [2]: %timeit -n100 perms(10) 
100 loops, best of 3: 40 ms per loop

3
from __future__ import print_function

def perm(n):
    p = []
    for i in range(0,n+1):
        p.append(i)
    while True:
        for i in range(1,n+1):
            print(p[i], end=' ')
        print("")
        i = n - 1
        found = 0
        while (not found and i>0):
            if p[i]<p[i+1]:
                found = 1
            else:
                i = i - 1
        k = n
        while p[i]>p[k]:
            k = k - 1
        aux = p[i]
        p[i] = p[k]
        p[k] = aux
        for j in range(1,(n-i)/2+1):
            aux = p[i+j]
            p[i+j] = p[n-j+1]
            p[n-j+1] = aux
        if not found:
            break

perm(5)

3

นี่คือขั้นตอนวิธีการที่ทำงานในรายการโดยไม่ต้องสร้างรายการใหม่กลางคล้ายกับวิธีการแก้ปัญหาที่เบอร์https://stackoverflow.com/a/108651/184528

def permute(xs, low=0):
    if low + 1 >= len(xs):
        yield xs
    else:
        for p in permute(xs, low + 1):
            yield p        
        for i in range(low + 1, len(xs)):        
            xs[low], xs[i] = xs[i], xs[low]
            for p in permute(xs, low + 1):
                yield p        
            xs[low], xs[i] = xs[i], xs[low]

for p in permute([1, 2, 3, 4]):
    print p

คุณสามารถลองใช้โค้ดด้วยตัวเองได้ที่นี่: http://repl.it/J9v


3

ความงามของการเรียกซ้ำ:

>>> import copy
>>> def perm(prefix,rest):
...      for e in rest:
...              new_rest=copy.copy(rest)
...              new_prefix=copy.copy(prefix)
...              new_prefix.append(e)
...              new_rest.remove(e)
...              if len(new_rest) == 0:
...                      print new_prefix + new_rest
...                      continue
...              perm(new_prefix,new_rest)
... 
>>> perm([],['a','b','c','d'])
['a', 'b', 'c', 'd']
['a', 'b', 'd', 'c']
['a', 'c', 'b', 'd']
['a', 'c', 'd', 'b']
['a', 'd', 'b', 'c']
['a', 'd', 'c', 'b']
['b', 'a', 'c', 'd']
['b', 'a', 'd', 'c']
['b', 'c', 'a', 'd']
['b', 'c', 'd', 'a']
['b', 'd', 'a', 'c']
['b', 'd', 'c', 'a']
['c', 'a', 'b', 'd']
['c', 'a', 'd', 'b']
['c', 'b', 'a', 'd']
['c', 'b', 'd', 'a']
['c', 'd', 'a', 'b']
['c', 'd', 'b', 'a']
['d', 'a', 'b', 'c']
['d', 'a', 'c', 'b']
['d', 'b', 'a', 'c']
['d', 'b', 'c', 'a']
['d', 'c', 'a', 'b']
['d', 'c', 'b', 'a']

3

อัลกอริธึมนี้เป็นวิธีที่มีประสิทธิภาพมากที่สุดโดยหลีกเลี่ยงการผ่านอาร์เรย์และการจัดการในการเรียกซ้ำการทำงานใน Python 2, 3:

def permute(items):
    length = len(items)
    def inner(ix=[]):
        do_yield = len(ix) == length - 1
        for i in range(0, length):
            if i in ix: #avoid duplicates
                continue
            if do_yield:
                yield tuple([items[y] for y in ix + [i]])
            else:
                for p in inner(ix + [i]):
                    yield p
    return inner()

การใช้งาน:

for p in permute((1,2,3)):
    print(p)

(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)

3
def pzip(c, seq):
    result = []
    for item in seq:
        for i in range(len(item)+1):
            result.append(item[i:]+c+item[:i])
    return result


def perm(line):
    seq = [c for c in line]
    if len(seq) <=1 :
        return seq
    else:
        return pzip(seq[0], perm(seq[1:]))

3

อีกวิธีการ (ไม่ต้อง libs)

def permutation(input):
    if len(input) == 1:
        return input if isinstance(input, list) else [input]

    result = []
    for i in range(len(input)):
        first = input[i]
        rest = input[:i] + input[i + 1:]
        rest_permutation = permutation(rest)
        for p in rest_permutation:
            result.append(first + p)
    return result

อินพุตสามารถเป็นสตริงหรือรายการ

print(permutation('abcd'))
print(permutation(['a', 'b', 'c', 'd']))

วิธีนี้ใช้ไม่ได้กับรายการที่มีจำนวนเต็มเช่น [1, 2, 3]ผลตอบแทน[6, 6, 6, 6, 6, 6]
RK1

@ RK1 คุณสามารถลองได้print(permutation(['1','2','3']))
Tatsu

ขอบคุณที่ใช้งานได้
RK1

3

คำเตือน: ปลั๊กไม่มีรูปแบบโดยผู้เขียนแพคเกจ :)

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

ไม่ว่าในกรณีใดในการสร้างรายการการเรียงลำดับเราสามารถทำสิ่งต่อไปนี้

import trotter

my_permutations = trotter.Permutations(3, [1, 2, 3])

print(my_permutations)

for p in my_permutations:
    print(p)

เอาท์พุท:

รายการเทียมที่มี 6 การเปลี่ยนแปลง 3 แบบคือ [1, 2, 3]
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]

2

สร้างวิธีเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมด

ฉันใช้ python3.4:

def calcperm(arr, size):
    result = set([()])
    for dummy_idx in range(size):
        temp = set()
        for dummy_lst in result:
            for dummy_outcome in arr:
                if dummy_outcome not in dummy_lst:
                    new_seq = list(dummy_lst)
                    new_seq.append(dummy_outcome)
                    temp.add(tuple(new_seq))
        result = temp
    return result

กรณีทดสอบ:

lst = [1, 2, 3, 4]
#lst = ["yellow", "magenta", "white", "blue"]
seq = 2
final = calcperm(lst, seq)
print(len(final))
print(final)

2

เพื่อช่วยให้คุณประหยัดเวลาในการค้นหาและทดลองต่อไปนี้เป็นวิธีการเปลี่ยนรูปแบบไม่ซ้ำแบบเรียกซ้ำใน Python ซึ่งทำงานร่วมกับ Numba (เช่น v. 0.41):

@numba.njit()
def permutations(A, k):
    r = [[i for i in range(0)]]
    for i in range(k):
        r = [[a] + b for a in A for b in r if (a in b)==False]
    return r
permutations([1,2,3],3)
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

วิธีสร้างความประทับใจเกี่ยวกับประสิทธิภาพ:

%timeit permutations(np.arange(5),5)

243 µs ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
time: 406 ms

%timeit list(itertools.permutations(np.arange(5),5))
15.9 µs ± 8.61 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
time: 12.9 s

ดังนั้นให้ใช้เวอร์ชั่นนี้เฉพาะในกรณีที่คุณต้องเรียกใช้จากฟังก์ชั่น njitted มิฉะนั้นจะชอบการใช้งาน itertools


1

ฉันเห็นการวนซ้ำจำนวนมากเกิดขึ้นภายในฟังก์ชันการเรียกซ้ำเหล่านี้ไม่ใช่การเรียกซ้ำที่แท้จริง ...

ดังนั้นสำหรับพวกคุณที่ไม่สามารถแม้แต่จะวนซ้ำนี่เป็นวิธีแก้ปัญหาแบบวนซ้ำโดยไม่จำเป็นทั้งหมด

def all_insert(x, e, i=0):
    return [x[0:i]+[e]+x[i:]] + all_insert(x,e,i+1) if i<len(x)+1 else []

def for_each(X, e):
    return all_insert(X[0], e) + for_each(X[1:],e) if X else []

def permute(x):
    return [x] if len(x) < 2 else for_each( permute(x[1:]) , x[0])


perms = permute([1,2,3])

1

ทางออกอื่น:

def permutation(flag, k =1 ):
    N = len(flag)
    for i in xrange(0, N):
        if flag[i] != 0:
            continue
        flag[i] = k 
        if k == N:
            print flag
        permutation(flag, k+1)
        flag[i] = 0

permutation([0, 0, 0])

0

My Python Solution:

def permutes(input,offset):
    if( len(input) == offset ):
        return [''.join(input)]

    result=[]        
    for i in range( offset, len(input) ):
         input[offset], input[i] = input[i], input[offset]
         result = result + permutes(input,offset+1)
         input[offset], input[i] = input[i], input[offset]
    return result

# input is a "string"
# return value is a list of strings
def permutations(input):
    return permutes( list(input), 0 )

# Main Program
print( permutations("wxyz") )

0
def permutation(word, first_char=None):
    if word == None or len(word) == 0: return []
    if len(word) == 1: return [word]

    result = []
    first_char = word[0]
    for sub_word in permutation(word[1:], first_char):
        result += insert(first_char, sub_word)
    return sorted(result)

def insert(ch, sub_word):
    arr = [ch + sub_word]
    for i in range(len(sub_word)):
        arr.append(sub_word[i:] + ch + sub_word[:i])
    return arr


assert permutation(None) == []
assert permutation('') == []
assert permutation('1')  == ['1']
assert permutation('12') == ['12', '21']

print permutation('abc')

ผลลัพธ์: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']


0

การใช้ Counter

from collections import Counter

def permutations(nums):
    ans = [[]]
    cache = Counter(nums)

    for idx, x in enumerate(nums):
        result = []
        for items in ans:
            cache1 = Counter(items)
            for id, n in enumerate(nums):
                if cache[n] != cache1[n] and items + [n] not in result:
                    result.append(items + [n])

        ans = result
    return ans
permutations([1, 2, 2])
> [[1, 2, 2], [2, 1, 2], [2, 2, 1]]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.