Thuật toán để tạo ra tất cả các hoán vị có thể có của một danh sách?


119

Giả sử tôi có một danh sách gồm n phần tử, tôi biết có n! các cách có thể để sắp xếp các yếu tố này. Thuật toán tạo tất cả các chuỗi có thể có của danh sách này là gì? Ví dụ, tôi có danh sách [a, b, c]. Thuật toán sẽ trả về [[a, b, c], [a, c, b,], [b, a, c], [b, c, a], [c, a, b], [c, b , a]].

Tôi đang đọc cái này tại đây http://en.wikipedia.org/wiki/Permutation#Algorithm_to_generate_permutations

Nhưng Wikipedia chưa bao giờ giỏi giải thích. Tôi không hiểu nhiều về nó.


5
Tôi đã viết một câu trả lời mở rộng cho một câu hỏi khác về việc tạo hoán vị một lần. Tôi nghĩ nó sẽ được bạn quan tâm: stackoverflow.com/questions/1506078/…
Joren

2
Điều này có thể giải quyết vấn đề của bạn en.wikipedia.org/wiki/Heap's_algorithm
Felix

Câu trả lời:


96

Về cơ bản, đối với mỗi mục từ trái sang phải, tất cả các hoán vị của các mục còn lại được tạo ra (và mỗi mục được thêm vào với các phần tử hiện tại). Điều này có thể được thực hiện một cách đệ quy (hoặc lặp đi lặp lại nếu bạn thích đau đớn) cho đến khi đạt được mục cuối cùng tại thời điểm đó chỉ có một đơn đặt hàng khả thi.

Vì vậy, với danh sách [1,2,3,4] tất cả các hoán vị bắt đầu bằng 1 được tạo ra, sau đó tất cả các hoán vị bắt đầu bằng 2, rồi 3 rồi 4.

Điều này có hiệu quả làm giảm vấn đề từ một trong việc tìm các hoán vị của danh sách bốn mục thành danh sách ba mục. Sau khi giảm xuống 2 và sau đó 1 danh sách mục, tất cả chúng sẽ được tìm thấy.
Ví dụ hiển thị hoán vị quy trình sử dụng 3 quả bóng màu:
Hình ảnh hoán vị các quả bóng màu đỏ, xanh lục và xanh lam được sắp xếp theo thứ tự(từ https://en.wikipedia.org/wiki/Permutation#/media/File:Permutations_RGB.svg - https://commons.wikimedia.org/wiki/File:Permutations_RGB. svg )


2
Lúc đầu tôi cũng nghĩ về điều này nhưng sau đó phần tử hiện tại sẽ không được đưa vào giữa một số điều sau. Vì vậy, không phải tất cả các hoán vị sẽ được tạo ra.
Fent

@LLer xin lỗi, đã cập nhật câu trả lời của tôi từ "folllowing" thành "còn lại" để làm rõ. Nó hoạt động tốt mặc dù. Kiểm tra bằng cách viết mã và xác minh rằng bạn nhận được 4! kết quả khác nhau.
WhirlWind

2
int hoán vị (int n, vector <int> a) {static int num_permutations = 0; if (n == (a.size () - 1)) {for (int i = 0; i <a.size (); i ++) cout << a [i] << ""; cout << "\ n"; num_permutations ++; } else {for (int i = n + 1; i <= a.size (); i ++) {hoán vị (n + 1, a); if (i <a.size ()) int temp = a [n], a [n] = a [i], a [i] = temp; }} trả về num_permutations; } int main (void) {vector <int> v; v.push_back (1); ... return hoán vị (0, v); }
Somesh

Rất tiếc - không chắc chắn cách định dạng mã trong một nhận xét ... Đã kiểm tra mã với 7 và nhận được 5040. Cảm ơn @WhirlWind đã gợi ý.
Somesh

Thuật ngữ này không thay đổi nếu bạn có thể có 2 hoặc 3 quả bóng màu đỏ # 1, thay vì chỉ 1 quả bóng mỗi màu?
Alexander Mills

26

Đây là một thuật toán bằng Python hoạt động tại chỗ trên một mảng:

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

Bạn có thể thử mã cho chính mình tại đây: http://repl.it/J9v


Bạn có thể vui lòng giải thích phần năng suất? Tôi không thể chạy mã khô. Cảm ơn trước.
Agniswar Bakshi

Câu hỏi Stack Overflow tại stackoverflow.com/questions/104420/… cho biết có một mô-đun thư viện tiêu chuẩn trong phiên bản 2.6 trở đi và có câu trả lời cung cấp giải pháp 6 dòng trong một hàm để nhận hoán vị danh sách.
Edward

@Agniswar Tóm lại, câu lệnh lợi nhuận được sử dụng để xác định trình tạo, thay thế việc trả về của một hàm để cung cấp kết quả cho trình gọi của nó mà không phá hủy các biến cục bộ. Không giống như một hàm, trong đó mỗi lần gọi nó bắt đầu với một tập hợp các biến mới, một trình tạo sẽ tiếp tục thực thi ở nơi nó đã dừng. pythoncentral.io/python-generators-and-yield-keyword
MSS

Giải pháp này sẽ không hoạt động khi xử lý danh sách các mục nhập giống hệt nhau.
KaiserKatze

Cám ơn vì đã chia sẻ. Điều này trực quan và hiệu quả, mặc dù đầu ra không theo thứ tự từ vựng.
Sam

16

Đã có rất nhiều giải pháp tốt ở đây, nhưng tôi muốn chia sẻ cách tôi đã tự mình giải quyết vấn đề này và hy vọng rằng điều này có thể hữu ích cho một số người cũng muốn tìm ra giải pháp của riêng mình.

Sau khi cân nhắc về vấn đề, tôi đã đưa ra hai kết luận sau:

  1. Đối với danh sách có Lkích thước nsẽ có số nghiệm bằng nhau bắt đầu bằng L 1 , L 2 ... L n phần tử của danh sách. Vì tổng số có các n!hoán vị của danh sách kích thước n, chúng tôi nhận được n! / n = (n-1)!hoán vị trong mỗi nhóm.
  2. Danh sách 2 phần tử chỉ có 2 hoán vị => [a,b][b,a].

Sử dụng hai ý tưởng đơn giản này, tôi đã suy ra thuật toán sau:

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

Đây là cách tôi triển khai điều này trong 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

Câu trả lời của Wikipedia cho "thứ tự từ vựng" dường như hoàn toàn rõ ràng theo kiểu sách dạy nấu ăn đối với tôi. Nó trích dẫn nguồn gốc từ thế kỷ 14 cho thuật toán!

Tôi vừa viết một bản triển khai nhanh trong Java thuật toán của Wikipedia như một sự kiểm tra và nó không có vấn đề gì. Nhưng những gì bạn có trong Q của mình làm ví dụ KHÔNG phải là "liệt kê tất cả các hoán vị", mà là "DANH SÁCH tất cả các hoán vị", vì vậy wikipedia sẽ không giúp được nhiều cho bạn. Bạn cần một ngôn ngữ trong đó danh sách các hoán vị được xây dựng khả thi. Và tin tôi đi, danh sách dài vài tỷ thường không được xử lý bằng ngôn ngữ bắt buộc. Bạn thực sự muốn một ngôn ngữ lập trình chức năng không nghiêm ngặt, trong đó danh sách là đối tượng hạng nhất, để lấy ra mọi thứ trong khi không đưa cỗ máy đến gần với cái chết nhiệt của Vũ trụ.

Thật dễ dàng. Trong Haskell tiêu chuẩn hoặc bất kỳ ngôn ngữ FP hiện đại nào:

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

Như WhirlWind đã nói, bạn bắt đầu ngay từ đầu.

Bạn hoán đổi con trỏ với từng giá trị còn lại, bao gồm cả chính con trỏ, đây là tất cả các phiên bản mới (tôi đã sử dụng dấu int[]array.clone()trong ví dụ).

Sau đó, thực hiện hoán vị trên tất cả các danh sách khác nhau này, đảm bảo con trỏ nằm ở bên phải.

Khi không còn giá trị nào nữa (con trỏ ở cuối), hãy in danh sách. Đây là điều kiện dừng.

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

Đệ quy luôn cần một số nỗ lực tinh thần để duy trì. Và đối với các số lớn, giai thừa dễ dàng lớn và tràn ngăn xếp sẽ dễ dàng là một vấn đề.

Đối với các số nhỏ (3 hoặc 4, thường gặp), nhiều vòng lặp là khá đơn giản và thẳng về phía trước. Đó là câu trả lời không may với vòng lặp không được bình chọn.

Hãy bắt đầu với phép liệt kê (thay vì hoán vị). Đơn giản chỉ cần đọc mã dưới dạng mã perl giả.

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

Phép liệt kê thường gặp hơn hoán vị, nhưng nếu cần hoán vị, chỉ cần thêm các điều kiệ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"

Bây giờ nếu bạn thực sự cần phương pháp chung cho danh sách lớn, chúng ta có thể sử dụng phương pháp cơ số. Đầu tiên, hãy xem xét vấn đề liệt kê:

$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

Tăng cơ số về cơ bản là đếm số (trong cơ sở số phần tử danh sách).

Bây giờ nếu bạn cần hoán vị, chỉ cần thêm các kiểm tra bên trong vòng lặp:

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

Chỉnh sửa: Đoạn mã trên sẽ hoạt động, nhưng đối với hoán vị, radix_increment có thể lãng phí. Vì vậy, nếu thời gian là mối quan tâm thực tế, chúng ta phải thay đổi radix_increment thành 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                                              

Tất nhiên bây giờ mã này phức tạp hơn về mặt logic, tôi sẽ để dành cho bài tập của người đọc.


7

nhập mô tả hình ảnh ở đây

// 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;
}

Tham khảo: Geeksforgeeks.org


5

Nếu có ai thắc mắc làm thế nào để được thực hiện trong hoán vị trong javascript.

Ý tưởng / mã giả

  1. chọn một phần tử tại một thời điểm
  2. hoán vị phần còn lại của phần tử và sau đó thêm phần tử đã chọn vào tất cả hoán vị

ví dụ. 'a' + hoán vị (bc). hoán vị của bc sẽ là bc & cb. Bây giờ thêm hai cái này sẽ cho abc, acb. tương tự, chọn b + permute (ac) sẽ chuyển sang bac, bca ... và tiếp tục.

bây giờ hãy nhìn vào mã

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

Hãy dành thời gian của bạn để hiểu điều này. Tôi lấy mã này từ ( pertumation trong JavaScript )


Tôi đã nghĩ về một cái gì đó tương tự nhưng bạn không nên thêm phần tử đã chọn vào cả phía trước và phần cuối của restParams? Trong trường hợp này, đối với 'abc', nếu bạn chọn a, thì các hoán vị 'bc' là 'bc' và 'cb'. Khi bạn thêm 'a' trở lại danh sách, bạn không nên thêm nó vào trước là 'a + bc' + 'a + cb' mà còn ở cuối là 'bc + a' + 'cb + a' of danh sách?
Artimus

Bạn sẽ nhận được các hoán vị đó khi hoán vị bắt đầu bằng 'b' và 'c' tương ứng. (tức là lần chạy thứ hai và thứ ba của vòng lặp 'for' bên ngoài)
Ryan O'Neill,

3

Một cái khác bằng Python, nó không đúng chỗ như @ cdiggins, nhưng tôi nghĩ nó dễ hiểu hơn

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

Tôi đã nghĩ đến việc viết mã để nhận các hoán vị của bất kỳ số nguyên nhất định nào có kích thước bất kỳ, tức là cung cấp một số 4567, chúng tôi sẽ nhận được tất cả các hoán vị có thể có cho đến 7654 ... Vì vậy, tôi đã làm việc trên nó và tìm ra một thuật toán và cuối cùng đã thực hiện nó, Đây là mã được viết bằng "c". Bạn chỉ cần sao chép nó và chạy trên bất kỳ trình biên dịch mã nguồn mở nào. Nhưng một số lỗ hổng đang chờ được gỡ lỗi. Hãy đánh giá cao.

Mã:

#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;
}

Tôi đã tạo ra cái này. dựa trên nghiên cứu quá hoán vị (qwe, 0, qwe.length-1); Xin lưu ý là bạn có thể làm điều đó có hoặc không có nhạc nền


3

Đây là một phương pháp Ruby đồ chơi hoạt động như #permutation.to_avậy có thể dễ đọc hơn đối với những người điên. Nó chậm, nhưng cũng có 5 dòng.

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

Tôi đã viết giải pháp đệ quy này trong ANSI C. Mỗi lần thực thi hàm Permutate cung cấp một hoán vị khác nhau cho đến khi tất cả được hoàn thành. Các biến toàn cục cũng có thể được sử dụng cho các biến fact và 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

Phiên bản 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);
    }
}

Ví dụ

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

        }
    }, 3, null, true);
}

đầu ra:

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

3

bằng 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

Đây là mã bằng Python để in tất cả các hoán vị có thể có của một danh sách:

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)

Tôi đã sử dụng thuật toán thứ tự từ điển để nhận được tất cả các hoán vị có thể có, nhưng thuật toán đệ quy hiệu quả hơn. Bạn có thể tìm mã cho thuật toán đệ quy tại đây: Hoán vị đệ quy 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);
    }
}

đó là một câu trả lời hữu ích
Ayaz Alifov

2

Ở Scala

    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

đây là phiên bản java để hoán vị

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

Đây là cách triển khai cho ColdFusion (yêu cầu CF10 vì đối số hợp nhất thành 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;
}

Dựa trên giải pháp js của KhanSharp ở trên.


Một số giải thích về chiến lược tổng thể của thuật toán của bạn sẽ là một cách tốt để cải thiện câu trả lời này.
Richard

Vì vậy, tại sao downvote? Đây là một triển khai hoạt động.
earachefl

@Richard, chiến lược tổng thể đã được giải thích ở trên bởi Whirlwind và những người khác. Tôi không thấy nhận xét của bạn về tất cả các câu trả lời khác được đăng dưới dạng triển khai mà không có giải thích.
earachefl

1

Tôi biết điều này rất cũ và thậm chí không có chủ đề trong stackoverflow ngày nay nhưng tôi vẫn muốn đóng góp một câu trả lời javascript thân thiện vì lý do đơn giản là nó chạy trong trình duyệt của bạn.

Tôi cũng đã thêm debuggerđiểm ngắt chỉ thị để bạn có thể xem qua mã (yêu cầu chrome) để xem thuật toán này hoạt động như thế nào. Mở bảng điều khiển dành cho nhà phát triển của bạn trong chrome ( F12trong windows hoặcCMD + OPTION + I trên mac), sau đó nhấp vào "Chạy đoạn mã". Điều này thực hiện cùng một thuật toán chính xác mà @WhirlWind đã trình bày trong câu trả lời của anh ấy.

Trình duyệt của bạn sẽ tạm dừng thực thi theo lệnh debugger. Sử dụng F8để tiếp tục thực thi mã.

Bước qua mã và xem nó hoạt động như thế nào!

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

Trong giải pháp Java sau đây, chúng tôi tận dụng lợi thế của thực tế là Chuỗi là bất biến để tránh sao chép tập kết quả sau mỗi lần lặp.

Đầu vào sẽ là một Chuỗi, giả sử "abc" và đầu ra sẽ là tất cả các hoán vị có thể có:

abc
acb
bac
bca
cba
cab

Mã:

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

Có thể áp dụng cùng một cách tiếp cận cho mảng (thay vì một chuỗi):

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

Đó là giải pháp của tôi trên 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

Bạn không thể thực sự nói về việc giải quyết một vấn đề hoán vị trong đệ quy mà không đăng một cách triển khai bằng một ngôn ngữ (phương ngữ của) đã đi tiên phong trong ý tưởng . Vì vậy, để hoàn thiện, đây là một trong những cách có thể được thực hiện trong 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))))))))

gọi (permof (list "foo" "bar" "baz"))chúng tôi sẽ nhận được:

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

Tôi sẽ không đi vào chi tiết thuật toán vì nó đã được giải thích đủ trong các bài viết khác. Ý tưởng là như nhau.

Tuy nhiên, các vấn đề đệ quy có xu hướng khó mô hình hóa và suy nghĩ hơn nhiều trong các phương tiện phá hoại như Python, C và Java, trong khi trong Lisp hoặc ML, nó có thể được diễn đạt một cách ngắn gọn.


0

Đây là một giải pháp đệ quy trong PHP. Bài đăng của WhirlWind mô tả chính xác logic. Điều đáng nói là việc tạo ra tất cả các hoán vị chạy theo thời gian giai thừa, vì vậy thay vào đó có thể là một ý tưởng hay nếu sử dụng phương pháp lặp lại.

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

Hàm strDiff nhận hai chuỗi s1s2và trả về một chuỗi mới với mọi thứ trong đó s1không có phần tử trong s2(vật chất trùng lặp). Vì vậy, strDiff('finish','i')=> 'fnish'(chữ 'i' thứ hai không bị loại bỏ).


0

Đây là một thuật toán trong R, trong trường hợp bất kỳ ai cần tránh tải các thư viện bổ sung như tôi đã phải làm.

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

Ví dụ sử dụng:

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

Giải pháp cho thấy rằng bạn đã không hoán vị chuỗi theo yêu cầu. Hoán vị thứ hai lẽ ra phải là PYTHNO
Rahul Kadukar

0

Đây là một mã đệ quy cho java, ý tưởng là có một tiền tố để thêm phần còn lại của các ký tự:

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

Thí dụ:

Đầu vào = "ABC"; Đầu ra:

ABC ACB BAC BCA CAB CBA


1
Ý tưởng hay, nhưng tôi nghĩ bạn cũng nên loại bỏ charAt (i) khỏi strkhi gọi đệ quy, nếu không nó sẽ không kết thúc.
Crystal

1
Nếu định sao chép và dán, bạn cần (1) ghi nhận tác giả và (2) đảm bảo mọi chỉnh sửa đều chính xác. Đối với thuộc tính, đây là perm1 từ introcs.cs.princeton.edu/java/23recursion/… . Ngoài ra, chỉnh sửa của bạn không chính xác: str.substring (0, i) + str.substring (i + 1, n) không giống với str, vì trước đó bỏ qua ký tự ở vị trí i.
kevin

0

Chỉ để hoàn thành, 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

Đây là một giải pháp không đệ quy trong C ++ cung cấp phép hoán vị tiếp theo theo thứ tự tăng dần, tương tự như chức năng được cung cấp bởi 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());
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.