Algoritma untuk menghasilkan semua permutasi yang mungkin dari daftar?


119

Katakanlah saya memiliki daftar n elemen, saya tahu ada n! cara yang mungkin untuk memesan elemen ini. Apa algoritma untuk menghasilkan semua kemungkinan urutan daftar ini? Contoh, saya punya daftar [a, b, c]. Algoritme akan mengembalikan [[a, b, c], [a, c, b,], [b, a, c], [b, c, a], [c, a, b], [c, b , Sebuah]].

Saya membaca ini di sini http://en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations

Tapi Wikipedia tidak pernah pandai menjelaskan. Saya tidak mengerti banyak tentang itu.


5
Saya menulis jawaban ekstensif untuk pertanyaan lain tentang menghasilkan permutasi sekali. Saya pikir itu akan menarik bagi Anda: stackoverflow.com/questions/1506078/…
Joren

2
Ini dapat menyelesaikan masalah Anda en.wikipedia.org/wiki/Heap's_algorithm
Felix

Jawaban:


96

Pada dasarnya, untuk setiap item dari kiri ke kanan, semua permutasi item yang tersisa dibuat (dan masing-masing ditambahkan dengan elemen saat ini). Ini dapat dilakukan secara rekursif (atau secara berulang jika Anda suka nyeri) sampai item terakhir tercapai di mana hanya ada satu urutan yang memungkinkan.

Jadi dengan daftar [1,2,3,4] semua permutasi yang dimulai dengan 1 dibuat, lalu semua permutasi yang dimulai dengan 2, lalu 3 lalu 4.

Ini secara efektif mengurangi masalah dari salah satu menemukan permutasi dari daftar empat item menjadi daftar tiga item. Setelah dikurangi menjadi 2 dan kemudian 1 daftar item, semuanya akan ditemukan.
Contoh yang menunjukkan permutasi proses menggunakan 3 bola berwarna:
Bola berwarna merah, hijau dan biru memerintahkan gambar permutasi(dari https://en.wikipedia.org/wiki/Permutation#/media/File:Permutations_RGB.svg - https://commons.wikimedia.org/wiki/File:Permutations_RGB. svg )


2
Saya juga memikirkan hal ini pada awalnya, tetapi kemudian elemen saat ini tidak akan ditempatkan di antara beberapa hal berikut. Jadi tidak semua permutasi akan dibuat.
Fent

@Ler maaf, memperbarui jawaban saya dari "mengikuti" menjadi "tersisa" untuk mengklarifikasi. Ini bekerja dengan baik. Periksa dengan menulis kode dan memverifikasi bahwa Anda mendapatkan 4! hasil yang berbeda.
WhirlWind

2
permutasi int (int n, vektor <int> a) {static int num_permutations = 0; if (n == (a.size () - 1)) {untuk (int i = 0; i <a.size (); i ++) cout << a [i] << ""; cout << "\ n"; num_permutations ++; } else {untuk (int i = n + 1; i <= a.size (); i ++) {permutasi (n + 1, a); jika (i <a.size ()) int temp = a [n], a [n] = a [i], a [i] = temp; }} return num_permutations; } int utama (kosong) {vektor <int> v; v.push_back (1); ... kembali permutasi (0, v); }
Somesh

Ups - tidak yakin bagaimana memformat kode dalam komentar ... Menguji kode dengan 7 dan mendapatkan 5040. Terima kasih kepada @WhirlWind untuk sarannya.
Agak

Bukankah ini juga berubah jika Anda dapat memiliki 2 atau 3 bola merah # 1, alih-alih hanya 1 dari setiap warna?
Alexander Mills

26

Berikut adalah algoritma dalam Python yang bekerja di tempat pada sebuah array:

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

Anda dapat mencoba kode sendiri di sini: http://repl.it/J9v


Bisakah Anda menjelaskan bagian hasil? Saya tidak bisa menjalankan kode kering. Terima kasih sebelumnya.
Agniswar Bakshi

Pertanyaan Stack Overflow di stackoverflow.com/questions/104420/… menyatakan ada modul pustaka standar di versi 2.6 dan seterusnya dan memiliki jawaban yang menyediakan solusi 6 baris dalam fungsi untuk mendapatkan permutasi daftar.
Edward

@Agniswar Sekilas, pernyataan yield digunakan untuk mendefinisikan generator, menggantikan kembalinya suatu fungsi untuk memberikan hasil kepada pemanggilnya tanpa merusak variabel lokal. Tidak seperti fungsi, di mana pada setiap panggilan dimulai dengan kumpulan variabel baru, generator akan melanjutkan eksekusi di tempat yang ditinggalkannya. pythoncentral.io/python-generators-and-yield-keyword
MSS

Solusi ini tidak akan berfungsi saat menangani daftar entri yang identik.
KaiserKatze

Terima kasih telah berbagi. Ini intuitif dan efisien, meskipun keluarannya tidak dalam urutan leksikografis.
Sam

16

Sudah ada banyak solusi bagus di sini, tetapi saya ingin berbagi bagaimana saya menyelesaikan masalah ini sendiri dan berharap ini dapat membantu seseorang yang juga ingin mendapatkan solusinya sendiri.

Setelah merenungkan masalah tersebut, saya mendapatkan dua kesimpulan berikut:

  1. Untuk daftar Lukuran nakan ada jumlah solusi yang sama dimulai dengan L 1 , L 2 ... L n elemen daftar. Karena total ada n!permutasi daftar ukuran n, kami mendapatkan n! / n = (n-1)!permutasi di setiap grup.
  2. Daftar 2 elemen hanya memiliki 2 permutasi => [a,b]dan [b,a].

Dengan menggunakan dua ide sederhana ini, saya memperoleh algoritme berikut:

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

Berikut adalah bagaimana saya menerapkan ini di 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

Bagi saya, jawaban Wikipedia untuk "urutan leksikografik" tampak sangat eksplisit dalam gaya buku masak. Ini mengutip asal abad ke-14 untuk algoritme!

Saya baru saja menulis implementasi cepat di Java dari algoritma Wikipedia sebagai cek dan tidak ada masalah. Tapi apa yang Anda miliki di Q Anda sebagai contoh BUKAN "daftar semua permutasi", tetapi "DAFTAR semua permutasi", jadi wikipedia tidak akan banyak membantu Anda. Anda memerlukan bahasa di mana daftar permutasi dapat dibuat dengan layak. Dan percayalah, daftar yang panjangnya beberapa miliar biasanya tidak ditangani dalam bahasa-bahasa penting. Anda benar-benar menginginkan bahasa pemrograman fungsional non-ketat, di mana daftar adalah objek kelas satu, untuk mengeluarkan barang-barang tanpa membawa mesin mendekati kematian panas Semesta.

Itu mudah. Dalam Haskell standar atau bahasa FP modern lainnya:

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

dan

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

Seperti yang dikatakan WhirlWind, Anda mulai dari awal.

Anda menukar kursor dengan setiap nilai yang tersisa, termasuk kursor itu sendiri, ini semua adalah contoh baru (saya menggunakan int[]dan array.clone()dalam contoh).

Kemudian lakukan permutasi pada semua daftar yang berbeda ini, pastikan kursor berada di kanan.

Jika tidak ada lagi nilai yang tersisa (kursor berada di ujung), cetak daftar. Ini adalah kondisi berhenti.

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

Rekursif selalu membutuhkan usaha mental untuk mempertahankannya. Dan untuk bilangan besar, faktorial mudah besar dan stack overflow dengan mudah menjadi masalah.

Untuk bilangan kecil (3 atau 4, yang paling sering ditemui), beberapa loop cukup sederhana dan lurus ke depan. Sayangnya, jawaban dengan loop tidak dipilih.

Mari kita mulai dengan enumerasi (bukan permutasi). Cukup baca kode tersebut sebagai kode perl semu.

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

Enumeration lebih sering dijumpai daripada permutasi, tetapi jika permutasi dibutuhkan, tambahkan saja ketentuannya:

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

Sekarang jika Anda benar-benar membutuhkan metode umum yang berpotensi untuk daftar besar, kita dapat menggunakan metode radix. Pertama, pertimbangkan masalah pencacahan:

$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

Kenaikan radix pada dasarnya adalah penghitungan angka (dalam dasar jumlah elemen daftar).

Sekarang jika Anda membutuhkan permutaion, cukup tambahkan cek di dalam loop:

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

Edit: Kode di atas seharusnya berfungsi, tetapi untuk permutasi, radix_increment bisa jadi boros. Jadi jika waktu adalah masalah praktis, kita harus mengubah radix_increment menjadi 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                                              

Tentu saja sekarang kode ini secara logika lebih kompleks, saya akan tinggalkan untuk latihan pembaca.


7

masukkan deskripsi gambar di sini

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

Referensi: Geeksforgeeks.org


5

Jika ada yang bertanya-tanya bagaimana melakukan permutasi di javascript.

Ide / pseudocode

  1. pilih satu elemen pada satu waktu
  2. permutasi sisa elemen dan kemudian tambahkan elemen yang dipilih ke semua permutasi

sebagai contoh. 'a' + permute (bc). permute dari bc akan menjadi bc & cb. Sekarang tambahkan keduanya akan menghasilkan abc, acb. Demikian pula, pilih b + permute (ac) akan menyediakan bac, bca ... dan terus berjalan.

sekarang lihat kodenya

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

Luangkan waktu Anda untuk memahami ini. Saya mendapat kode ini dari ( pertumation in JavaScript )


Saya sedang memikirkan sesuatu yang serupa tetapi haruskah Anda tidak menambahkan elemen yang dipetik ke depan dan akhir restParams? Dalam hal ini, untuk 'abc', jika Anda memilih a, maka permutasi 'bc' adalah 'bc' dan 'cb'. Ketika Anda menambahkan 'a' kembali ke daftar, bukankah seharusnya Anda menambahkannya ke depan sebagai 'a + bc' + 'a + cb' tetapi juga di akhir sebagai 'bc + a' + 'cb + a' dari Daftar?
Artimus

Anda akan mendapatkan permutasi tersebut saat permutasi dimulai dengan 'b' dan 'c'. (yaitu langkah kedua dan ketiga dari loop 'for' luar)
Ryan O'Neill

3

Satu lagi di Python, tidak ditempatkan sebagai @ cdiggins, tapi saya pikir itu lebih mudah untuk dipahami

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

Saya berpikir untuk menulis kode untuk mendapatkan permutasi dari bilangan bulat yang diberikan dengan ukuran berapa pun, yaitu, dengan memberikan angka 4567, kami mendapatkan semua kemungkinan permutasi hingga 7654 ... Jadi saya mengerjakannya dan menemukan algoritme dan akhirnya menerapkannya, Di sini adalah kode yang tertulis dalam "c". Anda cukup menyalinnya dan menjalankannya di kompiler open source apa pun. Tetapi beberapa kekurangan menunggu untuk di-debug. Mohon hargai.

Kode:

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

Saya membuat yang ini. berdasarkan penelitian terlalu permutate (qwe, 0, qwe.length-1); Asal tahu saja, Anda bisa melakukannya dengan atau tanpa mundur


3

Berikut adalah metode mainan Ruby yang berfungsi seperti #permutation.to_aitu mungkin lebih terbaca oleh orang gila. Ini sangat lambat, tetapi juga 5 baris.

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

Saya telah menulis solusi rekursif ini di ANSI C. Setiap eksekusi fungsi Permutasi menyediakan satu permutasi berbeda sampai semuanya selesai. Variabel global juga dapat digunakan untuk variabel fakta dan hitungan.

#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

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

Misalnya

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

        }
    }, 3, null, true);
}

keluaran:

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

3

di 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

Berikut adalah kode dengan Python untuk mencetak semua kemungkinan permutasi dari daftar:

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)

Saya telah menggunakan algoritme urutan leksikografik untuk mendapatkan semua kemungkinan permutasi, tetapi algoritme rekursif lebih efisien. Anda dapat menemukan kode untuk algoritma rekursif di sini: Permutasi rekursi 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);
    }
}

ini adalah jawaban yang berguna
Ayaz Alifov

2

Di 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

ini adalah versi java untuk permutasi

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

Berikut adalah implementasi untuk ColdFusion (memerlukan CF10 karena argumen penggabungan ke 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;
}

Berdasarkan solusi js KhanSharp di atas.


Beberapa penjelasan tentang keseluruhan strategi algoritme Anda akan menjadi cara yang baik untuk meningkatkan jawaban ini.
Richard

Jadi mengapa suara negatifnya? Ini adalah implementasi yang berfungsi.
earachefl

@Richard, strategi keseluruhan telah dijelaskan di atas oleh Whirlwind dan lainnya. Saya tidak melihat komentar Anda di semua jawaban lain yang diposting sebagai implementasi tanpa penjelasan.
earachefl

1

Saya tahu ini sangat sangat tua dan bahkan di luar topik dalam stackoverflow hari ini, tetapi saya masih ingin memberikan jawaban javascript yang ramah untuk alasan sederhana yang berjalan di browser Anda.

Saya juga telah menambahkan titik debuggerputus direktif sehingga Anda dapat melangkah melalui kode (diperlukan chrome) untuk melihat cara kerja algoritma ini. Buka konsol dev Anda di chrome ( F12di windows atau CMD + OPTION + Idi mac) lalu klik "Jalankan cuplikan kode". Ini mengimplementasikan algoritme yang persis sama dengan yang disajikan @WhirlWind dalam jawabannya.

Browser Anda harus menghentikan sementara eksekusi sesuai debuggerpetunjuk. Gunakan F8untuk melanjutkan eksekusi kode.

Telusuri kode dan lihat cara kerjanya!

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

Dalam solusi Java berikut, kami memanfaatkan fakta bahwa Strings tidak dapat diubah untuk menghindari kloning set hasil pada setiap iterasi.

Inputnya adalah String, katakan "abc", dan outputnya adalah semua kemungkinan permutasi:

abc
acb
bac
bca
cba
cab

Kode:

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

Pendekatan yang sama dapat diterapkan ke array (bukan string):

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

Ini solusi saya di 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

Anda tidak dapat benar-benar berbicara tentang memecahkan masalah permultation dalam rekursi tanpa memposting implementasi dalam bahasa (dialek) yang memelopori ide tersebut . Nah, demi kelengkapan berikut ini salah satu cara yang bisa dilakukan di 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))))))))

menelepon (permof (list "foo" "bar" "baz"))kami akan mendapatkan:

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

Saya tidak akan membahas detail algoritme karena sudah cukup dijelaskan di posting lain. Idenya sama.

Namun, masalah rekursif cenderung lebih sulit untuk dimodelkan dan dipikirkan dalam medium destruktif seperti Python, C, dan Java, sedangkan di Lisp atau ML dapat diekspresikan secara ringkas.


0

Berikut adalah solusi rekursif di PHP. Postingan WhirlWind mendeskripsikan logika secara akurat. Perlu disebutkan bahwa membuat semua permutasi berjalan dalam waktu faktorial, jadi sebaiknya gunakan pendekatan berulang sebagai gantinya.

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

Fungsi strDiff mengambil dua string, s1dan s2, dan mengembalikan string baru dengan semua yang ada di dalamnya s1tanpa elemen di s2(materi duplikat). Jadi, strDiff('finish','i')=> 'fnish'('i' kedua tidak dihapus).


0

Berikut adalah algoritma di R, jika ada yang perlu menghindari memuat pustaka tambahan seperti yang harus saya lakukan.

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

Contoh penggunaan:

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

Solusinya menunjukkan bahwa Anda belum mengubah string sesuai persyaratan. Permutasi kedua seharusnya PYTHNO
Rahul Kadukar

0

Ini adalah kode rekursif untuk java, idenya adalah memiliki awalan yang menambahkan sisa karakter:

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

Contoh:

Masukan = "ABC"; Keluaran:

ABC ACB BAC BCA CAB CBA


1
Ide bagus, tetapi saya pikir Anda juga harus menghapus charAt (i) dari strsaat memanggil secara rekursif, jika tidak maka tidak akan berhenti.
Crystal

1
Jika Anda akan menyalin dan menempel, Anda perlu (1) memberikan atribusi dan (2) memastikan semua pengeditan sudah benar. Untuk atribusi, ini adalah perm1 dari introcs.cs.princeton.edu/java/23recursion/… . Juga hasil edit Anda salah: str.substring (0, i) + str.substring (i + 1, n) tidak sama dengan str, karena yang pertama menghilangkan karakter pada posisi i.
kevin

0

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

Berikut adalah solusi non-rekursif dalam C ++ yang menyediakan permutasi berikutnya dalam urutan menaik, mirip dengan fungsionalitas yang disediakan oleh 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());
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.