อัลกอริทึมเพื่อสร้างการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดของรายการ?


119

สมมติว่าฉันมีรายการขององค์ประกอบ n ฉันรู้ว่ามี n! วิธีที่เป็นไปได้ในการจัดลำดับองค์ประกอบเหล่านี้ อัลกอริทึมในการสร้างลำดับที่เป็นไปได้ทั้งหมดของรายการนี้คืออะไร? ตัวอย่างเช่นฉันมีรายการ [a, b, c] อัลกอริทึมจะส่งคืน [[a, b, c], [a, c, b,], [b, a, c], [b, c, a], [c, a, b], [c, b , ก]].

ฉันกำลังอ่านที่นี่ http://en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations

แต่วิกิพีเดียไม่เคยอธิบายได้ดี ฉันไม่เข้าใจมันมากนัก


5
ฉันเขียนคำตอบที่ครอบคลุมสำหรับคำถามอื่นเกี่ยวกับการสร้างการเรียงสับเปลี่ยนหนึ่งครั้ง ฉันคิดว่ามันน่าสนใจสำหรับคุณ: stackoverflow.com/questions/1506078/…
Joren

2
วิธีนี้สามารถแก้ปัญหาของคุณได้en.wikipedia.org/wiki/Heap's_algorithm
Felix

คำตอบ:


96

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

ดังนั้นด้วยรายการ [1,2,3,4] การเรียงสับเปลี่ยนทั้งหมดที่ขึ้นต้นด้วย 1 จะถูกสร้างขึ้นจากนั้นการเรียงสับเปลี่ยนทั้งหมดที่เริ่มต้นด้วย 2 แล้ว 3 ตามด้วย 4

วิธีนี้ช่วยลดปัญหาได้อย่างมีประสิทธิภาพจากหนึ่งในการค้นหาการเรียงสับเปลี่ยนของรายการสี่รายการไปจนถึงรายการสามรายการ หลังจากลดเป็น 2 แล้วก็จะพบ 1 รายการทั้งหมด
ตัวอย่างแสดงการเรียงสับเปลี่ยนกระบวนการโดยใช้ลูกบอลสี 3 ลูก:
ลูกบอลสีแดงเขียวและน้ำเงินเรียงลำดับภาพ(จากhttps://en.wikipedia.org/wiki/Permutation#/media/File:Permutations_RGB.svg - https://commons.wikimedia.org/wiki/File:Permutations_RGB svg )


2
ตอนแรกฉันก็คิดเกี่ยวกับเรื่องนี้เหมือนกัน แต่องค์ประกอบปัจจุบันจะไม่อยู่ในระหว่างบางส่วนต่อไปนี้ ดังนั้นจะไม่สร้างการเรียงสับเปลี่ยนทั้งหมด
fent

@Ler ขออภัยอัปเดตคำตอบของฉันจาก "folllowing" เป็น "ที่เหลือ" เพื่อชี้แจง มันใช้งานได้ดี ตรวจสอบโดยเขียนรหัสและยืนยันว่าคุณได้รับ 4! ผลลัพธ์ที่แตกต่างกัน
WhirlWind

2
การเรียงสับเปลี่ยน int (int n เวกเตอร์ <int> a) {คงที่ int num_permutations = 0; ถ้า (n == (a.size () - 1)) {สำหรับ (int i = 0; i <a.size (); i ++) cout << a [i] << ""; ศาล << "\ n"; num_permutations ++; } else {สำหรับ (int i = n + 1; i <= a.size (); i ++) {การเรียงสับเปลี่ยน (n + 1, a); ถ้า (i <a.size ()) int temp = a [n], a [n] = a [i], a [i] = temp; }} คืนค่า num_permutations; } int main (โมฆะ) {vector <int> v; v.push_back (1); ... กลับเรียงสับเปลี่ยน (0, v); }
Somesh

อ๊ะ - ไม่แน่ใจว่าจะจัดรูปแบบโค้ดในความคิดเห็นอย่างไร ... ทดสอบรหัสด้วย 7 และได้รับ 5040 ขอบคุณ @WhirlWind สำหรับคำแนะนำ
Somesh

สิ่งนี้จะไม่เปลี่ยนแปลงหากคุณสามารถมีลูกบอลสีแดง # 1 2 หรือ 3 แทนที่จะมีเพียง 1 สีในแต่ละสี?
Alexander Mills

26

นี่คืออัลกอริทึมใน Python ที่ทำงานร่วมกับอาร์เรย์:

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


คุณช่วยอธิบายส่วนที่ให้ผลผลิตได้หรือไม่? ฉันไม่สามารถทำให้โค้ดแห้งได้ ขอบคุณล่วงหน้า.
Agniswar Bakshi

คำถาม Stack Overflow ที่stackoverflow.com/questions/104420/…ระบุว่ามีโมดูลไลบรารีมาตรฐานในเวอร์ชัน 2.6 เป็นต้นไปและมีคำตอบที่ให้โซลูชัน 6 บรรทัดในฟังก์ชันเพื่อรับการเรียงลำดับรายการ
Edward

@Agniswar โดยสรุปคำสั่งผลตอบแทนจะใช้เพื่อกำหนดเครื่องกำเนิดไฟฟ้าแทนที่การส่งคืนฟังก์ชันเพื่อให้ผลลัพธ์แก่ผู้เรียกโดยไม่ทำลายตัวแปรภายในเครื่อง ต่างจากฟังก์ชันที่ในการเรียกแต่ละครั้งจะเริ่มต้นด้วยชุดตัวแปรใหม่เครื่องกำเนิดไฟฟ้าจะกลับมาดำเนินการต่อจากที่ค้างไว้ pythoncentral.io/python-generators-and-yield-keyword
MSS

โซลูชันนี้จะไม่ทำงานเมื่อจัดการรายการที่เหมือนกัน
KaiserKatze

ขอบคุณสำหรับการแบ่งปัน. สิ่งนี้ใช้งานง่ายและมีประสิทธิภาพแม้ว่าผลลัพธ์จะไม่อยู่ในลำดับศัพท์
แซม

16

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

หลังจากไตร่ตรองเกี่ยวกับปัญหาแล้วฉันก็ได้ข้อสรุปสองประการดังต่อไปนี้:

  1. สำหรับรายการLขนาดnจะมีจำนวนคำตอบที่เท่ากันเริ่มต้นด้วย L 1 , L 2 ... L nองค์ประกอบของรายการ เนื่องจากมีn!การเรียงสับเปลี่ยนของรายการขนาดnทั้งหมดเราจึงได้รับn! / n = (n-1)!การเรียงสับเปลี่ยนในแต่ละกลุ่ม
  2. รายการที่ 2 องค์ประกอบที่มีเพียง 2 พีชคณิต => และ[a,b][b,a]

การใช้แนวคิดง่ายๆทั้งสองนี้ฉันได้รับอัลกอริทึมต่อไปนี้:

permute array
    if array is of size 2
       return first and second element as new array
       return second and first element as new array
    else
        for each element in array
            new subarray = array with excluded element
            return element + permute subarray

นี่คือวิธีที่ฉันใช้สิ่งนี้ใน C #:

public IEnumerable<List<T>> Permutate<T>(List<T> input)
{
    if (input.Count == 2) // this are permutations of array of size 2
    {
        yield return new List<T>(input);
        yield return new List<T> {input[1], input[0]}; 
    }
    else
    {
        foreach(T elem in input) // going through array
        {
            var rlist = new List<T>(input); // creating subarray = array
            rlist.Remove(elem); // removing element
            foreach(List<T> retlist in Permutate(rlist))
            {
                retlist.Insert(0,elem); // inserting the element at pos 0
                yield return retlist;
            }

        }
    }
}

16

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

ฉันเพิ่งเขียนการใช้งานอย่างรวดเร็วใน Java ของอัลกอริทึมของ Wikipedia เป็นการตรวจสอบและก็ไม่มีปัญหา แต่สิ่งที่คุณมีใน Q เป็นตัวอย่างไม่ใช่ "รายการการเรียงสับเปลี่ยนทั้งหมด" แต่เป็น "รายการการเรียงสับเปลี่ยนทั้งหมด" ดังนั้นวิกิพีเดียจะไม่ช่วยคุณได้มากนัก คุณต้องมีภาษาที่สามารถสร้างรายการการเรียงสับเปลี่ยนได้ และเชื่อฉันว่ารายการที่มีความยาวไม่กี่พันล้านมักไม่ได้รับการจัดการในภาษาที่จำเป็น คุณต้องการภาษาการเขียนโปรแกรมที่ใช้งานได้ไม่เข้มงวดซึ่งรายการเป็นวัตถุชั้นหนึ่งเพื่อกำจัดสิ่งต่าง ๆ ในขณะที่ไม่นำเครื่องเข้าใกล้ความร้อนของจักรวาล

ง่ายมาก ในภาษา Haskell มาตรฐานหรือภาษา FP สมัยใหม่:

-- perms of a list
perms :: [a] -> [ [a] ]
perms (a:as) = [bs ++ a:cs | perm <- perms as, (bs,cs) <- splits perm]
perms []     = [ [] ]

และ

-- ways of splitting a list into two parts
splits :: [a] -> [ ([a],[a]) ]
splits []     = [ ([],[]) ]
splits (a:as) = ([],a:as) : [(a:bs,cs) | (bs,cs) <- splits as]

9

ดังที่ WhirlWind กล่าวว่าคุณเริ่มที่จุดเริ่มต้น

คุณสลับเคอร์เซอร์กับค่าที่เหลือแต่ละค่ารวมทั้งเคอร์เซอร์เองด้วยนี่คืออินสแตนซ์ใหม่ทั้งหมด (ฉันใช้int[]และarray.clone()ในตัวอย่าง)

จากนั้นทำการเรียงสับเปลี่ยนในรายการต่างๆเหล่านี้ตรวจสอบให้แน่ใจว่าเคอร์เซอร์อยู่ทางขวา

เมื่อไม่มีค่าเหลือ (เคอร์เซอร์อยู่ที่ส่วนท้าย) ให้พิมพ์รายการ นี่คือเงื่อนไขการหยุด

public void permutate(int[] list, int pointer) {
    if (pointer == list.length) {
        //stop-condition: print or process number
        return;
    }
    for (int i = pointer; i < list.length; i++) {
        int[] permutation = (int[])list.clone();.
        permutation[pointer] = list[i];
        permutation[i] = list[pointer];
        permutate(permutation, pointer + 1);
    }
}

8

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

สำหรับตัวเลขขนาดเล็ก (3 หรือ 4 ซึ่งส่วนใหญ่จะพบ) หลาย ๆ ลูปนั้นค่อนข้างง่ายและตรงไปตรงมา เป็นคำตอบที่โชคร้ายกับลูปไม่ได้รับการโหวต

เริ่มจากการแจงนับ (แทนที่จะเป็นการเรียงสับเปลี่ยน) เพียงแค่อ่านรหัสเป็นรหัส pseudo perl

$foreach $i1 in @list
    $foreach $i2 in @list 
        $foreach $i3 in @list
            print "$i1, $i2, $i3\n"

การแจงนับมักจะพบมากกว่าการเรียงสับเปลี่ยน แต่หากจำเป็นต้องมีการเรียงสับเปลี่ยนให้เพิ่มเงื่อนไข:

$foreach $i1 in @list
    $foreach $i2 in @list 
        $if $i2==$i1
            next
        $foreach $i3 in @list
            $if $i3==$i1 or $i3==$i2
                next
            print "$i1, $i2, $i3\n"

ตอนนี้หากคุณต้องการวิธีการทั่วไปที่อาจเกิดขึ้นกับรายการใหญ่ ๆ เราสามารถใช้วิธี radix ได้ ขั้นแรกให้พิจารณาปัญหาการแจงนับ:

$n=@list
my @radix
$for $i=0:$n
    $radix[$i]=0
$while 1
    my @temp
    $for $i=0:$n
        push @temp, $list[$radix[$i]]
    print join(", ", @temp), "\n"
    $call radix_increment

subcode: radix_increment
    $i=0
    $while 1
        $radix[$i]++
        $if $radix[$i]==$n
            $radix[$i]=0
            $i++
        $else
            last
    $if $i>=$n
        last

การเพิ่มขึ้นของ Radix คือการนับจำนวนเป็นหลัก (ในฐานของจำนวนองค์ประกอบรายการ)

ตอนนี้หากคุณต้องการ Permutaion เพียงเพิ่มการตรวจสอบภายในลูป:

subcode: check_permutation
    my @check
    my $flag_dup=0
    $for $i=0:$n
        $check[$radix[$i]]++
        $if $check[$radix[$i]]>1
            $flag_dup=1
            last
    $if $flag_dup
        next

แก้ไข: โค้ดด้านบนควรใช้งานได้ แต่สำหรับการเปลี่ยนแปลง radix_increment อาจสิ้นเปลือง ดังนั้นหากเวลาเป็นปัญหาในทางปฏิบัติเราต้องเปลี่ยน radix_increment เป็น permute_inc:

subcode: permute_init
    $for $i=0:$n
        $radix[$i]=$i

subcode: permute_inc                                       
    $max=-1                                                
    $for $i=$n:0                                           
        $if $max<$radix[$i]                                
            $max=$radix[$i]                                
        $else                                              
            $for $j=$n:0                                   
                $if $radix[$j]>$radix[$i]                  
                    $call swap, $radix[$i], $radix[$j]     
                    break                                  
            $j=$i+1                                        
            $k=$n-1                                        
            $while $j<$k                                   
                $call swap, $radix[$j], $radix[$k]         
                $j++                                       
                $k--                                       
            break                                          
    $if $i<0                                               
        break                                              

แน่นอนว่าตอนนี้รหัสนี้ซับซ้อนขึ้นอย่างมีเหตุผลฉันจะปล่อยให้ผู้อ่านออกกำลังกาย


7

ใส่คำอธิบายภาพที่นี่

// C program to print all permutations with duplicates allowed
#include <stdio.h>
#include <string.h>

/* Function to swap values at two pointers */
void swap(char *x, char *y)
{
    char temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

/* Function to print permutations of string
   This function takes three parameters:
   1. String
   2. Starting index of the string
   3. Ending index of the string. */

void permute(char *a, int l, int r)
{
   int i;
   if (l == r)
     printf("%s\n", a);
   else
   {
       for (i = l; i <= r; i++)
       {
          swap((a+l), (a+i));
          permute(a, l+1, r);
          swap((a+l), (a+i)); //backtrack
       }
   }
}

/* Driver program to test above functions */
int main()
{
    char str[] = "ABC";
    int n = strlen(str);
    permute(str, 0, n-1);
    return 0;
}

อ้างอิง: Geeksforgeeks.org


5

หากใครสงสัยว่าต้องทำอย่างไรในการเรียงสับเปลี่ยนในจาวาสคริปต์

ความคิด / pseudocode

  1. เลือกทีละองค์ประกอบ
  2. เปลี่ยนองค์ประกอบที่เหลือจากนั้นเพิ่มองค์ประกอบที่เลือกลงในการเปลี่ยนแปลงทั้งหมด

ตัวอย่างเช่น. 'a' + permute (bc) Permute ของ bc จะเป็น bc & cb ตอนนี้เพิ่มสองตัวนี้จะให้ abc, acb ในทำนองเดียวกันเลือก b + permute (ac) จะ provice bac, bca ... และดำเนินการต่อไป

ตอนนี้ดูรหัส

function permutations(arr){

   var len = arr.length, 
       perms = [],
       rest,
       picked,
       restPerms,
       next;

    //for one or less item there is only one permutation 
    if (len <= 1)
        return [arr];

    for (var i=0; i<len; i++)
    {
        //copy original array to avoid changing it while picking elements
        rest = Object.create(arr);

        //splice removed element change array original array(copied array)
        //[1,2,3,4].splice(2,1) will return [3] and remaining array = [1,2,4]
        picked = rest.splice(i, 1);

        //get the permutation of the rest of the elements
        restPerms = permutations(rest);

       // Now concat like a+permute(bc) for each
       for (var j=0; j<restPerms.length; j++)
       {
           next = picked.concat(restPerms[j]);
           perms.push(next);
       }
    }

   return perms;
}

ใช้เวลาทำความเข้าใจเรื่องนี้ ฉันได้รับรหัสนี้จาก ( pertumation in JavaScript )


ฉันกำลังคิดถึงบางสิ่งที่คล้ายกัน แต่คุณไม่ควรเพิ่มองค์ประกอบที่เลือกไว้ทั้งด้านหน้าและด้านท้ายของ restParams หรือไม่? ในกรณีนี้สำหรับ "abc" หากคุณเลือก a การเรียงสับเปลี่ยน "bc" จะเป็น "bc" และ "cb" เมื่อคุณเพิ่ม 'a' กลับเข้าไปในรายการคุณไม่ควรเพิ่มไว้ข้างหน้าเป็น 'a + bc' + 'a + cb' แต่ต่อท้ายด้วย 'bc + a' + 'cb + a' ของ รายการ?
Artimus

คุณจะได้รับการเรียงสับเปลี่ยนเหล่านั้นเมื่อคุณกำหนดค่าเริ่มต้นด้วย 'b' และ 'c' ตามลำดับ (เช่นการวิ่งครั้งที่สองและสามของวงรอบนอก 'for')
Ryan O'Neill

3

อีกอันใน Python มันไม่ได้อยู่ในตำแหน่ง @ cdiggins แต่ฉันคิดว่ามันเข้าใจง่ายกว่า

def permute(num):
    if len(num) == 2:
        # get the permutations of the last 2 numbers by swapping them
        yield num
        num[0], num[1] = num[1], num[0]
        yield num
    else:
        for i in range(0, len(num)):
            # fix the first number and get the permutations of the rest of numbers
            for perm in permute(num[0:i] + num[i+1:len(num)]):
                yield [num[i]] + perm

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

3

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

รหัส:

#include <stdio.h>
#include <conio.h>
#include <malloc.h>

                //PROTOTYPES

int fact(int);                  //For finding the factorial
void swap(int*,int*);           //Swapping 2 given numbers
void sort(int*,int);            //Sorting the list from the specified path
int imax(int*,int,int);         //Finding the value of imax
int jsmall(int*,int);           //Gives position of element greater than ith but smaller than rest (ahead of imax)
void perm();                    //All the important tasks are done in this function


int n;                         //Global variable for input OR number of digits

void main()
{
int c=0;

printf("Enter the number : ");
scanf("%d",&c);
perm(c);
getch();
}

void perm(int c){
int *p;                     //Pointer for allocating separate memory to every single entered digit like arrays
int i, d;               
int sum=0;
int j, k;
long f;

n = 0;

while(c != 0)               //this one is for calculating the number of digits in the entered number
{
    sum = (sum * 10) + (c % 10);
    n++;                            //as i told at the start of loop
    c = c / 10;
}

f = fact(n);                        //It gives the factorial value of any number

p = (int*) malloc(n*sizeof(int));                //Dynamically allocation of array of n elements

for(i=0; sum != 0 ; i++)
{
    *(p+i) = sum % 10;                               //Giving values in dynamic array like 1234....n separately
    sum = sum / 10;
}

sort(p,-1);                                         //For sorting the dynamic array "p"

for(c=0 ; c<f/2 ; c++) {                        //Most important loop which prints 2 numbers per loop, so it goes upto 1/2 of fact(n)

    for(k=0 ; k<n ; k++)
        printf("%d",p[k]);                       //Loop for printing one of permutations
    printf("\n");

    i = d = 0;
    i = imax(p,i,d);                            //provides the max i as per algo (i am restricted to this only)
    j = i;
    j = jsmall(p,j);                            //provides smallest i val as per algo
    swap(&p[i],&p[j]);

    for(k=0 ; k<n ; k++)
        printf("%d",p[k]);
    printf("\n");

    i = d = 0;
    i = imax(p,i,d);
    j = i;
    j = jsmall(p,j);
    swap(&p[i],&p[j]);

    sort(p,i);
}
free(p);                                        //Deallocating memory
}

int fact (int a)
{
long f=1;
while(a!=0)
{
    f = f*a;
    a--;
}
return f;
}


void swap(int *p1,int *p2)
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
return;
}


void sort(int*p,int t)
{
int i,temp,j;
for(i=t+1 ; i<n-1 ; i++)
{
    for(j=i+1 ; j<n ; j++)
    {
        if(*(p+i) > *(p+j))
        {
            temp = *(p+i);
            *(p+i) = *(p+j);
            *(p+j) = temp;
        }
    }
}
}


int imax(int *p, int i , int d)
{
    while(i<n-1 && d<n-1)
{
    if(*(p+d) < *(p+d+1))
    {   
        i = d;
        d++;
    }
    else
        d++;
}
return i;
}


int jsmall(int *p, int j)
{
int i,small = 32767,k = j;
for (i=j+1 ; i<n ; i++)
{
    if (p[i]<small && p[i]>p[k])
    {     
       small = p[i];
       j = i;
    }
}
return j;
}

3
void permutate(char[] x, int i, int n){
    x=x.clone();
    if (i==n){
        System.out.print(x);
        System.out.print(" ");
        counter++;}
    else
    {
        for (int j=i; j<=n;j++){
     //   System.out.print(temp); System.out.print(" ");    //Debugger
        swap (x,i,j);
      //  System.out.print(temp); System.out.print(" "+"i="+i+" j="+j+"\n");// Debugger
        permutate(x,i+1,n);
    //    swap (temp,i,j);
    }
    }
}

void swap (char[] x, int a, int b){
char temp = x[a];
x[a]=x[b];
x[b]=temp;
}

ฉันสร้างสิ่งนี้ขึ้นมา ขึ้นอยู่กับการวิจัยพีชคณิตมากเกินไป (qwe, 0, qwe.length-1); ขอแจ้งให้ทราบว่าคุณสามารถทำได้โดยมีหรือไม่มีย้อนกลับ


3

นี่เป็นวิธีการของ Toy Ruby ที่ใช้งาน#permutation.to_aได้ดีกับคนบ้า มันช้า แต่ก็ 5 บรรทัด

def permute(ary)
  return [ary] if ary.size <= 1
  ary.collect_concat.with_index do |e, i|
    rest = ary.dup.tap {|a| a.delete_at(i) }
    permute(rest).collect {|a| a.unshift(e) }
  end
end

3

ฉันได้เขียนโซลูชันแบบวนซ้ำนี้ใน ANSI C การดำเนินการของฟังก์ชัน Permutate แต่ละครั้งจะให้การเปลี่ยนแปลงที่แตกต่างกันหนึ่งรายการจนกว่าทั้งหมดจะเสร็จสมบูรณ์ นอกจากนี้ยังสามารถใช้ตัวแปรส่วนกลางสำหรับตัวแปร fact และ count

#include <stdio.h>
#define SIZE 4

void Rotate(int vec[], int size)
{
    int i, j, first;

    first = vec[0];
    for(j = 0, i = 1; i < size; i++, j++)
    {
        vec[j] = vec[i];
    }
    vec[j] = first;
}

int Permutate(int *start, int size, int *count)
{
    static int fact;

    if(size > 1)
    {
        if(Permutate(start + 1, size - 1, count))
        {
            Rotate(start, size);
        }
        fact *= size;
    }
    else
    {
        (*count)++;
        fact = 1;
    }

    return !(*count % fact);
}

void Show(int vec[], int size)
{
    int i;

    printf("%d", vec[0]);
    for(i = 1; i < size; i++)
    {
        printf(" %d", vec[i]);
    }
    putchar('\n');
}

int main()
{
    int vec[] = { 1, 2, 3, 4, 5, 6 }; /* Only the first SIZE items will be permutated */
    int count = 0;

    do
    {
        Show(vec, SIZE);
    } while(!Permutate(vec, SIZE, &count));

    putchar('\n');
    Show(vec, SIZE);
    printf("\nCount: %d\n\n", count);

    return 0;
}

3

เวอร์ชัน Java

/**
 * @param uniqueList
 * @param permutationSize
 * @param permutation
 * @param only            Only show the permutation of permutationSize,
 *                        else show all permutation of less than or equal to permutationSize.
 */
public static void my_permutationOf(List<Integer> uniqueList, int permutationSize, List<Integer> permutation, boolean only) {
    if (permutation == null) {
        assert 0 < permutationSize && permutationSize <= uniqueList.size();
        permutation = new ArrayList<>(permutationSize);
        if (!only) {
            System.out.println(Arrays.toString(permutation.toArray()));
        }
    }
    for (int i : uniqueList) {
        if (permutation.contains(i)) {
            continue;
        }
        permutation.add(i);
        if (!only) {
            System.out.println(Arrays.toString(permutation.toArray()));
        } else if (permutation.size() == permutationSize) {
            System.out.println(Arrays.toString(permutation.toArray()));
        }
        if (permutation.size() < permutationSize) {
            my_permutationOf(uniqueList, permutationSize, permutation, only);
        }
        permutation.remove(permutation.size() - 1);
    }
}

เช่น

public static void main(String[] args) throws Exception { 
    my_permutationOf(new ArrayList<Integer>() {
        {
            add(1);
            add(2);
            add(3);

        }
    }, 3, null, true);
}

เอาท์พุท:

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

3

ใน PHP

$set=array('A','B','C','D');

function permutate($set) {
    $b=array();
    foreach($set as $key=>$value) {
        if(count($set)==1) {
            $b[]=$set[$key];
        }
        else {
            $subset=$set;
            unset($subset[$key]);
            $x=permutate($subset);
            foreach($x as $key1=>$value1) {
                $b[]=$value.' '.$value1;
            }
        }
    }
    return $b;
}

$x=permutate($set);
var_export($x);

3

นี่คือรหัสใน Python เพื่อพิมพ์การเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดของรายการ:

def next_perm(arr):
    # Find non-increasing suffix
    i = len(arr) - 1
    while i > 0 and arr[i - 1] >= arr[i]:
        i -= 1
    if i <= 0:
        return False

    # Find successor to pivot
    j = len(arr) - 1
    while arr[j] <= arr[i - 1]:
        j -= 1
    arr[i - 1], arr[j] = arr[j], arr[i - 1]

    # Reverse suffix
    arr[i : ] = arr[len(arr) - 1 : i - 1 : -1]
    print arr
    return True

def all_perm(arr):
    a = next_perm(arr)
    while a:
        a = next_perm(arr)
    arr = raw_input()
    arr.split(' ')
    arr = map(int, arr)
    arr.sort()
    print arr
    all_perm(arr)

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


3
public class PermutationGenerator
{
    private LinkedList<List<int>> _permutationsList;
    public void FindPermutations(List<int> list, int permutationLength)
    {
        _permutationsList = new LinkedList<List<int>>();
        foreach(var value in list)
        {
            CreatePermutations(value, permutationLength);
        }
    }

    private void CreatePermutations(int value, int permutationLength)
    {
        var node = _permutationsList.First;
        var last = _permutationsList.Last;
        while (node != null)
        {
            if (node.Value.Count < permutationLength)
            {
                GeneratePermutations(node.Value, value, permutationLength);
            }
            if (node == last)
            {
                break;
            }
            node = node.Next;
        }

        List<int> permutation = new List<int>();
        permutation.Add(value);
        _permutationsList.AddLast(permutation);
    }

    private void GeneratePermutations(List<int> permutation, int value, int permutationLength)
    {
       if (permutation.Count < permutationLength)
        {
            List<int> copyOfInitialPermutation = new List<int>(permutation);
            copyOfInitialPermutation.Add(value);
            _permutationsList.AddLast(copyOfInitialPermutation);
            List<int> copyOfPermutation = new List<int>();
            copyOfPermutation.AddRange(copyOfInitialPermutation);
            int lastIndex = copyOfInitialPermutation.Count - 1;
            for (int i = lastIndex;i > 0;i--)
            {
                int temp = copyOfPermutation[i - 1];
                copyOfPermutation[i - 1] = copyOfPermutation[i];
                copyOfPermutation[i] = temp;

                List<int> perm = new List<int>();
                perm.AddRange(copyOfPermutation);
                _permutationsList.AddLast(perm);
            }
        }
    }

    public void PrintPermutations(int permutationLength)
    {
        int count = _permutationsList.Where(perm => perm.Count() == permutationLength).Count();
        Console.WriteLine("The number of permutations is " + count);
    }
}

เป็นคำตอบที่มีประโยชน์
Ayaz Alifov

2

ในสกาล่า

    def permutazione(n: List[Int]): List[List[Int]] = permutationeAcc(n, Nil)



def permutationeAcc(n: List[Int], acc: List[Int]): List[List[Int]] = {

    var result: List[List[Int]] = Nil
    for (i ← n if (!(acc contains (i))))
        if (acc.size == n.size-1)
            result = (i :: acc) :: result
        else
            result = result ::: permutationeAcc(n, i :: acc)
    result
}

2

นี่เป็นเวอร์ชัน java สำหรับการเปลี่ยนแปลง

public class Permutation {

    static void permute(String str) {
        permute(str.toCharArray(), 0, str.length());
    }

    static void permute(char [] str, int low, int high) {
        if (low == high) {
            System.out.println(str);
            return;
        }

        for (int i=low; i<high; i++) {
            swap(str, i, low);
            permute(str, low+1, high);
            swap(str, low, i);
        }

    }

    static void swap(char [] array, int i, int j) {
        char t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}

2

นี่คือการนำไปใช้สำหรับ ColdFusion (ต้องใช้ CF10 เนื่องจากอาร์กิวเมนต์ผสานกับ ArrayAppend ()):

public array function permutateArray(arr){

    if (not isArray(arguments.arr) ) {
        return ['The ARR argument passed to the permutateArray function is not of type array.'];    
    }

    var len = arrayLen(arguments.arr);
    var perms = [];
    var rest = [];
    var restPerms = [];
    var rpLen = 0;
    var next = [];

    //for one or less item there is only one permutation 
    if (len <= 1) {
        return arguments.arr;
    }

    for (var i=1; i <= len; i++) {
        // copy the original array so as not to change it and then remove the picked (current) element
        rest = arraySlice(arguments.arr, 1);
        arrayDeleteAt(rest, i);

         // recursively get the permutation of the rest of the elements
         restPerms = permutateArray(rest);
         rpLen = arrayLen(restPerms);

        // Now concat each permutation to the current (picked) array, and append the concatenated array to the end result
        for (var j=1; j <= rpLen; j++) {
            // for each array returned, we need to make a fresh copy of the picked(current) element array so as to not change the original array
            next = arraySlice(arguments.arr, i, 1);
            arrayAppend(next, restPerms[j], true);
            arrayAppend(perms, next);
        }
     }

    return perms;
}

ตามโซลูชัน js ของ KhanSharp ข้างต้น


คำอธิบายเกี่ยวกับกลยุทธ์โดยรวมของอัลกอริทึมของคุณอาจเป็นวิธีที่ดีในการปรับปรุงคำตอบนี้
Richard

ทำไมต้องโหวตลด? นี่คือการนำไปใช้งาน
earachefl

@ ริชาร์ดกลยุทธ์โดยรวมได้รับการอธิบายข้างต้นโดย Whirlwind และคนอื่น ๆ ฉันไม่เห็นความคิดเห็นของคุณในคำตอบอื่น ๆ ทั้งหมดที่โพสต์เป็นการใช้งานโดยไม่มีคำอธิบาย
earachefl

1

ฉันรู้ว่านี่เป็นเรื่องเก่ามากและแม้กระทั่งนอกหัวข้อใน stackoverflow ของวันนี้ แต่ฉันยังต้องการให้คำตอบจาวาสคริปต์ที่เป็นมิตรด้วยเหตุผลง่ายๆที่มันทำงานในเบราว์เซอร์ของคุณ

ฉันยังได้เพิ่มdebuggerเบรกพอยต์คำสั่งเพื่อให้คุณสามารถก้าวผ่านโค้ด (ต้องใช้โครเมี่ยม) เพื่อดูว่าอัลกอริทึมนี้ทำงานอย่างไร เปิดคอนโซลนักพัฒนาของคุณใน chrome ( F12ใน windows หรือCMD + OPTION + Iบน Mac) จากนั้นคลิก "เรียกใช้ข้อมูลโค้ด" สิ่งนี้ใช้อัลกอริทึมเดียวกับที่ @WhirlWind นำเสนอในคำตอบของเขา

เบราว์เซอร์ของคุณควรหยุดการทำงานชั่วคราวตามdebuggerคำสั่ง ใช้F8เพื่อดำเนินการต่อรหัส

ขั้นตอนผ่านรหัสและดูว่ามันทำงานอย่างไร!

function permute(rest, prefix = []) {
  if (rest.length === 0) {
    return [prefix];
  }
  return (rest
    .map((x, index) => {
      const oldRest = rest;
      const oldPrefix = prefix;
      // the `...` destructures the array into single values flattening it
      const newRest = [...rest.slice(0, index), ...rest.slice(index + 1)];
      const newPrefix = [...prefix, x];
      debugger;

      const result = permute(newRest, newPrefix);
      return result;
    })
    // this step flattens the array of arrays returned by calling permute
    .reduce((flattened, arr) => [...flattened, ...arr], [])
  );
}
console.log(permute([1, 2, 3]));


1

ในโซลูชัน Java ต่อไปนี้เราใช้ประโยชน์จากข้อเท็จจริงที่ว่า Strings ไม่เปลี่ยนรูปเพื่อหลีกเลี่ยงการโคลนผลลัพธ์ที่ตั้งไว้ในการทำซ้ำทุกครั้ง

อินพุตจะเป็นสตริงพูดว่า "abc" และเอาต์พุตจะเป็นการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมด:

abc
acb
bac
bca
cba
cab

รหัส:

public static void permute(String s) {
    permute(s, 0);
}

private static void permute(String str, int left){
    if(left == str.length()-1) {
        System.out.println(str);
    } else {
        for(int i = left; i < str.length(); i++) {
            String s = swap(str, left, i);
            permute(s, left+1);
        }
    }
}

private static String swap(String s, int left, int right) {
    if (left == right)
        return s;

    String result = s.substring(0, left);
    result += s.substring(right, right+1);
    result += s.substring(left+1, right);
    result += s.substring(left, left+1);
    result += s.substring(right+1);
    return result;
}

แนวทางเดียวกันนี้สามารถใช้ได้กับอาร์เรย์ (แทนที่จะเป็นสตริง):

public static void main(String[] args) {
    int[] abc = {1,2,3};
    permute(abc, 0);
}
public static void permute(int[] arr, int index) {
    if (index == arr.length) {
        System.out.println(Arrays.toString(arr));
    } else {
        for (int i = index; i < arr.length; i++) {
            int[] permutation = arr.clone();
            permutation[index] = arr[i];
            permutation[i] = arr[index];
            permute(permutation, index + 1);
        }
    }
}

1

เป็นวิธีแก้ปัญหาของฉันบน Java:

public class CombinatorialUtils {

    public static void main(String[] args) {
        List<String> alphabet = new ArrayList<>();
        alphabet.add("1");
        alphabet.add("2");
        alphabet.add("3");
        alphabet.add("4");

        for (List<String> strings : permutations(alphabet)) {
            System.out.println(strings);
        }
        System.out.println("-----------");
        for (List<String> strings : combinations(alphabet)) {
            System.out.println(strings);
        }
    }

    public static List<List<String>> combinations(List<String> alphabet) {
        List<List<String>> permutations = permutations(alphabet);
        List<List<String>> combinations = new ArrayList<>(permutations);

        for (int i = alphabet.size(); i > 0; i--) {
            final int n = i;
            combinations.addAll(permutations.stream().map(strings -> strings.subList(0, n)).distinct().collect(Collectors.toList()));
        }
        return combinations;
    }

    public static <T> List<List<T>> permutations(List<T> alphabet) {
        ArrayList<List<T>> permutations = new ArrayList<>();
        if (alphabet.size() == 1) {
            permutations.add(alphabet);
            return permutations;
        } else {
            List<List<T>> subPerm = permutations(alphabet.subList(1, alphabet.size()));
            T addedElem = alphabet.get(0);
            for (int i = 0; i < alphabet.size(); i++) {
                for (List<T> permutation : subPerm) {
                    int index = i;
                    permutations.add(new ArrayList<T>(permutation) {{
                        add(index, addedElem);
                    }});
                }
            }
        }
        return permutations;
    }
}

1

คุณไม่สามารถพูดถึงการแก้ปัญหาการเรียงลำดับในการเรียกซ้ำโดยไม่โพสต์การใช้งานในภาษา (ภาษาถิ่น) ที่เป็นหัวหอกในแนวคิดนี้ ดังนั้นเพื่อความสมบูรณ์นี่คือหนึ่งในวิธีที่สามารถทำได้ใน Scheme

(define (permof wd)
  (cond ((null? wd) '())
        ((null? (cdr wd)) (list wd))
        (else
         (let splice ([l '()] [m (car wd)] [r (cdr wd)])
           (append
            (map (lambda (x) (cons m x)) (permof (append l r)))
            (if (null? r)
                '()
                (splice (cons m l) (car r) (cdr r))))))))

(permof (list "foo" "bar" "baz"))เราจะโทรหา:

'(("foo" "bar" "baz")
  ("foo" "baz" "bar")
  ("bar" "foo" "baz")
  ("bar" "baz" "foo")
  ("baz" "bar" "foo")
  ("baz" "foo" "bar"))

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

อย่างไรก็ตามปัญหาที่เกิดซ้ำมักจะยากกว่ามากในการสร้างแบบจำลองและคิดเกี่ยวกับสื่อทำลายล้างเช่น Python, C และ Java ในขณะที่ใน Lisp หรือ ML นั้นสามารถแสดงออกได้อย่างกระชับ


0

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

public function permute($sofar, $input){
  for($i=0; $i < strlen($input); $i++){
    $diff = strDiff($input,$input[$i]);
    $next = $sofar.$input[$i]; //next contains a permutation, save it
    $this->permute($next, $diff);
  }
}

ฟังก์ชัน strDiff รับสองสตริงs1และs2และส่งคืนสตริงใหม่โดยมีทุกอย่างในs1องค์ประกอบs2(ซ้ำกัน) ดังนั้นstrDiff('finish','i')=> 'fnish'('i' ตัวที่สองจะไม่ถูกลบออก)


0

นี่คืออัลกอริทึมใน R ในกรณีที่ใครก็ตามต้องการหลีกเลี่ยงการโหลดไลบรารีเพิ่มเติมเช่นฉันต้องทำ

permutations <- function(n){
    if(n==1){
        return(matrix(1))
    } else {
        sp <- permutations(n-1)
        p <- nrow(sp)
        A <- matrix(nrow=n*p,ncol=n)
        for(i in 1:n){
            A[(i-1)*p+1:p,] <- cbind(i,sp+(sp>=i))
        }
        return(A)
    }
}

ตัวอย่างการใช้งาน:

> matrix(letters[permutations(3)],ncol=3)
     [,1] [,2] [,3]
[1,] "a"  "b"  "c" 
[2,] "a"  "c"  "b" 
[3,] "b"  "a"  "c" 
[4,] "b"  "c"  "a" 
[5,] "c"  "a"  "b" 
[6,] "c"  "b"  "a" 

0
#!/usr/bin/env python
import time

def permutations(sequence):
  # print sequence
  unit = [1, 2, 1, 2, 1]

  if len(sequence) >= 4:
    for i in range(4, (len(sequence) + 1)):
      unit = ((unit + [i - 1]) * i)[:-1]
      # print unit
    for j in unit:
      temp = sequence[j]
      sequence[j] = sequence[0]
      sequence[0] = temp
      yield sequence
  else:
    print 'You can use PEN and PAPER'


# s = [1,2,3,4,5,6,7,8,9,10]
s = [x for x in 'PYTHON']

print s

z = permutations(s)
try:
  while True:
    # time.sleep(0.0001)
    print next(z)
except StopIteration:
    print 'Done'

['P', 'Y', 'T', 'H', 'O', 'N']
['Y', 'P', 'T', 'H', 'O', 'N']
['T', 'P', 'Y', 'H', 'O', 'N']
['P', 'T', 'Y', 'H', 'O', 'N']
['Y', 'T', 'P', 'H', 'O', 'N']
['T', 'Y', 'P', 'H', 'O', 'N']
['H', 'Y', 'P', 'T', 'O', 'N']
['Y', 'H', 'P', 'T', 'O', 'N']
['P', 'H', 'Y', 'T', 'O', 'N']
['H', 'P', 'Y', 'T', 'O', 'N']
['Y', 'P', 'H', 'T', 'O', 'N']
['P', 'Y', 'H', 'T', 'O', 'N']
['T', 'Y', 'H', 'P', 'O', 'N']
['Y', 'T', 'H', 'P', 'O', 'N']
['H', 'T', 'Y', 'P', 'O', 'N']
['T', 'H', 'Y', 'P', 'O', 'N']
['Y', 'H', 'T', 'P', 'O', 'N']
['H', 'Y', 'T', 'P', 'O', 'N']
['P', 'Y', 'T', 'H', 'O', 'N']
.
.
.
['Y', 'T', 'N', 'H', 'O', 'P']
['N', 'T', 'Y', 'H', 'O', 'P']
['T', 'N', 'Y', 'H', 'O', 'P']
['Y', 'N', 'T', 'H', 'O', 'P']
['N', 'Y', 'T', 'H', 'O', 'P']

วิธีแก้ปัญหาแสดงว่าคุณไม่ได้กำหนดสตริงตามความต้องการ การเรียงสับเปลี่ยนครั้งที่สองควรเป็น PYTHNO
Rahul Kadukar

0

นี่เป็นรหัสเรียกซ้ำสำหรับ java แนวคิดคือการมีคำนำหน้าที่เพิ่มอักขระที่เหลือ:

public static void permutation(String str) { 
    permutation("", str); 
}

private static void permutation(String prefix, String str) {
    int n = str.length();
    if (n == 0) System.out.println(prefix);
    else {
        for (int i = 0; i < n; i++)
            permutation(prefix + str.charAt(i), str);
    }
}

ตัวอย่าง:

อินพุต = "ABC"; เอาท์พุท:

ABC ACB BAC BCA CAB CBA


1
เป็นความคิดที่ดี แต่ฉันคิดว่าคุณควรลบ charAt (i) ออกstrเมื่อโทรซ้ำมิฉะนั้นจะไม่ยุติ
คริสตัล

1
หากคุณจะคัดลอกและวางคุณต้อง (1) ระบุแหล่งที่มาและ (2) ตรวจสอบว่าการแก้ไขถูกต้อง สำหรับการกำหนดลักษณะนี้เป็น perm1 จากintrocs.cs.princeton.edu/java/23recursion/... นอกจากนี้การแก้ไขของคุณไม่ถูกต้อง: str.substring (0, i) + str.substring (i + 1, n) ไม่เหมือนกับ str เนื่องจากเดิมจะละอักขระที่ตำแหน่ง i
kevin

0

เพื่อให้สมบูรณ์ C ++

#include <iostream>
#include <algorithm>
#include <string>

std::string theSeq = "abc";
do
{
  std::cout << theSeq << endl;
} 
while (std::next_permutation(theSeq.begin(), theSeq.end()));

...

abc
acb
bac
bca
cab
cba

0

นี่คือโซลูชันแบบไม่วนซ้ำใน C ++ ที่จัดเตรียมการเปลี่ยนลำดับถัดไปโดยเรียงลำดับจากน้อยไปมากเช่นเดียวกับฟังก์ชันที่จัดทำโดย std :: next_permutation:

void permute_next(vector<int>& v)
{
  if (v.size() < 2)
    return;

  if (v.size() == 2)
  { 
    int tmp = v[0];
    v[0] = v[1];
    v[1] = tmp;
    return;
  }

  // Step 1: find first ascending-ordered pair from right to left
  int i = v.size()-2;
  while(i>=0)
  { 
    if (v[i] < v[i+1])
      break;
    i--;
  }
  if (i<0) // vector fully sorted in descending order (last permutation)
  {
    //resort in ascending order and return
    sort(v.begin(), v.end());
    return;
  }

  // Step 2: swap v[i] with next higher element of remaining elements
  int pos = i+1;
  int val = v[pos];
  for(int k=i+2; k<v.size(); k++)
    if(v[k] < val && v[k] > v[i])
    {
      pos = k;
      val = v[k];
    }
  v[pos] = v[i];
  v[i] = val;

  // Step 3: sort remaining elements from i+1 ... end
  sort(v.begin()+i+1, v.end());
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.