Daftar semua permutasi string / integer


159

Tugas umum dalam pemrograman wawancara (bukan dari pengalaman wawancara saya) adalah untuk mengambil string atau bilangan bulat dan daftar setiap permutasi yang mungkin.

Apakah ada contoh bagaimana hal ini dilakukan dan logika di balik penyelesaian masalah seperti itu?

Saya telah melihat beberapa cuplikan kode tetapi tidak dikomentari / dijelaskan dengan baik sehingga sulit untuk diikuti.


1
Berikut adalah pertanyaan untuk Permutasi dengan beberapa jawaban yang menjelaskan dengan baik, termasuk grafik , tetapi tidak dalam C #.
pengguna tidak diketahui

Jawaban:


152

Pertama-tama: baunya seperti rekursi tentu saja!

Karena Anda juga ingin tahu prinsipnya, saya melakukan yang terbaik untuk menjelaskannya dalam bahasa manusia. Saya pikir rekursi sangat mudah. Anda hanya perlu memahami dua langkah:

  1. Langkah pertama
  2. Semua langkah lainnya (semua dengan logika yang sama)

Dalam bahasa manusia :

Singkatnya:
1. Permutasi 1 elemen adalah satu elemen.
2. Permutasi seperangkat elemen adalah daftar masing-masing elemen, disatukan dengan setiap permutasi elemen lainnya.

Contoh:

Jika set hanya memiliki satu elemen ->
kembalikan.
perm (a) -> a

Jika set memiliki dua karakter: untuk setiap elemen di dalamnya: kembalikan elemen, dengan permutasi sisa elemen yang ditambahkan, seperti:

perm (ab) ->

a + perm (b) -> ab

b + perm (a) -> ba

Selanjutnya: untuk setiap karakter di set: mengembalikan karakter, disatukan dengan perumusan> sisa set

perm (abc) ->

a + perm (bc) -> abc , acb

b + perm (ac) -> bac , bca

c + perm (ab) -> cab , cba

perm (abc ... z) ->

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

Saya menemukan kodesemu di 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, dan sesuatu yang lebih rumit (dan karena itu ditandai #), dari http://radio.weblogs.com/0111551/stories/2002/10/14/permutations.html : Agak panjang, tapi saya memutuskan untuk menyalinnya ngomong-ngomong, jadi postingan tidak tergantung pada aslinya.

Fungsi ini mengambil string karakter, dan menuliskan setiap permutasi yang mungkin dari string yang tepat itu, jadi misalnya, jika "ABC" telah disediakan, harus keluar:

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

Kode:

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
Untuk sedikit lebih jelas, saya akan memanggil k "recursionDepth" dan memanggil m "maxDepth".
Nerf Herder

3
Swap ke-2 ( Swap(ref list[k], ref list[i]);) tidak perlu.
dance2die

1
Terima kasih atas solusi ini. Saya membuat biola ini ( dotnetfiddle.net/oTzihw ) darinya (dengan penamaan yang tepat, bukan k dan m). Sejauh yang saya mengerti algo, Swap kedua diperlukan (untuk mundur) karena Anda mengedit array asli di tempat.
Andrew

3
poin minor: Sepertinya metode Swap lebih baik diterapkan dengan variabel buffer sementara dan tidak menggunakan XOR ( dotnetperls.com/swap )
Sergioet

7
Menggunakan tuple C # 7 Anda dapat membuat swap lebih elegan:(a[x], a[y]) = (a[y], a[x]);
Darren

81

Itu hanya dua baris kode jika LINQ diizinkan untuk digunakan. Silakan lihat jawaban saya di sini .

EDIT

Inilah fungsi generik saya yang dapat mengembalikan semua permutasi (bukan kombinasi) dari daftar 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 }));
}

Contoh:

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

Output - daftar bilangan bulat:

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

Karena fungsi ini menggunakan LINQ sehingga membutuhkan .net 3.5 atau lebih tinggi.


3
kombinasi dan permutasi adalah hal yang berbeda. ini mirip, tetapi jawaban Anda tampaknya menjawab masalah yang berbeda dari semua permutasi dari serangkaian elemen.
Shawn Kovac

@ShawnKovac, terima kasih sudah menunjukkan ini! Saya telah memperbarui kode saya dari kombinasi ke permutasi.
Pengyang

1
@Pengyang saya melihat jawaban Anda yang lain dan saya akan mengatakan bahwa ini banyak membantu saya tetapi saya memiliki situasi lain yang saya tidak tahu jika Anda menunjukkan cara yang benar untuk menyelesaikannya. Saya ingin menemukan semua permutasi kata seperti 'HALLOWEEN' tetapi menemukan bahwa saya juga ingin memasukkan 'L's dan' E's di set hasil. Dalam iterasi saya, saya mengulangi metode Anda memberikan peningkatan panjang dengan setiap iterasi (panjang ++) dan akan berharap bahwa mengingat panjang penuh kata HALLOWEEN (9 chars) saya akan mendapatkan hasil 9 chars panjang ... tapi ini tidak terjadi: Saya hanya mendapatkan 7 (1 L dan 1 E dihilangkan)
MegaMark

Saya juga ingin menunjukkan bahwa saya TIDAK ingin situasi di mana saya melihat 9 'H sebagai' H 'hanya muncul sekali dalam kata.
MegaMark

4
@MegaMark Fungsi ini membutuhkan elemen untuk menjadi unik. Coba ini:const string s = "HALLOWEEN"; var result = GetPermutations(Enumerable.Range(0, s.Length), s.Length).Select(t => t.Select(i => s[i]));
Pengyang

36

Di sini saya telah menemukan solusinya. Itu ditulis dalam Java, tetapi saya telah mengubahnya menjadi C #. Saya harap ini akan membantu Anda.

Masukkan deskripsi gambar di sini

Berikut kode dalam 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;
}

Apakah porting dari bahasa lain? Pasti memberi +1 untuk gambar, karena benar-benar menambah nilai. Namun, kode itu sendiri tampaknya memiliki potensi peningkatan tertentu. Beberapa bagian kecil tidak diperlukan tetapi yang paling penting, saya mendapatkan perasaan C ++ ini ketika kami mengirim sesuatu dan melakukan hal-hal untuk itu alih-alih memberikan in-parameter dan mengambil nilai yang dikembalikan. Bahkan, saya menggunakan gambar Anda untuk menerapkan kode C # -styled C # (gaya menjadi persepsi pribadi saya, tentu saja), dan itu sangat membantu saya, jadi ketika saya akan mempostingnya saya pasti akan mencurinya dari Anda (dan kredit kasih untuk itu).
Konrad Viltersten

21

Rekursi tidak perlu, berikut adalah informasi yang baik tentang solusi ini.

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

Saya telah menggunakan algoritma ini selama bertahun-tahun, ia memiliki O (N) waktu dan kompleksitas ruang untuk menghitung setiap permutasi .

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

Bekerja di luar kotak!
revobtz

1
mungkin saya tidak mengerti notasi O (n). bukankah N merujuk pada berapa "loop dalam" yang diperlukan untuk membuat algoritma Anda bekerja? Sepertinya saya seperti jika Anda memiliki nomor N, sepertinya itu O (N * N!) (karena N! kali itu harus melakukan N swap). Plus, itu harus melakukan satu ton penyalinan array. Kode ini "rapi", tetapi saya tidak akan menggunakannya.
eric frazer

@ ericfrazer Setiap permutasi hanya menggunakan satu salinan array, dan O(N-1)untuk urutan dan O(N)untuk swap, yaitu O(N). Dan saya masih menggunakan ini dalam produksi tetapi dengan refactor untuk menghasilkan hanya satu permutasi seperti: di GetPermutation(i)mana 0 <= i <= N!-1. Saya akan dengan senang hati menggunakan sesuatu dengan kinerja yang lebih baik dari ini, jadi bebas untuk memanggil referensi untuk sesuatu yang lebih baik, sebagian besar penggunaan alternatif O(N!)dalam memori sehingga Anda mungkin memeriksanya juga.
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]);
  }
}

Anda dapat menulis fungsi swap Anda untuk bertukar karakter.
Ini disebut permute (string, 0);


5
Ini terlihat seperti C, bukan C #.
Jon Schneider

9

Pertama-tama, set memiliki permutasi, bukan string atau integer, jadi saya hanya akan menganggap Anda berarti "set karakter dalam sebuah string."

Perhatikan bahwa satu set ukuran n memiliki n! n-permutasi.

Kodesemu berikut (dari Wikipedia), dipanggil dengan k = 1 ... n! akan memberikan semua permutasi:

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

Berikut kode Python yang setara (untuk indeks array berbasis 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
bahasa apa ini? pertanyaannya ditandai C #. saya tidak tahu apa k := k / j;.
Shawn Kovac

8

Versi sedikit dimodifikasi dalam C # yang menghasilkan permutasi yang diperlukan dalam array jenis APA PUN.

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

Satu peringatan kecil dengan implementasi ini: itu hanya berfungsi dengan baik jika Anda tidak berusaha untuk menyimpan nilai enumerasi. Jika Anda mencoba melakukan sesuatu seperti Permutations(vals).ToArray()maka Anda berakhir dengan referensi N ke array yang sama. Jika Anda ingin dapat menyimpan hasil, Anda harus membuat salinan secara manual. MisalnyaPermutations(values).Select(v => (T[])v.Clone())
Pharap

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
Solusi gila. Terima kasih!
Cristian E.

7

Saya menyukai pendekatan FBryant87 karena sederhana. Sayangnya, ia tidak seperti banyak "solusi" lainnya yang tidak menawarkan semua permutasi atau misalnya integer jika mengandung digit yang sama lebih dari sekali. Ambil 656123 sebagai contoh. Garis:

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

menggunakan Kecuali akan menyebabkan semua kejadian dihapus, yaitu ketika c = 6, dua digit dihapus dan kita dibiarkan dengan misal 5123. Karena tidak ada solusi yang saya coba selesaikan ini, saya memutuskan untuk mencoba dan menyelesaikannya sendiri oleh FBryant87 's kode sebagai basis. Inilah yang saya pikirkan:

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

Saya hanya cukup menghapus kejadian pertama yang ditemukan menggunakan .Hapus dan .IndexOf. Tampaknya berfungsi sebagaimana dimaksudkan untuk penggunaan saya setidaknya. Saya yakin itu bisa dibuat lebih pintar.

Satu hal yang perlu diperhatikan: Daftar yang dihasilkan dapat berisi duplikat, jadi pastikan Anda membuat metode pengembalian misalnya HashSet sebagai gantinya atau menghapus duplikat setelah pengembalian menggunakan metode apa pun yang Anda suka.


Berfungsi seperti keindahan mutlak, pertama-tama saya menemukan bahwa menangani karakter duplikat +1
Jack Casey


5

Berikut ini adalah implementasi F # yang murni fungsional:


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 }

Kinerja dapat sangat ditingkatkan dengan mengubah swap untuk mengambil keuntungan dari sifat array CLR yang bisa berubah, tetapi implementasi ini aman untuk hal terkait dengan larik sumber dan yang mungkin diinginkan dalam beberapa konteks. Juga, untuk array dengan lebih dari 16 elemen int harus diganti dengan tipe dengan presisi yang lebih besar / arbitrer karena faktorial 17 menghasilkan aliran int32.


5

Berikut ini adalah solusi sederhana dalam c # menggunakan rekursi,

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

Terima kasih atas solusi yang sangat sederhana dan singkat! :)
Kristaps Vilerts

4

Berikut ini adalah fungsi permutasi yang mudah dipahami untuk string dan integer sebagai input. Dengan ini Anda bahkan dapat mengatur panjang output Anda (yang dalam kasus normal itu sama dengan panjang input)

Tali

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

dan untuk Integer cukup ubah metode pemanggil dan MakePermutations () tetap tidak tersentuh:

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

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

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

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


Saya suka solusi Anda karena ini mudah dimengerti, terima kasih untuk itu! Namun saya memilih untuk menggunakannya: stackoverflow.com/questions/756055/ā€¦ . Alasannya adalah bahwa ToList, ToArray dan RemoveAt semuanya memiliki kompleksitas waktu O (N). Jadi pada dasarnya Anda harus memeriksa semua elemen koleksi (lihat stackoverflow.com/a/15042066/1132522 ). Sama untuk int di mana Anda pada dasarnya memeriksa semua elemen lagi di akhir untuk mengubahnya menjadi int. Saya setuju bahwa ini tidak memiliki banyak dampak untuk "abc" atau 486.
Andrew

2

Ini adalah fungsi yang akan mencetak semua permutasi. Fungsi ini mengimplementasikan logika Dijelaskan oleh 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 bawah ini adalah implementasi permutasi saya. Jangan pedulikan nama variabel, karena saya melakukannya untuk bersenang-senang :)

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

Inilah contoh tingkat tinggi yang saya tulis yang menggambarkan penjelasan bahasa manusia yang diberikan Peter:

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

Solusi ini sebenarnya cacat karena jika rangkaian string berisi karakter yang berulang, itu akan gagal. Misalnya, pada kata 'test', perintah Kecuali akan menghapus kedua contoh 't' bukan hanya yang pertama dan terakhir saat diperlukan.
Middas

1
@Middas terlihat dengan baik, untungnya pelukan telah datang dengan solusi untuk mengatasi ini.
FBryant87

1

Jika kinerja dan memori adalah masalah, saya sarankan implementasi yang sangat efisien ini. Menurut algoritma Heap di Wikipedia , itu harus yang tercepat. Semoga sesuai dengan kebutuhan Anda :-)!

Sama seperti perbandingan ini dengan implementasi Linq untuk 10! (termasuk kode):

  • Ini: 36288000 item dalam 235 milidetik
  • Linq: 36288000 item dalam 50051 milidetik

    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

Inilah solusi saya dalam JavaScript (NodeJS). Ide utamanya adalah kita mengambil satu elemen pada satu waktu, "menghapusnya" dari string, memvariasikan sisa karakter, dan memasukkan elemen di depan.

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;

Dan inilah tesnya:

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

Berikut adalah solusi paling sederhana yang dapat saya pikirkan:

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

The distributefungsi mengambil elemen baru edan ndaftar -element dan mengembalikan daftar n+1daftar yang masing-masing telah edimasukkan di tempat yang berbeda. Misalnya, memasukkan 10di masing-masing dari empat tempat yang mungkin dalam daftar [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]]

The permuteFungsi lipatan lebih setiap elemen pada gilirannya mendistribusikan selama permutasi akumulasi sejauh ini, yang berpuncak pada semua permutasi. Sebagai contoh, 6 permutasi daftar [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]]

Mengubah foldke scanuntuk menjaga akumulator menengah menyoroti bagaimana permutasi dihasilkan suatu elemen pada suatu waktu:

> 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

Daftar permutasi string. Hindari duplikasi ketika karakter diulang:

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
Dengan begitu banyak solusi yang sudah ada, Anda mungkin ingin menggambarkan apa yang membuat solusi Anda menonjol dari semua solusi lain di sini.
nvoigt

Hindari duplikasi ketika karakter diulang (oleh chindirala untuk jawaban lain). Untuk "aab": aab aba baa
Val

1

Membangun di atas solusi @ Peter, inilah versi yang menyatakan Permutations()metode ekstensi gaya LINQ sederhana yang bekerja pada semuaIEnumerable<T> .

Penggunaan (pada contoh karakter string):

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

Output:

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

Atau pada jenis koleksi lainnya:

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

Output:

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

Berikut adalah fungsi yang akan mencetak semua permutasi secara rekursif.

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

Berikut adalah jawaban C # yang sedikit disederhanakan.

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

Keluaran:

1 2 3
1 3 2

2 1 3
2 3 1

3 2 1
3 1 2

0

Ini adalah solusi saya yang mudah bagi saya untuk mengerti

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

Berikut ini satu lagi implementasi dari algo yang disebutkan.

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")keluaranstring[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);

Alangkah baiknya jika Anda bisa menguraikan sedikit tentang cara kode ini bekerja, daripada membiarkannya di sini sendirian.
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);
        }
    }        
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.