Elencando tutte le permutazioni di una stringa / intero


159

Un compito comune nella programmazione delle interviste (non dalla mia esperienza di interviste però) è quello di prendere una stringa o un numero intero ed elencare ogni possibile permutazione.

C'è un esempio di come ciò viene fatto e della logica alla base della risoluzione di un tale problema?

Ho visto alcuni frammenti di codice ma non sono stati ben commentati / spiegati e quindi difficili da seguire.


1
Ecco una domanda a Permutations con alcune buone risposte esplicative, incluso un grafico , ma non in C #.
utente sconosciuto

Risposte:


152

Prima di tutto: profuma di ricorsione ovviamente!

Dato che anche tu volevi conoscere il principio, ho fatto del mio meglio per spiegarlo come linguaggio umano. Penso che la ricorsione sia molto facile la maggior parte delle volte. Devi solo cogliere due passaggi:

  1. Il primo passo
  2. Tutti gli altri passaggi (tutti con la stessa logica)

Nel linguaggio umano :

In breve:
1. La permutazione di 1 elemento è un elemento.
2. La permutazione di un insieme di elementi è un elenco di ciascuno degli elementi, concatenato con ogni permutazione degli altri elementi.

Esempio:

Se il set ha solo un elemento ->
restituiscilo.
perm (a) -> a

Se l'insieme ha due caratteri: per ogni elemento in esso contenuto: restituisce l'elemento, con la permutazione del resto degli elementi aggiunti, in questo modo:

perm (ab) ->

a + perm (b) -> ab

b + perm (a) -> ba

Inoltre: per ogni personaggio nel set: restituisce un personaggio, concatenato con una perumazione di> il resto del set

perm (abc) ->

a + perm (bc) -> abc , acb

b + perm (ac) -> bac , bca

c + perm (ab) -> cabina , cba

perm (abc ... z) ->

a + perm (...), b + perm (....)
....

Ho trovato il pseudocodice su http://www.programmersheaven.com/mb/Algorithms/369713/369713/permutation-algorithm-help/ :

makePermutations(permutation) {
  if (length permutation < required length) {
    for (i = min digit to max digit) {
      if (i not in permutation) {
        makePermutations(permutation+i)
      }
    }
  }
  else {
    add permutation to list
  }
}

C #

OK, e qualcosa di più elaborato (e poiché è taggato c #), da http://radio.weblogs.com/0111551/stories/2002/10/14/permutations.html : piuttosto lungo, ma ho deciso di copiarlo comunque, quindi il post non dipende dall'originale.

La funzione accetta una stringa di caratteri e annota tutte le possibili permutazioni di quella stringa esatta, quindi ad esempio, se è stato fornito "ABC", dovrebbe essere visualizzato:

ABC, ACB, BAC, BCA, CAB, CBA.

Codice:

class Program
{
    private static void Swap(ref char a, ref char b)
    {
        if (a == b) return;

        var temp = a;
        a = b;
        b = temp;
    }

    public static void GetPer(char[] list)
    {
        int x = list.Length - 1;
        GetPer(list, 0, x);
    }

    private static void GetPer(char[] list, int k, int m)
    {
        if (k == m)
        {
            Console.Write(list);
        }
        else
            for (int i = k; i <= m; i++)
            {
                   Swap(ref list[k], ref list[i]);
                   GetPer(list, k + 1, m);
                   Swap(ref list[k], ref list[i]);
            }
    }

    static void Main()
    {
        string str = "sagiv";
        char[] arr = str.ToCharArray();
        GetPer(arr);
    }
}

21
Per maggiore chiarezza, chiamerei k "recursionDepth" e chiamerei m "maxDepth".
Nerf Herder,

3
Il 2 ° swap ( Swap(ref list[k], ref list[i]);) non è necessario.
dance2die,

1
Grazie per questa soluzione Ho creato questo violino ( dotnetfiddle.net/oTzihw ) da esso (con un nome appropriato invece di k e m). Per quanto ho capito l'algo, è necessario il secondo Swap (per tornare indietro) poiché si modifica l'array originale sul posto.
Andrew

3
un aspetto secondario: sembra che il metodo Swap sia meglio implementato con una variabile buffer temporanea e non usando gli XOR ( dotnetperls.com/swap )
Sergioet,

7
Usando le tuple C # 7 puoi rendere lo scambio molto più elegante:(a[x], a[y]) = (a[y], a[x]);
Darren

81

Sono solo due righe di codice se LINQ è autorizzato a utilizzare. Si prega di vedere la mia risposta qui .

MODIFICARE

Ecco la mia funzione generica che può restituire tutte le permutazioni (non combinazioni) da un elenco di T:

static IEnumerable<IEnumerable<T>>
    GetPermutations<T>(IEnumerable<T> list, int length)
{
    if (length == 1) return list.Select(t => new T[] { t });

    return GetPermutations(list, length - 1)
        .SelectMany(t => list.Where(e => !t.Contains(e)),
            (t1, t2) => t1.Concat(new T[] { t2 }));
}

Esempio:

IEnumerable<IEnumerable<int>> result =
    GetPermutations(Enumerable.Range(1, 3), 3);

Output - un elenco di elenchi di numeri interi:

{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,1,2} {3,2,1}

Poiché questa funzione utilizza LINQ, richiede .net 3.5 o versioni successive.


3
combinazioni e permutazioni sono cose diverse. è simile, ma la tua risposta sembra rispondere a un problema diverso rispetto a tutte le permutazioni di un insieme di elementi.
Shawn Kovac

@ShawnKovac, grazie per averlo segnalato! Ho aggiornato il mio codice dalla combinazione alla permutazione.
Pengyang,

1
@Pengyang Ho guardato la tua altra risposta e dirò che questo mi ha aiutato moltissimo, ma ho un'altra situazione che non so se hai indicato il modo corretto di risolverlo. Volevo trovare tutte le permutazioni di una parola come "HALLOWEEN", ma ho scoperto che volevo includere anche "L" e "E" nel set di risultati. Nelle mie iterazioni cerco il tuo metodo dando una lunghezza maggiore ad ogni iterazione (lunghezza ++) e mi aspetto che, data la lunghezza della parola HALLOWEEN (9 caratteri), otterrò risultati lunghi 9 caratteri ... ma non è così: Ne ottengo solo 7 (1 L e 1 E sono omesse)
MegaMark

Vorrei anche sottolineare che NON voglio una situazione in cui vedo 9 'H come' H 'appare una sola volta nella parola.
MegaMark,

4
@MegaMark Questa funzione richiede che gli elementi siano univoci. Prova questo:const string s = "HALLOWEEN"; var result = GetPermutations(Enumerable.Range(0, s.Length), s.Length).Select(t => t.Select(i => s[i]));
Pengyang,

36

Qui ho trovato la soluzione. È stato scritto in Java, ma l'ho convertito in C #. Spero che ti possa aiutare.

Inserisci qui la descrizione dell'immagine

Ecco il codice in C #:

static void Main(string[] args)
{
    string str = "ABC";
    char[] charArry = str.ToCharArray();
    Permute(charArry, 0, 2);
    Console.ReadKey();
}

static void Permute(char[] arry, int i, int n)
{
    int j;
    if (i==n)
        Console.WriteLine(arry);
    else
    {
        for(j = i; j <=n; j++)
        {
            Swap(ref arry[i],ref arry[j]);
            Permute(arry,i+1,n);
            Swap(ref arry[i], ref arry[j]); //backtrack
        }
    }
}

static void Swap(ref char a, ref char b)
{
    char tmp;
    tmp = a;
    a=b;
    b = tmp;
}

Viene portato da un'altra lingua? Sicuramente +1 per l'immagine, perché aggiunge davvero valore. Tuttavia, il codice stesso sembra avere un certo potenziale di miglioramento. Alcune parti minori non sono necessarie ma, cosa più importante, sto ricevendo questa sensazione C ++ quando inviamo qualcosa e facciamo cose al suo posto invece di fornire parametri in e recuperare un valore restituito. In effetti, ho usato la tua immagine per implementare un codice C # con stile C # (lo stile è la mia percezione personale, ovviamente), e mi ha aiutato molto, quindi quando lo posterò lo ruberò sicuramente a te (e merito per questo).
Konrad Viltersten,

21

La ricorsione non è necessaria, ecco alcune buone informazioni su questa soluzione.

var values1 = new[] { 1, 2, 3, 4, 5 };

foreach (var permutation in values1.GetPermutations())
{
    Console.WriteLine(string.Join(", ", permutation));
}

var values2 = new[] { 'a', 'b', 'c', 'd', 'e' };

foreach (var permutation in values2.GetPermutations())
{
    Console.WriteLine(string.Join(", ", permutation));
}

Console.ReadLine();

Sono stato usato questo algoritmo per anni, ha O (N) tempo e complessità spaziale per calcolare ogni permutazione .

public static class SomeExtensions
{
    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> enumerable)
    {
        var array = enumerable as T[] ?? enumerable.ToArray();

        var factorials = Enumerable.Range(0, array.Length + 1)
            .Select(Factorial)
            .ToArray();

        for (var i = 0L; i < factorials[array.Length]; i++)
        {
            var sequence = GenerateSequence(i, array.Length - 1, factorials);

            yield return GeneratePermutation(array, sequence);
        }
    }

    private static IEnumerable<T> GeneratePermutation<T>(T[] array, IReadOnlyList<int> sequence)
    {
        var clone = (T[]) array.Clone();

        for (int i = 0; i < clone.Length - 1; i++)
        {
            Swap(ref clone[i], ref clone[i + sequence[i]]);
        }

        return clone;
    }

    private static int[] GenerateSequence(long number, int size, IReadOnlyList<long> factorials)
    {
        var sequence = new int[size];

        for (var j = 0; j < sequence.Length; j++)
        {
            var facto = factorials[sequence.Length - j];

            sequence[j] = (int)(number / facto);
            number = (int)(number % facto);
        }

        return sequence;
    }

    static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }

    private static long Factorial(int n)
    {
        long result = n;

        for (int i = 1; i < n; i++)
        {
            result = result * i;
        }

        return result;
    }
}

Funziona fuori dalla scatola!
revobtz,

1
forse non capisco la notazione O (n). la N non si riferisce a quanti "loop interni" sono necessari per far funzionare il tuo algoritmo? mi sembra che se hai N numeri, sembra che sia O (N * N!) (perché N! volte deve fare N swap). Inoltre, deve eseguire moltissime copie di array. Questo codice è "pulito", ma non lo userei.
Eric Frazer,

@ericfrazer Ogni permutazione usa solo una copia dell'array, e O(N-1)per la sequenza e O(N)per gli swap, che è O(N). E lo sto ancora usando in produzione ma con un refactor per generare una sola permutazione come: GetPermutation(i)where 0 <= i <= N!-1. Sarò felice di usare qualcosa con prestazioni migliori di così, quindi sii libero di chiamare un riferimento per qualcosa di meglio, la maggior parte delle alternative utilizza O(N!)in memoria, quindi potresti controllare anche quello.
Najera,

11
void permute (char *str, int ptr) {
  int i, len;
  len = strlen(str);
  if (ptr == len) {
    printf ("%s\n", str);
    return;
  }

  for (i = ptr ; i < len ; i++) {
    swap (&str[ptr], &str[i]);
    permute (str, ptr + 1);
    swap (&str[ptr], &str[i]);
  }
}

È possibile scrivere la funzione di scambio per scambiare i caratteri.
Questo deve essere chiamato come permute (stringa, 0);


5
Sembra C, non C #.
Jon Schneider,

9

Prima di tutto, gli insiemi hanno permutazioni, non stringhe o numeri interi, quindi suppongo che tu intenda "l'insieme di caratteri in una stringa".

Nota che un set di dimensioni n ha n! n-permutazioni.

Il seguente pseudocodice (da Wikipedia), chiamato con k = 1 ... n! darà tutte le permutazioni:

function permutation(k, s) {
    for j = 2 to length(s) {
        swap s[(k mod j) + 1] with s[j]; // note that our array is indexed starting at 1
        k := k / j; // integer division cuts off the remainder
    }
    return s;
}

Ecco l'equivalente codice Python (per gli indici di array basati su 0):

def permutation(k, s):
    r = s[:]
    for j in range(2, len(s)+1):
        r[j-1], r[k%j] = r[k%j], r[j-1]
        k = k/j+1
    return r

5
che lingua è questa? la domanda è contrassegnata con C #. non so cosa k := k / j;.
Shawn Kovac

8

Versione leggermente modificata in C # che produce le permutazioni necessarie in una matrice di QUALSIASI tipo.

    // USAGE: create an array of any type, and call Permutations()
    var vals = new[] {"a", "bb", "ccc"};
    foreach (var v in Permutations(vals))
        Console.WriteLine(string.Join(",", v)); // Print values separated by comma


public static IEnumerable<T[]> Permutations<T>(T[] values, int fromInd = 0)
{
    if (fromInd + 1 == values.Length)
        yield return values;
    else
    {
        foreach (var v in Permutations(values, fromInd + 1))
            yield return v;

        for (var i = fromInd + 1; i < values.Length; i++)
        {
            SwapValues(values, fromInd, i);
            foreach (var v in Permutations(values, fromInd + 1))
                yield return v;
            SwapValues(values, fromInd, i);
        }
    }
}

private static void SwapValues<T>(T[] values, int pos1, int pos2)
{
    if (pos1 != pos2)
    {
        T tmp = values[pos1];
        values[pos1] = values[pos2];
        values[pos2] = tmp;
    }
}

Un piccolo avvertimento con questa implementazione: funziona correttamente solo se non si tenta di memorizzare il valore di enumerazione. Se provi a fare qualcosa del genere, Permutations(vals).ToArray()finisci con N riferimenti allo stesso array. Se vuoi essere in grado di memorizzare i risultati devi creare manualmente una copia. Ad esempioPermutations(values).Select(v => (T[])v.Clone())
Pharap il

8
class Program
{
    public static void Main(string[] args)
    {
        Permutation("abc");
    }

    static void Permutation(string rest, string prefix = "")
    {
        if (string.IsNullOrEmpty(rest)) Console.WriteLine(prefix);

        // Each letter has a chance to be permutated
        for (int i = 0; i < rest.Length; i++)
        {                
            Permutation(rest.Remove(i, 1), prefix + rest[i]);
        }
    }
}

1
Soluzione folle. Grazie!
Cristian E.

7

Mi è piaciuto l' approccio FBryant87 poiché è semplice. Purtroppo, come molte altre "soluzioni" non offre tutte le permutazioni o, ad esempio, un numero intero se contiene la stessa cifra più di una volta. Prendi 656123 come esempio. La linea:

var tail = chars.Except(new List<char>(){c});

Tranne usando causerà tutte le occorrenze di essere rimosso, cioè quando c = 6, due cifre vengono rimossi e si sono lasciati con ad esempio 5123. Poiché nessuna delle soluzioni Ho provato risolto questo, decisi di cercare di risolvere io stesso da FBryant87 s' codice come base. Questo è quello che mi è venuto in mente:

private static List<string> FindPermutations(string set)
    {
        var output = new List<string>();
        if (set.Length == 1)
        {
            output.Add(set);
        }
        else
        {
            foreach (var c in set)
            {
                // Remove one occurrence of the char (not all)
                var tail = set.Remove(set.IndexOf(c), 1);
                foreach (var tailPerms in FindPermutations(tail))
                {
                    output.Add(c + tailPerms);
                }
            }
        }
        return output;
    }

Semplicemente rimuovo la prima occorrenza trovata usando .Remove e .IndexOf. Sembra funzionare almeno come previsto per il mio utilizzo. Sono sicuro che potrebbe essere reso più intelligente.

Una cosa da notare però: l'elenco risultante può contenere duplicati, quindi assicurati di restituire il metodo, ad esempio un HashSet, o di rimuovere i duplicati dopo il ritorno utilizzando qualsiasi metodo che ti piace.


Funziona come un'assoluta bellezza, prima ho scoperto che gestisce personaggi duplicati +1
Jack Casey,


5

Ecco un'implementazione F # puramente funzionale:


let factorial i =
    let rec fact n x =
        match n with
        | 0 -> 1
        | 1 -> x
        | _ -> fact (n-1) (x*n)
    fact i 1

let swap (arr:'a array) i j = [| for k in 0..(arr.Length-1) -> if k = i then arr.[j] elif k = j then arr.[i] else arr.[k] |]

let rec permutation (k:int,j:int) (r:'a array) =
    if j = (r.Length + 1) then r
    else permutation (k/j+1, j+1) (swap r (j-1) (k%j))

let permutations (source:'a array) = seq { for k = 0 to (source |> Array.length |> factorial) - 1 do yield permutation (k,2) source }

Le prestazioni possono essere notevolmente migliorate modificando lo swap per sfruttare la natura mutevole degli array CLR, ma questa implementazione è sicura per quanto riguarda l'array di origine e ciò può essere desiderabile in alcuni contesti. Inoltre, per le matrici con più di 16 elementi int deve essere sostituito con tipi con maggiore / arbitraria precisione poiché 17 fattoriale provoca un overflow int32.


5

Ecco una semplice soluzione in c # usando la ricorsione,

void Main()
{
    string word = "abc";
    WordPermuatation("",word);
}

void WordPermuatation(string prefix, string word)
{
    int n = word.Length;
    if (n == 0) { Console.WriteLine(prefix); }
    else
    {
        for (int i = 0; i < n; i++)
        {
            WordPermuatation(prefix + word[i],word.Substring(0, i) + word.Substring(i + 1, n - (i+1)));
        }
    }
}

Grazie per la soluzione molto semplice e breve! :)
Kristaps Vilerts,

4

Ecco una funzione di permutaion di facile comprensione per stringa e numero intero come input. Con questo è anche possibile impostare la lunghezza dell'output (che nel caso normale è uguale alla lunghezza dell'input)

Corda

    static ICollection<string> result;

    public static ICollection<string> GetAllPermutations(string str, int outputLength)
    {
        result = new List<string>();
        MakePermutations(str.ToCharArray(), string.Empty, outputLength);
        return result;
    }

    private static void MakePermutations(
       char[] possibleArray,//all chars extracted from input
       string permutation,
       int outputLength//the length of output)
    {
         if (permutation.Length < outputLength)
         {
             for (int i = 0; i < possibleArray.Length; i++)
             {
                 var tempList = possibleArray.ToList<char>();
                 tempList.RemoveAt(i);
                 MakePermutations(tempList.ToArray(), 
                      string.Concat(permutation, possibleArray[i]), outputLength);
             }
         }
         else if (!result.Contains(permutation))
            result.Add(permutation);
    }

e per Integer basta cambiare il metodo del chiamante e MakePermutations () rimane intatto:

    public static ICollection<int> GetAllPermutations(int input, int outputLength)
    {
        result = new List<string>();
        MakePermutations(input.ToString().ToCharArray(), string.Empty, outputLength);
        return result.Select(m => int.Parse(m)).ToList<int>();
    }

esempio 1: GetAllPermutations ("abc", 3); "abc" "acb" "bac" "bca" "cab" "cba"

esempio 2: GetAllPermutations ("abcd", 2); "ab" "ac" "ad" "ba" "bc" "bd" "ca" "cb" "cd" "da" "db" "dc"

esempio 3: GetAllPermutations (486,2); 48 46 84 86 64 68


Mi piace la tua soluzione perché è facile da capire, grazie per quello! Eppure ho scelto di andare con quello: stackoverflow.com/questions/756055/… . Il motivo è che ToList, ToArray e RemoveAt hanno tutti una complessità temporale di O (N). Quindi in pratica devi esaminare tutti gli elementi della raccolta (vedi stackoverflow.com/a/15042066/1132522 ). Lo stesso vale per l'int dove sostanzialmente ripassate tutti gli elementi alla fine per convertirli in int. Concordo sul fatto che ciò non ha molto impatto su "abc" o 486.
Andrew

2

Ecco la funzione che stamperà tutta la permutaion. Questa funzione implementa la logica Spiegata da Peter.

public class Permutation
{
    //http://www.java2s.com/Tutorial/Java/0100__Class-Definition/RecursivemethodtofindallpermutationsofaString.htm

    public static void permuteString(String beginningString, String endingString)
    {           

        if (endingString.Length <= 1)
            Console.WriteLine(beginningString + endingString);
        else
            for (int i = 0; i < endingString.Length; i++)
            {

                String newString = endingString.Substring(0, i) + endingString.Substring(i + 1);

                permuteString(beginningString + endingString.ElementAt(i), newString);

            }
    }
}

    static void Main(string[] args)
    {

        Permutation.permuteString(String.Empty, "abc");
        Console.ReadLine();

    }

2

Di seguito è la mia implementazione della permutazione. Non importa i nomi delle variabili, mentre lo facevo per divertimento :)

class combinations
{
    static void Main()
    {

        string choice = "y";
        do
        {
            try
            {
                Console.WriteLine("Enter word :");
                string abc = Console.ReadLine().ToString();
                Console.WriteLine("Combinatins for word :");
                List<string> final = comb(abc);
                int count = 1;
                foreach (string s in final)
                {
                    Console.WriteLine("{0} --> {1}", count++, s);
                }
                Console.WriteLine("Do you wish to continue(y/n)?");
                choice = Console.ReadLine().ToString();
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc);
            }
        } while (choice == "y" || choice == "Y");
    }

    static string swap(string test)
    {
        return swap(0, 1, test);
    }

    static List<string> comb(string test)
    {
        List<string> sec = new List<string>();
        List<string> first = new List<string>();
        if (test.Length == 1) first.Add(test);
        else if (test.Length == 2) { first.Add(test); first.Add(swap(test)); }
        else if (test.Length > 2)
        {
            sec = generateWords(test);
            foreach (string s in sec)
            {
                string init = s.Substring(0, 1);
                string restOfbody = s.Substring(1, s.Length - 1);

                List<string> third = comb(restOfbody);
                foreach (string s1 in third)
                {
                    if (!first.Contains(init + s1)) first.Add(init + s1);
                }


            }
        }

        return first;
    }

    static string ShiftBack(string abc)
    {
        char[] arr = abc.ToCharArray();
        char temp = arr[0];
        string wrd = string.Empty;
        for (int i = 1; i < arr.Length; i++)
        {
            wrd += arr[i];
        }

        wrd += temp;
        return wrd;
    }

    static List<string> generateWords(string test)
    {
        List<string> final = new List<string>();
        if (test.Length == 1)
            final.Add(test);
        else
        {
            final.Add(test);
            string holdString = test;
            while (final.Count < test.Length)
            {
                holdString = ShiftBack(holdString);
                final.Add(holdString);
            }
        }

        return final;
    }

    static string swap(int currentPosition, int targetPosition, string temp)
    {
        char[] arr = temp.ToCharArray();
        char t = arr[currentPosition];
        arr[currentPosition] = arr[targetPosition];
        arr[targetPosition] = t;
        string word = string.Empty;
        for (int i = 0; i < arr.Length; i++)
        {
            word += arr[i];

        }

        return word;

    }
}

2

Ecco un esempio di alto livello che ho scritto che illustra la spiegazione del linguaggio umano che Peter ha dato:

    public List<string> FindPermutations(string input)
    {
        if (input.Length == 1)
            return new List<string> { input };
        var perms = new List<string>();
        foreach (var c in input)
        {
            var others = input.Remove(input.IndexOf(c), 1);
            perms.AddRange(FindPermutations(others).Select(perm => c + perm));
        }
        return perms;
    }

Questa soluzione è in realtà imperfetta dal fatto che se il set di stringhe contiene caratteri di ripetizione, fallirà. Ad esempio, sulla parola 'test', il comando Except rimuoverà entrambe le istanze di 't' anziché solo il primo e l'ultimo quando necessario.
Middas,

1
@Middas ben individuato, fortunatamente l'abbraccio ha trovato una soluzione per risolvere questo problema.
FBryant87,

1

Se le prestazioni e la memoria sono un problema, suggerisco questa implementazione molto efficiente. Secondo l'algoritmo di Heap in Wikipedia , dovrebbe essere il più veloce. Spero che si adatti alle tue necessità :-)!

Proprio come un confronto di questo con un'implementazione Linq per 10! (codice incluso):

  • Questo: 36288000 articoli in 235 millisecondi
  • Linq: 36288000 articoli in 50051 millisecondi

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    
    namespace WpfPermutations
    {
        /// <summary>
        /// EO: 2016-04-14
        /// Generator of all permutations of an array of anything.
        /// Base on Heap's Algorithm. See: https://en.wikipedia.org/wiki/Heap%27s_algorithm#cite_note-3
        /// </summary>
        public static class Permutations
        {
            /// <summary>
            /// Heap's algorithm to find all pmermutations. Non recursive, more efficient.
            /// </summary>
            /// <param name="items">Items to permute in each possible ways</param>
            /// <param name="funcExecuteAndTellIfShouldStop"></param>
            /// <returns>Return true if cancelled</returns> 
            public static bool ForAllPermutation<T>(T[] items, Func<T[], bool> funcExecuteAndTellIfShouldStop)
            {
                int countOfItem = items.Length;
    
                if (countOfItem <= 1)
                {
                    return funcExecuteAndTellIfShouldStop(items);
                }
    
                var indexes = new int[countOfItem];
                for (int i = 0; i < countOfItem; i++)
                {
                    indexes[i] = 0;
                }
    
                if (funcExecuteAndTellIfShouldStop(items))
                {
                    return true;
                }
    
                for (int i = 1; i < countOfItem;)
                {
                    if (indexes[i] < i)
                    { // On the web there is an implementation with a multiplication which should be less efficient.
                        if ((i & 1) == 1) // if (i % 2 == 1)  ... more efficient ??? At least the same.
                        {
                            Swap(ref items[i], ref items[indexes[i]]);
                        }
                        else
                        {
                            Swap(ref items[i], ref items[0]);
                        }
    
                        if (funcExecuteAndTellIfShouldStop(items))
                        {
                            return true;
                        }
    
                        indexes[i]++;
                        i = 1;
                    }
                    else
                    {
                        indexes[i++] = 0;
                    }
                }
    
                return false;
            }
    
            /// <summary>
            /// This function is to show a linq way but is far less efficient
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="length"></param>
            /// <returns></returns>
            static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
            {
                if (length == 1) return list.Select(t => new T[] { t });
    
                return GetPermutations(list, length - 1)
                    .SelectMany(t => list.Where(e => !t.Contains(e)),
                        (t1, t2) => t1.Concat(new T[] { t2 }));
            }
    
            /// <summary>
            /// Swap 2 elements of same type
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="a"></param>
            /// <param name="b"></param>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            static void Swap<T>(ref T a, ref T b)
            {
                T temp = a;
                a = b;
                b = temp;
            }
    
            /// <summary>
            /// Func to show how to call. It does a little test for an array of 4 items.
            /// </summary>
            public static void Test()
            {
                ForAllPermutation("123".ToCharArray(), (vals) =>
                {
                    Debug.Print(String.Join("", vals));
                    return false;
                });
    
                int[] values = new int[] { 0, 1, 2, 4 };
    
                Debug.Print("Non Linq");
                ForAllPermutation(values, (vals) =>
                {
                    Debug.Print(String.Join("", vals));
                    return false;
                });
    
                Debug.Print("Linq");
                foreach(var v in GetPermutations(values, values.Length))
                {
                    Debug.Print(String.Join("", v));
                }
    
                // Performance
                int count = 0;
    
                values = new int[10];
                for(int n = 0; n < values.Length; n++)
                {
                    values[n] = n;
                }
    
                Stopwatch stopWatch = new Stopwatch();
                stopWatch.Reset();
                stopWatch.Start();
    
                ForAllPermutation(values, (vals) =>
                {
                    foreach(var v in vals)
                    {
                        count++;
                    }
                    return false;
                });
    
                stopWatch.Stop();
                Debug.Print($"Non Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
    
                count = 0;
                stopWatch.Reset();
                stopWatch.Start();
    
                foreach (var vals in GetPermutations(values, values.Length))
                {
                    foreach (var v in vals)
                    {
                        count++;
                    }
                }
    
                stopWatch.Stop();
                Debug.Print($"Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
    
            }
        }
    }

1

Ecco la mia soluzione in JavaScript (NodeJS). L'idea principale è che prendiamo un elemento alla volta, "lo rimuoviamo" dalla stringa, variamo il resto dei caratteri e inseriamo l'elemento in primo piano.

function perms (string) {
  if (string.length == 0) {
    return [];
  }
  if (string.length == 1) {
    return [string];
  }
  var list = [];
  for(var i = 0; i < string.length; i++) {
    var invariant = string[i];
    var rest = string.substr(0, i) + string.substr(i + 1);
    var newPerms = perms(rest);
    for (var j = 0; j < newPerms.length; j++) {
      list.push(invariant + newPerms[j]);
    }
  }
  return list;
}

module.exports = perms;

Ed ecco i test:

require('should');
var permutations = require('../src/perms');

describe('permutations', function () {
  it('should permute ""', function () {
    permutations('').should.eql([]);
  })

  it('should permute "1"', function () {
    permutations('1').should.eql(['1']);
  })

  it('should permute "12"', function () {
    permutations('12').should.eql(['12', '21']);
  })

  it('should permute "123"', function () {
    var expected = ['123', '132', '321', '213', '231', '312'];
    var actual = permutations('123');
    expected.forEach(function (e) {
      actual.should.containEql(e);
    })
  })

  it('should permute "1234"', function () {
    // Wolfram Alpha FTW!
    var expected = ['1234', '1243', '1324', '1342', '1423', '1432', '2134', '2143', '2314', '2341', '2413', '2431', '3124', '3142', '3214', '3241', '3412', '3421', '4123', '4132'];
    var actual = permutations('1234');
    expected.forEach(function (e) {
      actual.should.containEql(e);
    })
  })
})

1

Ecco la soluzione più semplice che mi viene in mente:

let rec distribute e = function
  | [] -> [[e]]
  | x::xs' as xs -> (e::xs)::[for xs in distribute e xs' -> x::xs]

let permute xs = Seq.fold (fun ps x -> List.collect (distribute x) ps) [[]] xs

La distributefunzione accetta un nuovo elemento ee un nelenco di elementi e restituisce un elenco di n+1elenchi ciascuno dei quali è stato einserito in una posizione diversa. Ad esempio, inserendo 10in ognuna delle quattro posizioni possibili nell'elenco [1;2;3]:

> distribute 10 [1..3];;
val it : int list list =
  [[10; 1; 2; 3]; [1; 10; 2; 3]; [1; 2; 10; 3]; [1; 2; 3; 10]]

La permutefunzione si ripiega su ogni elemento a sua volta distribuendo sulle permutazioni accumulate finora, culminando in tutte le permutazioni. Ad esempio, le 6 permutazioni dell'elenco [1;2;3]:

> permute [1;2;3];;
val it : int list list =
  [[3; 2; 1]; [2; 3; 1]; [2; 1; 3]; [3; 1; 2]; [1; 3; 2]; [1; 2; 3]]

La modifica folddi scanin a per mantenere gli accumulatori intermedi fa luce su come le permutazioni vengono generate un elemento alla volta:

> Seq.scan (fun ps x -> List.collect (distribute x) ps) [[]] [1..3];;
val it : seq<int list list> =
  seq
    [[[]]; [[1]]; [[2; 1]; [1; 2]];
     [[3; 2; 1]; [2; 3; 1]; [2; 1; 3]; [3; 1; 2]; [1; 3; 2]; [1; 2; 3]]]

1

Elenca le permutazioni di una stringa. Evita la duplicazione quando i personaggi vengono ripetuti:

using System;
using System.Collections;

class Permutation{
  static IEnumerable Permutations(string word){
    if (word == null || word.Length <= 1) {
      yield return word;
      yield break;
    }

    char firstChar = word[0];
    foreach( string subPermute in Permutations (word.Substring (1)) ) {
      int indexOfFirstChar = subPermute.IndexOf (firstChar);
      if (indexOfFirstChar == -1) indexOfFirstChar = subPermute.Length;

      for( int index = 0; index <= indexOfFirstChar; index++ )
        yield return subPermute.Insert (index, new string (firstChar, 1));
    }
  }

  static void Main(){
    foreach( var permutation in Permutations ("aab") )
      Console.WriteLine (permutation);
  }
}

2
Con così tante soluzioni di lavoro già presenti, potresti voler descrivere ciò che distingue la tua soluzione da tutte le altre soluzioni qui.
nvoigt,

Evita la duplicazione quando i personaggi vengono ripetuti (da chindirala per un'altra risposta). Per "aab": aab aba baa
Val

1

Sulla base della soluzione di @ Peter, ecco una versione che dichiara un semplice Permutations()metodo di estensione in stile LINQ che funziona su qualsiasi IEnumerable<T>.

Utilizzo (sull'esempio di caratteri stringa):

foreach (var permutation in "abc".Permutations())
{
    Console.WriteLine(string.Join(", ", permutation));
}

Uscite:

a, b, c
a, c, b
b, a, c
b, c, a
c, b, a
c, a, b

O su qualsiasi altro tipo di raccolta:

foreach (var permutation in (new[] { "Apples", "Oranges", "Pears"}).Permutations())
{
    Console.WriteLine(string.Join(", ", permutation));
}

Uscite:

Apples, Oranges, Pears
Apples, Pears, Oranges
Oranges, Apples, Pears
Oranges, Pears, Apples
Pears, Oranges, Apples
Pears, Apples, Oranges
using System;
using System.Collections.Generic;
using System.Linq;

public static class PermutationExtension
{
    public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> source)
    {
        var sourceArray = source.ToArray();
        var results = new List<T[]>();
        Permute(sourceArray, 0, sourceArray.Length - 1, results);
        return results;
    }

    private static void Swap<T>(ref T a, ref T b)
    {
        T tmp = a;
        a = b;
        b = tmp;
    }

    private static void Permute<T>(T[] elements, int recursionDepth, int maxDepth, ICollection<T[]> results)
    {
        if (recursionDepth == maxDepth)
        {
            results.Add(elements.ToArray());
            return;
        }

        for (var i = recursionDepth; i <= maxDepth; i++)
        {
            Swap(ref elements[recursionDepth], ref elements[i]);
            Permute(elements, recursionDepth + 1, maxDepth, results);
            Swap(ref elements[recursionDepth], ref elements[i]);
        }
    }
}

0

Ecco la funzione che stampa ricorsivamente tutte le permutazioni.

public void Permutations(string input, StringBuilder sb)
    {
        if (sb.Length == input.Length)
        {
            Console.WriteLine(sb.ToString());
            return;
        }

        char[] inChar = input.ToCharArray();

        for (int i = 0; i < input.Length; i++)
        {
            if (!sb.ToString().Contains(inChar[i]))
            {
                sb.Append(inChar[i]);
                Permutations(input, sb);    
                RemoveChar(sb, inChar[i]);
            }
        }
    }

private bool RemoveChar(StringBuilder input, char toRemove)
    {
        int index = input.ToString().IndexOf(toRemove);
        if (index >= 0)
        {
            input.Remove(index, 1);
            return true;
        }
        return false;
    }

0
class Permutation
{
    public static List<string> Permutate(string seed, List<string> lstsList)
    {
        loopCounter = 0;
        // string s="\w{0,2}";
        var lstStrs = PermuateRecursive(seed);

        Trace.WriteLine("Loop counter :" + loopCounter);
        return lstStrs;
    }

    // Recursive function to find permutation
    private static List<string> PermuateRecursive(string seed)
    {
        List<string> lstStrs = new List<string>();

        if (seed.Length > 2)
        {
            for (int i = 0; i < seed.Length; i++)
            {
                str = Swap(seed, 0, i);

                PermuateRecursive(str.Substring(1, str.Length - 1)).ForEach(
                    s =>
                    {
                        lstStrs.Add(str[0] + s);
                        loopCounter++;
                    });
                ;
            }
        }
        else
        {
            lstStrs.Add(seed);
            lstStrs.Add(Swap(seed, 0, 1));
        }
        return lstStrs;
    }
    //Loop counter variable to count total number of loop execution in various functions
    private static int loopCounter = 0;

    //Non recursive  version of permuation function
    public static List<string> Permutate(string seed)
    {
        loopCounter = 0;
        List<string> strList = new List<string>();
        strList.Add(seed);
        for (int i = 0; i < seed.Length; i++)
        {
            int count = strList.Count;
            for (int j = i + 1; j < seed.Length; j++)
            {
                for (int k = 0; k < count; k++)
                {
                    strList.Add(Swap(strList[k], i, j));
                    loopCounter++;
                }
            }
        }
        Trace.WriteLine("Loop counter :" + loopCounter);
        return strList;
    }

    private static string Swap(string seed, int p, int p2)
    {
        Char[] chars = seed.ToCharArray();
        char temp = chars[p2];
        chars[p2] = chars[p];
        chars[p] = temp;
        return new string(chars);
    }
}

0

Ecco una risposta C # che è un po 'semplificata.

public static void StringPermutationsDemo()
{
    strBldr = new StringBuilder();

    string result = Permute("ABCD".ToCharArray(), 0);
    MessageBox.Show(result);
}     

static string Permute(char[] elementsList, int startIndex)
{
    if (startIndex == elementsList.Length)
    {
        foreach (char element in elementsList)
        {
            strBldr.Append(" " + element);
        }
        strBldr.AppendLine("");
    }
    else
    {
        for (int tempIndex = startIndex; tempIndex <= elementsList.Length - 1; tempIndex++)
        {
            Swap(ref elementsList[startIndex], ref elementsList[tempIndex]);

            Permute(elementsList, (startIndex + 1));

            Swap(ref elementsList[startIndex], ref elementsList[tempIndex]);
        }
    }

    return strBldr.ToString();
}

static void Swap(ref char Char1, ref char Char2)
{
    char tempElement = Char1;
    Char1 = Char2;
    Char2 = tempElement;
}

Produzione:

1 2 3
1 3 2

2 1 3
2 3 1

3 2 1
3 1 2

0

Questa è la mia soluzione che è facile per me capire

class ClassicPermutationProblem
{
    ClassicPermutationProblem() { }

    private static void PopulatePosition<T>(List<List<T>> finalList, List<T> list, List<T> temp, int position)
    {
         foreach (T element in list)
         {
             List<T> currentTemp = temp.ToList();
             if (!currentTemp.Contains(element))
                currentTemp.Add(element);
             else
                continue;

             if (position == list.Count)
                finalList.Add(currentTemp);
             else
                PopulatePosition(finalList, list, currentTemp, position + 1);
        }
    }

    public static List<List<int>> GetPermutations(List<int> list)
    {
        List<List<int>> results = new List<List<int>>();
        PopulatePosition(results, list, new List<int>(), 1);
        return results;
     }
}

static void Main(string[] args)
{
    List<List<int>> results = ClassicPermutationProblem.GetPermutations(new List<int>() { 1, 2, 3 });
}

0

Ecco un'altra implementazione dell'algo menzionato.

public class Program
{
    public static void Main(string[] args)
    {
        string str = "abcefgh";
        var astr = new Permutation().GenerateFor(str);
        Console.WriteLine(astr.Length);
        foreach(var a in astr)
        {
            Console.WriteLine(a);
        }
        //a.ForEach(Console.WriteLine);
    }
}

class Permutation
{
    public string[] GenerateFor(string s)
    {  

        if(s.Length == 1)
        {

            return new []{s}; 
        }

        else if(s.Length == 2)
        {

            return new []{s[1].ToString()+s[0].ToString(),s[0].ToString()+s[1].ToString()};

        }

        var comb = new List<string>();

        foreach(var c in s)
        {

            string cStr = c.ToString();

            var sToProcess = s.Replace(cStr,"");
            if (!string.IsNullOrEmpty(sToProcess) && sToProcess.Length>0)
            {
                var conCatStr = GenerateFor(sToProcess);



                foreach(var a in conCatStr)
                {
                    comb.Add(c.ToString()+a);
                }


            }
        }
        return comb.ToArray();

    }
}

new Permutation().GenerateFor("aba")uscitestring[4] { "ab", "baa", "baa", "ab" }
Atomosk

0
    //Generic C# Method
            private static List<T[]> GetPerms<T>(T[] input, int startIndex = 0)
            {
                var perms = new List<T[]>();

                var l = input.Length - 1;

                if (l == startIndex)
                    perms.Add(input);
                else
                {

                    for (int i = startIndex; i <= l; i++)
                    {
                        var copy = input.ToArray(); //make copy

                        var temp = copy[startIndex];

                        copy[startIndex] = copy[i];
                        copy[i] = temp;

                        perms.AddRange(GetPerms(copy, startIndex + 1));

                    }
                }

                return perms;
            }

            //usages
            char[] charArray = new char[] { 'A', 'B', 'C' };
            var charPerms = GetPerms(charArray);


            string[] stringArray = new string[] { "Orange", "Mango", "Apple" };
            var stringPerms = GetPerms(stringArray);


            int[] intArray = new int[] { 1, 2, 3 };
            var intPerms = GetPerms(intArray);

Sarebbe bello se riesci a elaborare un po 'su come funziona questo codice, invece di lasciarlo qui da solo.
iBug

-1
    /// <summary>
    /// Print All the Permutations.
    /// </summary>
    /// <param name="inputStr">input string</param>
    /// <param name="strLength">length of the string</param>
    /// <param name="outputStr">output string</param>
    private void PrintAllPermutations(string inputStr, int strLength,string outputStr, int NumberOfChars)
    {
        //Means you have completed a permutation.
        if (outputStr.Length == NumberOfChars)
        {
            Console.WriteLine(outputStr);                
            return;
        }

        //For loop is used to print permutations starting with every character. first print all the permutations starting with a,then b, etc.
        for(int i=0 ; i< strLength; i++)
        {
            // Recursive call : for a string abc = a + perm(bc). b+ perm(ac) etc.
            PrintAllPermutations(inputStr.Remove(i, 1), strLength - 1, outputStr + inputStr.Substring(i, 1), 4);
        }
    }        
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.