One thing lead to another and eventually I wanted to know the "proper" way of writing the permutation generating function in C#. Below is my code for generating permutations. This will go into an internal library, so the calling context is really generic. Expected calling is in a single-threaded context
/// <summary>
/// Generate all possible permutations of all elements in the list taken r at a time
/// Permutations: Choose 'r' letters from 'n' possible letters to form a word (sequence is important).
/// Formula for counting: nPr = n! / (n - r)!
/// </summary>
/// <param name="list">The list whose permutations are needed</param>
/// <param name="startPos">The position of first element of interest in the list</param>
/// <param name="r">Number of items required in the permuted list</param>
/// <returns>An enumeration containing a permutation of the original list</returns>
public static IEnumerable<IList<T>> GetPermutations<T>(this IList<T> list, int r, int startPos = 0) {
for (int i = startPos; i < list.Count; ++i) {
if (r > 1) {
list.Swap(startPos, i);
foreach (var permute in GetPermutations(list, r - 1, startPos + 1))
yield return new List<T>().AddRange(list[startPos].AsEnumerable(), permute);
list.Swap(startPos, i);
} else
yield return new List<T>() { list[i] };
}
}
The Utils class has the extensions used in the GetPermutations function
public static class Utils {
public static bool Swap<T>(this IList<T> list, int i1, int i2) {
if (i1 == i2)
return false;
var tmp = list[i1];
list[i1] = list[i2];
list[i2] = tmp;
return true;
}
public static IEnumerable<T> AsEnumerable<T>(this T item) {
yield return item;
}
public static List<T> AddRange<T>(this List<T> list, params IEnumerable<T>[] collections) {
foreach (var collection in collections)
list.AddRange(collection);
return list;
}
}
The code will be invoked as below
var items = new [] { 'a', 'b', 'c', 'd'};
foreach (var permutation in items.GetPermutations(3))
Console.WriteLine(string.Join(", ", permutation));
Couple of things on which I would like to get feedback are:
yield return
in a recursive function.- Generation of new Lists to be
yield returned
, given that we will have a really large number of permutations with small increase in the input set
Yield
toAsEnumerable
\$\endgroup\$