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!
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...
String instead of your List<byte[]> to leave you with something to do. ;- - Brad Christie 2012-04-04 20:50
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
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();
}
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:
{1,2,3}{1,2,4}, {1,2,5}{1,3,4}{1,3,5}{1,4,5}{2,3,4}{2,3,5}{2,4,5}{3,4,5}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();
}
2^nto be exact), you won't get too fast if you really want all of them - amit 2012-04-04 20:13