3

I'm trying to get all the predefined-length permutations of a list, in ascending order only.

For example, take the set:  "ABCDE"
I'd like the returning result to be:
ABC, ABD, ABE, ACD, ACE, ADE, BCD, BCE, BDE, CDE

In other words, a "B" can never appear before an "A" (ascending order), but I'd like every variation within this requirements.

I'd prefer not to use LINQ, and I'm trying to figure out the fastest way to implement this (speed is a factor in this app).

So far I have a list of lists of chars:

List<List<char>> Combinations;

where the inner "List" would be a combination like "ABC" (each letter being a char), and the outer list would be a list of all combinations.

The length of each resulting set (3 in the above example) needs to be dynamic, so I'm thinking I'll need some kind of recursion... I just can't figure out how to implement it.

Any help would be greatly appreciated!

EDIT

So far, here's what I have (I feel like I'm getting close... I just can't get it to actually build the final list (the union isn't working - am I using it incorrectly?):

    private List<List<char>> AscendingPermute(List<char> InMovements, int Combinations)
    {
        List<List<char>> Ret = new List<List<char>>();

        for(int i = 0; i <= InMovements.Count - Combinations; i++)
        {
            if(Combinations <= 1){
                Ret.Add(new List<char>() {InMovements[i] });
                return Ret;
            }
            else
            {
                Ret.Union(AscendingPermute(InMovements.GetRange(1, InMovements.Count - 1), Combinations - 1));
            }
        }

        return Ret;
    }

Am I on the right track? What am I missing?

Thanks!

user1270384
  • 691
  • 7
  • 23
  • 53
Harry
  • 813
  • 3
  • 9
  • 25
  • 1
    There are exponential number of possibilities (`2^n` to be exact), you won't get too fast if you really want all of them. – amit Apr 04 '12 at 20:13
  • 3
    What do you have so far? – 48klocs Apr 04 '12 at 20:14
  • 1
    I think there are nCk possibilites (n is the length of the first list, k is the length of each list in the result), that's not 2^n. – zmbq Apr 04 '12 at 20:14
  • @zmbq: Correct, I misread the question - thaught the OP wanted *all solutions*, without the k limitations. Though it is still exponential [in k]. – amit Apr 04 '12 at 20:18
  • UPDATE: I added some code I'm working on to the original post. – Harry Apr 04 '12 at 20:46

5 Answers5

5

So you want all possible k-elements out of a set of n elements, and you want to each k-element list in ascending order?

Take a look here: Algorithm to return all combinations of k elements from n

Community
  • 1
  • 1
zmbq
  • 35,452
  • 13
  • 80
  • 153
  • I saw that in my research, and it looks like http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n/1898744#1898744 would do what I'm looking for - it's just the comment about larger sets using a lot of memory that worried me. As such, I'm looking for more of a custom (raw and fast hopefully) solution. – Harry Apr 04 '12 at 20:20
  • 1
    you should actually try it out on your code with your data and see if it uses too much or not. The way enumerables work they can usually be streamed, which means as little as possible is in memory at any one point in time. As long as you don't .ToList the results or something like that. – Servy Apr 04 '12 at 20:22
3

I think this is what you're looking for, though I'm not positive about speed:

public static IEnumerable<string> GetPermutations(string letters, int max = 3, int curr = 0)
{
  if (curr < max - 1)
  {
    for (int a = 0; a < letters.Length; a++)
    {
      string firstHalf = letters.Substring(a,1);
      string subset = letters.Substring(a+1);
      foreach (string secondHalf in GetPermutations(subset, max, curr + 1))
      {
        //Console.Write("1st: {0}, 2nd: {1}; set: {2}", firstHalf, secondHalf, subset);
        yield return firstHalf + secondHalf;
      }
    }
  }
  else
    yield return String.Empty;
}

Example call:

foreach (var result in GetPermutations('ABCDE', 3)){
  Console.WriteLine(result);
}

Results in:

ABC
ABD
ABE
ACD
ACE
ADE
BCD
BCE
BDE
CDE
Press any key to continue...
Brad Christie
  • 96,086
  • 15
  • 143
  • 191
2

No need for recursion.

List<string> sortedResult = Perm("ABCDE",3);

static int BitCount(int n)
{
    int test = n,count = 0;

    while (test != 0)
    {
        if ((test & 1) == 1) count++;
        test >>= 1;
    }
    return count;
}


static List<string> Perm(string input,int M)
{
    var chars = input.ToCharArray();
    int N = chars.Length;
    List<List<char>> result = new List<List<char>>();

    for (int i = 0; i < Math.Pow(2, N); i++)
    {
        if (BitCount(i) == M)
        {
            List<char> line = new List<char>();
            for (int j = 0; j < N; j++)
            {
                if (((i >> j) & 1) == 1)
                {
                    line.Add(chars[j]);
                }
            }
            result.Add(line);
        }
    }

    return result.Select(l => String.Join("", l)).OrderBy(s => s).ToList();
}
L.B
  • 106,644
  • 18
  • 163
  • 208
0

You are looking for a recursive function that will calculate: first letter in the given alphabet (sorted in ascending order) concatenated with the ascending permutations with one less letter of the remainder of the alphabet, plus the ascending permutations of the remainder with the same letter count.

To clarify, for your example it is

asc_perm("ABCDE",3):="A"+asc_perm("BCDE",2) | asc_perm("BCDE",3)

To code it iteratively, you can have n indexes into your alphabet with the constraint that n>m => idx_{n} > idx_{m} and 0 < n,m <= count(alphabet), and enumerate all possible indexes. It is sort of a counter with some extra conditions. In order to count using these indexes, you start with 1, 2, 3, 4, ...n. Start with incrementing the last counter until it reaches the alphabet length. When it does, find the previous index, increment it by 1, and set every index following it to 1+idx_prev so long as the indexes don't get larger than your count. If it does, you repeat the process with the previous index, until you run out of valid positions.

A simple run down of your example would be:

  • Initial condition: {1,2,3}
  • Run the last index for all valid positions: {1,2,4}, {1,2,5}
  • Increment previous index (2) to next and reset the rest: {1,3,4}
  • Run the last index for all valid positions: {1,3,5}
  • Increment previous index (2) to next and reset the rest: {1,4,5}
  • Run the last index for all valid positions: no possible moves
  • Increment previous index (2) to next and reset the rest: no possible moves
  • Increment previous index (1) to next and reset the rest: {2,3,4}
  • Run the last index for all valid positions: {2,3,5}
  • Increment previous index (2) to next and reset the rest: {2,4,5}
  • Run the last index for all valid positions: no possible moves
  • Increment previous index (2) to next and reset the rest: no possible moves
  • Increment previous index (1) to next and reset the rest: {3,4,5}
  • Run the last index for all valid positions: no possible moves
  • Increment previous index (2) to next and reset the rest: no possible moves
  • Increment previous index (1) to next and reset the rest: no possible moves
  • Terminate
vhallac
  • 11,956
  • 2
  • 22
  • 34
  • Or, to simplify, it's just the *combinations* rather than the *permutations* in which the combinations are sorted (internally). – Servy Apr 04 '12 at 20:33
  • @Servy Yup. But naming it properly doesn't always allow you to code it up. :) – vhallac Apr 04 '12 at 20:44
  • True, but the link to the coded solution that does exactly that is rather useful, and it comes up in searches much easier if you use the proper term. – Servy Apr 04 '12 at 20:48
0

Here's a recursive method that does what you want:

    static IEnumerable<List<byte>> AscPerm(List<byte> inBytes, int combinations)
    {
        if (combinations == 1)
        {
            foreach (var b in inBytes)
            {
                yield return new List<byte> { b };
            }
        }
        else
        {
            for (int i = 0; i <= inBytes.Count - combinations; i++)
            {
                // Recurse down, passing last items of inBytes.
                var subPerms = AscPerm(inBytes.Skip(i +1).ToList(), combinations - 1);
                foreach (var subPerm in subPerms)
                {
                    List<byte> retBytes = new List<byte>{ inBytes[i] };
                    yield return retBytes.Concat(subPerm).ToList();
                }
            }
        }
    }

    static void Main(string[] args)
    {
        var retList = AscPerm(new List<byte> { 1, 2, 3, 4, 5, 6, 7 }, 3);
        foreach (var ret in retList)
        {
            foreach (var r in ret)
            {
                Console.Write(r);
            }
            Console.WriteLine();
        }
        Console.ReadLine();
    }
markmuetz
  • 8,235
  • 2
  • 30
  • 31