Список всех перестановок строки / целого числа

Распространенная задача при программировании собеседований (хотя и не из моего опыта проведения собеседований) - взять строку или целое число и перечислить все возможные перестановки.

Есть ли пример того, как это делается, и логика решения такой проблемы?

Я видел несколько фрагментов кода, но они не были хорошо прокомментированы / объяснены, и поэтому их трудно понять.


person GurdeepS    schedule 16.04.2009    source источник
comment
Вот вопрос к перестановкам с некоторыми хорошими поясняющими ответами, включая график, но не на C #.   -  person user unknown    schedule 09.05.2012


Ответы (25)


Во-первых: конечно, пахнет рекурсией!

Поскольку вы тоже хотели знать принцип, я постарался объяснить его на человеческом языке. Я думаю, что в большинстве случаев рекурсия очень проста. Вам нужно выполнить всего два шага:

  1. Первый шаг
  2. Все остальные шаги (все по той же логике)

На человеческом языке:

Вкратце:

  1. Перестановка 1 элемента - это один элемент.
  2. Перестановка набора элементов - это список каждого из элементов, объединенный с каждой перестановкой других элементов.

Пример:

Если в наборе всего один элемент - ›
верните его.
perm (a) -› a

Если в наборе есть два символа: для каждого элемента в нем: вернуть элемент с добавлением перестановок остальных элементов, например:

пермь (ab) - ›

а + пермь (б) - ›ab

б + пермь (а) - ›ба

Далее: для каждого символа в наборе: вернуть символ, объединенный перестановкой ›остальной части набора

Пермь (abc) - ›

a + perm (bc) - ›abc, acb

b + perm (ac) - ›bac, bca

c + perm (ab) - ›cab, cba

пермь (abc ... z) - ›

а + пермь (...), б + пермь (....)
....

Я нашел псевдокод на 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 #

Хорошо, и что-то более сложное (и поскольку он помечен тегом C #), от http://radio.weblogs.com/0111551/stories/2002/10/14/permutations.html: довольно длинный, но я все равно решил скопировать его, поэтому пост не зависит от оригинала .

Функция принимает строку символов и записывает все возможные перестановки этой точной строки, поэтому, например, если был предоставлен ABC, должно вылиться:

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

Код:

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);
    }
}
person Peter    schedule 16.04.2009
comment
Для большей ясности я бы назвал k recursionDepth и m maxDepth. - person Nerf Herder; 27.08.2014
comment
Второй своп (Swap(ref list[k], ref list[i]);) не является обязательным. - person dance2die; 31.05.2016
comment
Спасибо за это решение. Я создал эту скрипку (dotnetfiddle.net/oTzihw) из нее (с правильным именем вместо k и m). Насколько я понимаю алгоритм, требуется второй Swap (для возврата), поскольку вы редактируете исходный массив на месте. - person Andrew; 05.01.2017
comment
второстепенный момент: похоже, что метод Swap лучше реализовать с временной буферной переменной, а не с использованием XOR (dotnetperls .com / swap) - person Sergioet; 17.02.2017
comment
Это не работает, когда есть повторяющиеся символы. Как избежать дублирования при повторении символов в String. - person chindirala sampath kumar; 27.03.2017
comment
Используя кортежи C # 7, вы можете сделать обмен более элегантным: (a[x], a[y]) = (a[y], a[x]); - person Darren; 11.08.2017

Если LINQ разрешено использовать, это всего лишь две строки кода. См. Мой ответ здесь.

ИЗМЕНИТЬ

Вот моя универсальная функция, которая может возвращать все перестановки (не комбинации) из списка 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 }));
}

Пример:

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

Вывод - список целочисленных списков:

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

Поскольку эта функция использует LINQ, для нее требуется .net 3.5 или выше.

person Pengyang    schedule 17.05.2012
comment
комбинации и перестановки - разные вещи. это похоже, но ваш ответ, похоже, решает другую проблему, чем все перестановки набора элементов. - person Shawn Kovac; 04.03.2014
comment
@ShawnKovac, спасибо, что указали на это! Я обновил свой код от комбинации до перестановки. - person Pengyang; 12.03.2014
comment
@Pengyang Я посмотрел на ваш другой ответ и скажу, что это мне очень помогло, но у меня есть другая ситуация, о которой я не знаю, указали ли вы правильный способ ее решения. Я хотел найти все перестановки такого слова, как «HALLOWEEN», но обнаружил, что также хочу включить в результирующий набор как «L», так и «E». В своих итерациях я перебираю ваш метод, увеличивая длину с каждой итерацией (length ++), и ожидал, что, учитывая полную длину слова HALLOWEEN (9 символов), я получу результаты длиной 9 символов ... но это не так: Я получаю только 7 (1 L и 1 E опущены) - person MegaMark; 14.10.2015
comment
Я также хотел бы отметить, что я НЕ хочу ситуации, когда я вижу 9 знаков «Н», поскольку «Н» появляется в слове только один раз. - person MegaMark; 14.10.2015
comment
@MegaMark Эта функция требует, чтобы элементы были уникальными. Попробуйте это: const string s = "HALLOWEEN"; var result = GetPermutations(Enumerable.Range(0, s.Length), s.Length).Select(t => t.Select(i => s[i])); - person Pengyang; 15.10.2015
comment
Этот ответ расширил мои горизонты того, что возможно с LINQ. Спасибо! - person Aron; 05.12.2017
comment
Когда список содержит любые повторяющиеся элементы, это просто возвращает пустой IEnumerable. Решение @Najera работает с дубликатами, но вы получаете постоянное общее количество (perm 1 выбирает первый E, perm 2 выбирает второй E и т. Д.), Что означает дубликаты на выходе - person Thymine; 09.01.2019
comment
Хотя я сам являюсь поклонником LINQ, я боюсь, что у этого решения ужасная производительность. Это может быть вызвано ленивой оценкой или всеми накладными расходами итератора, я не знаю, но я сделал несколько временных мер, сравнивая это решение с Юрик реализовал, а его реализация была примерно в 40 раз быстрее. - person KnorxThieus; 13.04.2019
comment
PS: Код измерения, запускался несколько раз в csi: var watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 10; i++) GeneratePermutations(Enumerable.Range(1, 8).ToArray(), 0).ToList(); watch.Stop(); watch.ElapsedMilliseconds - person KnorxThieus; 13.04.2019
comment
@KnorxThieus Для этого есть веская причина. Реализация Юрика не создает новый массив для каждой перестановки, она возвращает один и тот же массив каждый раз после его изменения. Это означает, что он сломается, если вы попытаетесь использовать на нем что-то вроде .ToArray(). В версии Pengyang вместо этого вам нужно сделать .Select(v => v.ToArray()).ToArray(), но он работает так, как задумано. - person Pharap; 02.08.2019

Здесь я нашел решение. Он был написан на Java, но я преобразовал его на C #. Надеюсь, это вам поможет.

Введите описание изображения здесь

Вот код на 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;
}
person user3277651    schedule 18.02.2014
comment
С другого языка портировано? Определенно +1 за изображение, потому что это действительно добавляет ценности. Однако сам код, похоже, имеет определенный потенциал для улучшения. Некоторые второстепенные части не нужны, но, что наиболее важно, я чувствую это ощущение C ++, когда мы что-то отправляем и делаем с этим что-то, вместо того, чтобы указывать параметры и получать возвращаемое значение. Фактически, я использовал ваше изображение для реализации кода C # в стиле C # (стиль, конечно, был моим личным восприятием), и это очень помогло мне, поэтому, когда я опубликую его, я обязательно украду его у вас (и благодарю вам за это). - person Konrad Viltersten; 18.09.2016
comment
C # поддерживает подкачку, как Python, с момента появления кортежей. - person GNUSupporter 8964民主女神 地下教會; 27.10.2020

Рекурсия не требуется, здесь есть полезная информация об этом решении.

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

Я использовал этот алгоритм в течение многих лет, у него есть O (N) время и пространство сложность для вычисления каждой перестановки .

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;
    }
}
person Najera    schedule 12.09.2015
comment
Работает "из коробки"! - person revobtz; 12.10.2018
comment
возможно, я не понимаю обозначение O (n). разве N не относится к тому, сколько внутренних циклов необходимо для работы вашего алгоритма? мне кажется, что если у вас есть N номеров, кажется, что это O (N * N!) (потому что N! раз он должен сделать N свопов). Кроме того, он должен копировать множество массивов. Этот код хорош, но я бы не стал его использовать. - person eric frazer; 09.10.2019
comment
@ericfrazer Каждая перестановка использует только одну копию массива, и O(N-1) для последовательности и O(N) для свопов, то есть O(N). И я все еще использую это в производстве, но с рефакторингом, чтобы сгенерировать только одну перестановку, например: GetPermutation(i) где 0 <= i <= N!-1. Я буду счастлив использовать что-то с более высокой производительностью, чем это, так что не стесняйтесь обращаться к справочнику для чего-то лучшего, большинство альтернатив использует O(N!) в памяти, так что вы тоже можете это проверить. - person Najera; 28.06.2020

Немного измененная версия на C #, которая дает необходимые перестановки в массиве ЛЮБОГО типа.

    // 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;
    }
}
person Yuri Astrakhan    schedule 23.10.2012
comment
Одно небольшое предостережение в отношении этой реализации: она работает правильно только в том случае, если вы не пытаетесь сохранить значение перечисления. Если вы попытаетесь сделать что-то вроде Permutations(vals).ToArray(), вы получите N ссылок на один и тот же массив. Если вы хотите иметь возможность сохранять результаты, вам нужно вручную создать копию. Например. Permutations(values).Select(v => (T[])v.Clone()) - person Pharap; 02.08.2019

Прежде всего, наборы имеют перестановки, а не строки или целые числа, поэтому я просто предполагаю, что вы имеете в виду «набор символов в строке».

Обратите внимание, что в наборе размера n есть n! n-перестановки.

Следующий псевдокод (из Википедии), вызываемый с k = 1 ... n! даст все перестановки:

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

Вот эквивалентный код Python (для индексов массива с отсчетом от 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
person Can Berk Güder    schedule 16.04.2009
comment
Что это за язык? вопрос отмечен C #. я не знаю, что делает k := k / j;. - person Shawn Kovac; 04.03.2014

Мне понравился подход FBryant87, поскольку он прост. К сожалению, он, как и многие другие "решения", не предлагает всех вариантов или, например, целое число, если оно содержит одну и ту же цифру более одного раза. Возьмем, к примеру, 656123. Линия:

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

использование Except приведет к удалению всех вхождений, т.е. когда c = 6, две цифры удаляются, и у нас остается, например, 5123. Поскольку ни одно из опробованных мной решений не помогло решить эту проблему, я решил попробовать решить ее сам, используя код FBryant87 в качестве основы. Вот что я придумал:

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

Я просто удаляю первое найденное вхождение, используя .Remove и .IndexOf. По крайней мере, похоже, работает так, как предназначено для моего использования. Я уверен, что его можно было сделать умнее.

Однако следует отметить одну вещь: результирующий список может содержать дубликаты, поэтому убедитесь, что вы либо вернули метод, например. вместо этого HashSet или удалите дубликаты после возврата, используя любой метод, который вам нравится.

person hug    schedule 18.05.2015
comment
Работает как абсолютная красота, сначала я обнаружил, что обрабатывает повторяющиеся символы +1 - person Jack Casey; 09.07.2019

Вот чисто функциональная реализация F #:


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 }

Производительность можно значительно улучшить, изменив свопинг, чтобы воспользоваться преимуществами изменчивой природы массивов CLR, но эта реализация является потокобезопасной по отношению к исходному массиву, и это может быть желательно в некоторых контекстах. Кроме того, для массивов с более чем 16 элементами int необходимо заменить типами с большей / произвольной точностью, поскольку факториал 17 приводит к переполнению int32.

person em70    schedule 16.04.2009

Вот простое решение на С # с использованием рекурсии,

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)));
        }
    }
}
person raghavankl    schedule 20.10.2016

Вот простая для понимания функция перестановки как для строковых, так и для целых чисел в качестве входных данных. С помощью этого вы даже можете установить длину вывода (которая в нормальном случае равна длине ввода)

Строка

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

а для Integer просто измените метод вызывающего, и MakePermutations () останется нетронутым:

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

пример 1: GetAllPermutations ("abc", 3); "abc" "acb" "bac" "bca" "cab" "cba"

пример 2: GetAllPermutations ("abcd", 2); "ab" "ac" "ad" "ba" "bc" "bd" "ca" "cb" "cd" "da" "db" "dc"

пример 3: GetAllPermutations (486,2); 48 46 84 86 64 68

person Amir Chatrbahr    schedule 04.07.2016
comment
Мне нравится ваше решение, потому что это легко понять, спасибо вам за это! Тем не менее, я решил пойти с этим: stackoverflow.com/questions/756055/. Причина в том, что ToList, ToArray и RemoveAt имеют временную сложность O (N). Поэтому в основном вам нужно просмотреть все элементы коллекции (см. stackoverflow.com/a/15042066/1132522). То же самое для int, где вы в основном снова просматриваете все элементы, чтобы преобразовать их в int. Я согласен, что это не сильно повлияет на abc или 486. - person Andrew; 05.01.2017

Основываясь на решении @ Peter, вот версия, которая объявляет простой метод расширения Permutations() в стиле LINQ, который работает с любым IEnumerable<T>.

Использование (на примере строковых символов):

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

Выходы:

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

Или на любом другом типе коллекции:

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

Выходы:

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]);
        }
    }
}
person Lazlo    schedule 12.11.2019

Вот функция, которая распечатает все перестановки. Эта функция реализует логику. Объяснил Петр.

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

    }
person Amit Patel    schedule 19.12.2011

Ниже представлена ​​моя реализация перестановки. Не обращайте внимания на имена переменных, так как я делал это для развлечения :)

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;

    }
}
person priyank    schedule 22.09.2012

Вот пример высокого уровня, который я написал, который иллюстрирует объяснение, данное Питером на человеческом языке:

    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;
    }
person FBryant87    schedule 19.08.2014
comment
Это решение на самом деле ошибочно в том, что если набор строк содержит какие-либо повторяющиеся символы, оно не сработает. Например, для слова «тест» команда Except удалит оба экземпляра «t», а не только первый и последний, когда это необходимо. - person Middas; 27.06.2015
comment
@Middas хорошо заметен, к счастью, hug нашел решение этой проблемы. - person FBryant87; 07.07.2015

Это мое решение, которое мне легко понять

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 });
}
person Eduardo Teixeira    schedule 16.08.2015

Если производительность и память являются проблемой, я предлагаю эту очень эффективную реализацию. Согласно алгоритму кучи в Википедии, он должен быть самым быстрым. Надеюсь, он вам подойдет :-)!

Так же, как это сравнение с реализацией Linq для 10! (код включен):

  • Это: 36288000 элементов в 235 миллисекундах
  • Linq: 36288000 элементов в 50051 миллисекундах

    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");
    
            }
        }
    }
    
person Eric Ouellet    schedule 14.04.2016

Вот мое решение на JavaScript (NodeJS). Основная идея состоит в том, что мы берем по одному элементу за раз, «удаляем его» из строки, меняем остальные символы и вставляем элемент впереди.

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;

А вот и тесты:

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);
    })
  })
})
person Maria Ines Parnisari    schedule 13.01.2017

Вот самое простое решение, о котором я могу думать:

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

Функция distribute принимает новый элемент e и список n элементов и возвращает список из n+1 списков, каждый из которых e вставлен в другое место. Например, вставив 10 в каждое из четырех возможных мест в списке [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]]

Функция permute складывается по каждому элементу, по очереди распределяя по уже накопленным перестановкам, достигая высшей точки во всех перестановках. Например, 6 перестановок списка [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]]

Изменение fold на scan, чтобы сохранить промежуточные аккумуляторы, проливает некоторый свет на то, как перестановки генерируются для элемента за раз:

> 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]]]
person J D    schedule 22.02.2017

Перечисляет перестановки строки. Избегает дублирования при повторении символов:

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);
  }
}
person Val    schedule 24.04.2017
comment
Имея так много уже имеющихся рабочих решений, вы можете описать здесь, что отличает ваше решение от всех других. - person nvoigt; 24.04.2017
comment
Избегает дублирования при повторении символов (по chindirala для другого ответа). Для aab: aab aba baa - person Val; 24.04.2017

Вот функция, которая рекурсивно распечатает все перестановки.

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;
    }
person user2674028    schedule 12.08.2013

Вот ответ C #, который немного упрощен.

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

Вывод:

1 2 3
1 3 2

2 1 3
2 3 1

3 2 1
3 1 2
person Sai    schedule 18.04.2014

Вот еще одна реализация упомянутого алгоритма.

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

    }
}
person Deepak Rohilla    schedule 05.07.2016
comment
new Permutation().GenerateFor("aba") выходы string[4] { "ab", "baa", "baa", "ab" } - person Atomosk; 25.04.2017

База / пересмотр на Pengyang ответ < / а>

И вдохновлен перестановками в javascript

Версия C # FunctionalPermutations должна быть такой

static IEnumerable<IEnumerable<T>> FunctionalPermutations<T>(IEnumerable<T> elements, int length)
    {
        if (length < 2) return elements.Select(t => new T[] { t });
        /* Pengyang answser..
          return _recur_(list, length - 1).SelectMany(t => list.Where(e => !t.Contains(e)),(t1, t2) => t1.Concat(new T[] { t2 }));
        */
        return elements.SelectMany((element_i, i) => 
          FunctionalPermutations(elements.Take(i).Concat(elements.Skip(i + 1)), length - 1)
            .Select(sub_ei => new[] { element_i }.Concat(sub_ei)));
    }
person Tearf001    schedule 10.10.2020

Надеюсь, этого хватит:

using System;
                
public class Program
{
    public static void Main()
    {
        //Example using word cat
        permute("cat");
    
    }

static void permute(string word){
    for(int i=0; i < word.Length; i++){
        char start = word[0];
        for(int j=1; j < word.Length; j++){
            string left = word.Substring(1,j-1);
            string right = word.Substring(j);
            Console.WriteLine(start+right+left);
        }
        if(i+1 < word.Length){
            word = wordChange(word, i + 1);
        }
            
    }
}

static string wordChange(string word, int index){
    string newWord = "";
    for(int i=0; i<word.Length; i++){
        if(i== 0)
            newWord += word[index];
        else if(i== index)
            newWord += word[0];
        else
            newWord += word[i];
    }
    return newWord;
}

вывод:

cat
cta
act
atc
tca
tac
person DePacifier.ETH    schedule 22.01.2021

person    schedule
comment
Было бы здорово, если бы вы могли немного подробнее рассказать о том, как работает этот код, вместо того, чтобы оставлять его здесь в покое. - person iBug; 31.12.2017