Algoritmo per generare tutte le possibili permutazioni di una lista?


119

Diciamo che ho un elenco di n elementi, so che ce ne sono n! possibili modi per ordinare questi elementi. Qual è un algoritmo per generare tutti i possibili ordinamenti di questo elenco? Esempio, ho la lista [a, b, c]. L'algoritmo restituirà [[a, b, c], [a, c, b,], [b, a, c], [b, c, a], [c, a, b], [c, b , a]].

Sto leggendo questo qui http://en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations

Ma Wikipedia non è mai stata brava a spiegare. Non ne capisco molto.


5
Una volta ho scritto un'ampia risposta a un'altra domanda sulla generazione di permutazioni. Penso che sarà di vostro interesse: stackoverflow.com/questions/1506078/...
Joren

2
Questo può risolvere il tuo problema en.wikipedia.org/wiki/Heap's_algorithm
Felix

Risposte:


96

Fondamentalmente, per ogni elemento da sinistra a destra, vengono generate tutte le permutazioni degli elementi rimanenti (e ognuna viene aggiunta con gli elementi correnti). Questo può essere fatto in modo ricorsivo (o iterativo se ti piace il dolore) fino a raggiungere l'ultimo elemento, a quel punto c'è un solo ordine possibile.

Quindi con la lista [1,2,3,4] vengono generate tutte le permutazioni che iniziano con 1, poi tutte le permutazioni che iniziano con 2, poi 3 e poi 4.

Ciò riduce efficacemente il problema da uno di trovare permutazioni di un elenco di quattro elementi a un elenco di tre elementi. Dopo aver ridotto a 2 e poi 1 elenchi di elementi, verranno trovati tutti.
Esempio che mostra le permutazioni di processo utilizzando 3 palline colorate:
Sfere colorate rosse, verdi e blu hanno ordinato l'immagine di permutazioni(da https://en.wikipedia.org/wiki/Permutation#/media/File:Permutations_RGB.svg - https://commons.wikimedia.org/wiki/File:Permutations_RGB. svg )


2
All'inizio ho pensato anche a questo, ma poi l'elemento corrente non è stato inserito tra alcuni dei seguenti. Quindi non tutte le permutazioni verrebbero generate.
fent

@LLer mi dispiace, ho aggiornato la mia risposta da "folllowing" a "rimanente" per chiarire. Funziona bene però. Controllalo scrivendo il codice e verificando di ottenere 4! risultati diversi.
WhirlWind

2
int permutazioni (int n, vettore <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 ++) {permutations (n ​​+ 1, a); if (i <a.size ()) int temp = a [n], a [n] = a [i], a [i] = temp; }} return num_permutations; } int main (void) {vector <int> v; v.push_back (1); ... restituisce permutazioni (0, v); }
Somesh

Oops, non sono sicuro di come formattare il codice in un commento ... Ho provato il codice con 7 e ho ottenuto 5040. Grazie a @WhirlWind per il suggerimento.
Somesh

questo algoritmo non cambia se puoi avere 2 o 3 palline rosse n. 1, invece di solo 1 di ogni colore?
Alexander Mills

26

Ecco un algoritmo in Python che funziona in posizione su un 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

Puoi provare tu stesso il codice qui: http://repl.it/J9v


Puoi spiegare la parte di rendimento? Non ho potuto eseguire il codice a secco. Grazie in anticipo.
Agniswar Bakshi,

La domanda Stack Overflow su stackoverflow.com/questions/104420/… afferma che esiste un modulo libreria standard nelle versioni 2.6 in poi e ha una risposta che fornisce una soluzione di 6 righe in una funzione per ottenere permutazioni di lista.
Edward,

@Agniswar A colpo d'occhio, l'istruzione yield viene utilizzata per definire i generatori, sostituendo il ritorno di una funzione per fornire un risultato al suo chiamante senza distruggere le variabili locali. A differenza di una funzione, dove ad ogni chiamata inizia con un nuovo insieme di variabili, un generatore riprenderà l'esecuzione da dove era stata interrotta. pythoncentral.io/python-generators-and-yield-keyword
MSS

Questa soluzione non funzionerà quando si gestisce un elenco di voci identiche.
KaiserKatze

Grazie per la condivisione. Questo è intuitivo ed efficiente, sebbene l'output non sia in ordine lessicografico.
Sam

16

Ci sono già molte buone soluzioni qui, ma vorrei condividere come ho risolto questo problema da solo e spero che questo possa essere utile per qualcuno che vorrebbe anche trovare la propria soluzione.

Dopo aver riflettuto un po 'sul problema, sono giunto a due seguenti conclusioni:

  1. Per l'elenco Ldelle dimensioni nci sarà lo stesso numero di soluzioni che iniziano con L 1 , L 2 ... L n elementi della lista. Poiché in totale ci sono n!permutazioni dell'elenco di dimensioni n, otteniamo n! / n = (n-1)!permutazioni in ogni gruppo.
  2. L'elenco di 2 elementi ha solo 2 permutazioni => [a,b]e [b,a].

Utilizzando queste due semplici idee ho ricavato il seguente algoritmo:

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

Ecco come l'ho implementato in 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

La risposta di Wikipedia per "ordine lessicografico" mi sembra perfettamente esplicita in stile libro di cucina. Cita un'origine del XIV secolo per l'algoritmo!

Ho appena scritto una rapida implementazione in Java dell'algoritmo di Wikipedia come controllo e non ci sono stati problemi. Ma quello che hai nella tua Q come esempio NON è "elenca tutte le permutazioni", ma "una LISTA di tutte le permutazioni", quindi wikipedia non ti sarà di grande aiuto. Hai bisogno di una lingua in cui gli elenchi di permutazioni siano fattibili. E credimi, elenchi lunghi pochi miliardi di solito non sono gestiti in lingue imperative. Vuoi davvero un linguaggio di programmazione funzionale non rigoroso, in cui le liste sono un oggetto di prima classe, per tirare fuori le cose senza avvicinare la macchina alla morte termica dell'Universo.

Questo è facile. In Haskell standard o in qualsiasi linguaggio FP moderno:

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

e

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

Come ha detto WhirlWind, inizi dall'inizio.

Scambia il cursore con ogni valore rimanente, incluso il cursore stesso, queste sono tutte nuove istanze (ho usato un int[]e array.clone()nell'esempio).

Quindi esegui permutazioni su tutti questi diversi elenchi, assicurandoti che il cursore sia uno a destra.

Quando non ci sono più valori rimanenti (il cursore è alla fine), stampa l'elenco. Questa è la condizione di arresto.

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

La ricorsività richiede sempre uno sforzo mentale per essere mantenuta. E per grandi numeri, il fattoriale è facilmente enorme e l'overflow dello stack sarà facilmente un problema.

Per piccoli numeri (3 o 4, che si incontrano principalmente), più cicli sono abbastanza semplici e diretti. È un peccato che le risposte con loop non siano state votate.

Cominciamo con l'enumerazione (piuttosto che la permutazione). Leggi semplicemente il codice come pseudo codice Perl.

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

L'enumerazione si incontra più spesso della permutazione, ma se è necessaria la permutazione, aggiungi semplicemente le condizioni:

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

Ora se hai davvero bisogno di un metodo generale potenzialmente per grandi elenchi, possiamo usare il metodo radix. Innanzitutto, considera il problema dell'enumerazione:

$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

L'incremento della radice è essenzialmente un conteggio numerico (nella base del numero di elementi della lista).

Ora se hai bisogno di permuta, aggiungi i controlli all'interno del ciclo:

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

Modifica: il codice sopra dovrebbe funzionare, ma per la permutazione, radix_increment potrebbe essere uno spreco. Quindi, se il tempo è una preoccupazione pratica, dobbiamo cambiare radix_increment in 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                                              

Ovviamente ora questo codice è logicamente più complesso, lascio all'esercizio del lettore.


7

inserisci qui la descrizione dell'immagine

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

Riferimento: Geeksforgeeks.org


5

Se qualcuno si chiede come eseguire la permutazione in javascript.

Idea / pseudocodice

  1. scegli un elemento alla volta
  2. permuta il resto dell'elemento e quindi aggiungi l'elemento selezionato a tutta la permutazione

per esempio. 'a' + permuto (bc). permute di bc sarebbe bc & cb. Ora aggiungi questi due darà abc, acb. allo stesso modo, scegli b + permute (ac) provocherà bac, bca ... e continuerai.

ora guarda il codice

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

Prenditi il ​​tuo tempo per capirlo. Ho ricevuto questo codice da ( pertumation in JavaScript )


Stavo pensando a qualcosa di simile, ma non dovresti aggiungere l'elemento selezionato sia alla parte anteriore che alla fine dei restParams? In questo caso, per "abc", se scegli a, le permutazioni "bc" sono "bc" e "cb". Quando aggiungi "a" indietro all'elenco, non dovresti aggiungerlo in primo piano come "a + bc" + "a + cb" ma anche alla fine come "bc + a" + "cb + a" di la lista?
Artimus

Otterrai queste permutazioni quando permuti che inizi con "b" e "c" rispettivamente. (ovvero la seconda e la terza manche del ciclo "for" esterno)
Ryan O'Neill

3

Un altro in Python, non è a posto come quello di @ cdiggins, ma penso che sia più facile da capire

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

Stavo pensando di scrivere un codice per ottenere le permutazioni di qualsiasi numero intero di qualsiasi dimensione, cioè, fornendo un numero 4567 otteniamo tutte le possibili permutazioni fino a 7654 ... Quindi ci ho lavorato e ho trovato un algoritmo e alla fine l'ho implementato, Qui è il codice scritto in "c". Puoi semplicemente copiarlo ed eseguirlo su qualsiasi compilatore open source. Ma alcuni difetti attendono di essere corretti. Per favore, apprezzate.

Codice:

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

Ho creato questo. basato sulla ricerca troppo permutato (qwe, 0, qwe.length-1); Solo così sai, puoi farlo con o senza backtrack


3

Ecco un metodo Ruby giocattolo che funziona in #permutation.to_aquesto modo potrebbe essere più leggibile ai pazzi. È molto lento, ma anche 5 righe.

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

Ho scritto questa soluzione ricorsiva in ANSI C.Ogni esecuzione della funzione Permutate fornisce una permutazione diversa fino al completamento di tutte. Le variabili globali possono essere utilizzate anche per le variabili fact e 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

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

Per esempio

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

        }
    }, 3, null, true);
}

produzione:

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

3

in 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

Ecco il codice in Python per stampare tutte le possibili permutazioni di un elenco:

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)

Ho utilizzato un algoritmo di ordine lessicografico per ottenere tutte le possibili permutazioni, ma un algoritmo ricorsivo è più efficiente. Puoi trovare il codice per l'algoritmo ricorsivo qui: permutazioni di ricorsione 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);
    }
}

è una risposta utile
Ayaz Alifov

2

In 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

questa è una versione java per la permutazione

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

Ecco un'implementazione per ColdFusion (richiede CF10 a causa dell'argomento merge di 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;
}

Basato sulla soluzione js di KhanSharp sopra.


Qualche spiegazione della strategia generale del tuo algoritmo sarebbe un buon modo per migliorare questa risposta.
Richard,

Allora perché il voto negativo? Questa è un'implementazione funzionante.
earachefl

@Richard, la strategia generale è stata spiegata sopra da Whirlwind e altri. Non vedo il tuo commento su tutte le altre risposte pubblicate come implementazioni senza spiegazioni.
earachefl

1

So che questo è molto vecchio e persino fuori tema nello stackoverflow di oggi, ma volevo comunque contribuire con una risposta javascript amichevole per il semplice motivo che viene eseguito nel tuo browser.

Ho anche aggiunto il debuggerpunto di interruzione della direttiva in modo da poter passare attraverso il codice (chrome richiesto) per vedere come funziona questo algoritmo. Apri la tua console di sviluppo in Chrome ( F12in Windows o CMD + OPTION + IMac), quindi fai clic su "Esegui snippet di codice". Ciò implementa lo stesso algoritmo esatto che @WhirlWind ha presentato nella sua risposta.

Il tuo browser dovrebbe sospendere l'esecuzione alla debuggerdirettiva. Utilizzare F8per continuare l'esecuzione del codice.

Scorri il codice e guarda come funziona!

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

Nella seguente soluzione Java ci avvantaggiamo del fatto che le stringhe sono immutabili per evitare di clonare il set di risultati ad ogni iterazione.

L'input sarà una stringa, diciamo "abc", e l'output sarà tutte le possibili permutazioni:

abc
acb
bac
bca
cba
cab

Codice:

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

Lo stesso approccio può essere applicato agli array (invece di una stringa):

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

È la mia soluzione su 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

Non si può davvero parlare di risolvere un problema di permultazione nella ricorsione senza pubblicare un'implementazione in un linguaggio (dialetto) che ha aperto la strada all'idea . Quindi, per motivi di completezza, ecco uno dei modi che possono essere eseguiti in 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))))))))

chiamando (permof (list "foo" "bar" "baz"))avremo:

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

Non entrerò nei dettagli dell'algoritmo perché è stato spiegato abbastanza in altri post. L'idea è la stessa.

Tuttavia, i problemi ricorsivi tendono ad essere molto più difficili da modellare e pensare in mezzi distruttivi come Python, C e Java, mentre in Lisp o ML possono essere espressi in modo conciso.


0

Ecco una soluzione ricorsiva in PHP. Il post di WhirlWind descrive accuratamente la logica. Vale la pena ricordare che la generazione di tutte le permutazioni viene eseguita in tempo fattoriale, quindi potrebbe essere una buona idea utilizzare invece un approccio iterativo.

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

La funzione strDiff accetta due stringhe s1e s2, e restituisce una nuova stringa con tutto in s1senza elementi in s2(duplicati materia). Quindi, strDiff('finish','i')=> 'fnish'(la seconda 'i' non viene rimossa).


0

Ecco un algoritmo in R, nel caso qualcuno debba evitare di caricare librerie aggiuntive come ho dovuto.

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

Utilizzo di esempio:

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

La soluzione mostra che non hai permutato la stringa secondo il requisito. La seconda permutazione avrebbe dovuto essere PYTHNO
Rahul Kadukar

0

Questo è un codice ricorsivo per java, l'idea è di avere un prefisso che aggiunga il resto dei caratteri:

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

Esempio:

Input = "ABC"; Produzione:

ABC ACB BAC BCA CAB CBA


1
Bella idea, ma penso che dovresti anche rimuovere charAt (i) da strquando chiami ricorsivamente, altrimenti non terminerà.
Crystal

1
Se intendi copiare e incollare, devi (1) fornire l'attribuzione e (2) assicurarti che tutte le modifiche siano corrette. Per l'attribuzione, questo è perm1 da introcs.cs.princeton.edu/java/23recursion/… . Inoltre la tua modifica non è corretta: str.substring (0, i) + str.substring (i + 1, n) non è la stessa di str, poiché il primo omette il carattere nella posizione i.
kevin

0

Giusto per essere completo, 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

Ecco una soluzione non ricorsiva in C ++ che fornisce la successiva permutazione in ordine crescente, in modo simile alla funzionalità fornita da 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());
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.