1

I am current working on a project where I need to generate all possible permutations from a given set of characters. I am currently using this code:

public static IEnumerable<string> AllPermutations(this IEnumerable<char> s)
{
    return s.SelectMany(x =>
    {
        var index = Array.IndexOf(s.ToArray(), x);
        return s.Where((y, i) => i != index).AllPermutations().Select(y => new string(new[] { x }.Concat(y).ToArray())).Union(new[] { new string(new[] { x }) });
    }).Distinct();
}

From this answer.

The problem I have is that it won't generate permuations that use the same letter more than once.

For example if I used abcde as the input I need it to generate combinations like aaaaa and dcc etc.

I'm not experienced enough with LINQ to understand where the code is stopping duplicate letters. Any help is greatly appreciated.

11
  • Is there a reason to do this in LINQ? Commented Mar 9, 2012 at 13:54
  • I didn't write this, so was just looking for code that did the job really. Commented Mar 9, 2012 at 13:54
  • 1
    'aaaaa' is not a permutation of 'abcde'. If your project requires permutations don't include 'aaaaa', if you include 'aaaaa' don't call it a permutation (or a combination for that matter). You'll just confuse everyone reading your question, including yourself. Commented Mar 9, 2012 at 13:55
  • Perhaps what you need is variation with repetitions. Commented Mar 9, 2012 at 13:57
  • 1
    I also need any shorter versions, from 1 character to the string length, using all the input letters in any order. Commented Mar 9, 2012 at 14:03

3 Answers 3

3

This might work, but I'm sure it could be done more efficiently (taking the counting prompt from PeskyGnat):

    static IEnumerable<string> GetVariations(string s)
    {
        int[] indexes = new int[s.Length];
        StringBuilder sb = new StringBuilder();

        while (IncrementIndexes(indexes, s.Length))
        {
            sb.Clear();
            for (int i = 0; i < indexes.Length; i++)
            {
                if (indexes[i] != 0)
                {
                    sb.Append(s[indexes[i]-1]);
                }
            }
            yield return sb.ToString();
        }
    }

    static bool IncrementIndexes(int[] indexes, int limit)
    {
        for (int i = 0; i < indexes.Length; i++)
        {
            indexes[i]++;
            if (indexes[i] > limit)
            {
                indexes[i] = 1;
            }
            else
            {
                return true;
            }
        }
        return false;
    }

Edit: Changed to use yield return as per Rawlings suggestion. Much better memory usage if you don't need to keep all the results and you can start using the results before they've all been generated.

Sign up to request clarification or add additional context in comments.

9 Comments

That is absouletly brilliant! I can't thank you enough. +1 and accept, thanks!
Whether or not this could be done more efficiently, I'm sure it could be done a lot less efficiently. E.g. my answer :D
@Rawling: Actually one thing mine doesn't do that your does is deal with duplicates. For example, if the string is "cca", mine will spit out "caa" twice, once for each "c" which it sees as different. Yours doesn't do that.
@Matt Oh, mine doesn't intentionally deal with duplicates. Yours is in fact the O(1)-space solution I was thinking of, if you just switch to using yield return instead of a List. Edit: in fact the duplicates thing is trivial, just check the length of the input string, then strip it of duplicate characters before using it as your input...
Yes, the yield would make this handle larger values without any memory overhead, and adhere more closely to the original interface of returning an Enumerable
|
3

I'm amazed this works. It basically goes "make a list of strings from the characters. Then to each string taken from the list, add each character again, and add the resulting strings to the list. Repeat until you've got the right length."

public static IEnumerable<string> BuildStrings(this IEnumerable<char> alphabet)
{
    var strings = alphabet.Select(c => c.ToString());
    for (int i = 1; i < alphabet.Count(); i++)
    {
        strings = strings.Union(strings.SelectMany(s => alphabet.Select(c => s + c.ToString())));
    }
    return strings;
}

1 Comment

Aha, it only works because I'm doing Union rather than Concat, otherwise I'd have many, many duplicates...
0

A funny one using only recursive lambdas via a fixpoint operator (thx @Rawling for the SelectMany)

// Fix point operator   
public static Func<T, TResult> Fix<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> f)
    {
        return t => f(Fix<T, TResult>(f))(t);
    }

And then

var chars = new[] {'a','b','c','d','e'}.Select(c=>c.ToString()) ;

var result = Fix<int,IEnumerable<string>>(
    f => 
      x => 
       x == 1
         ? chars
         : chars.Union(f(x - 1).SelectMany(s => chars.Select(c => s + c))))(chars.Count());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.