I have a set of products that must be scheduled. There are P products each indexed from 1 to P. Each product can be scheduled into a time period 0 to T. I need to construct
public static IList<IList<T>> Permutation<T>(ImmutableList<ImmutableList<T>> dimensions)
{
IList<IList<T>> result = new List<IList<T>>();
Step(ImmutableList.Create<T>(), dimensions, result);
return result;
}
private static void Step<T>(ImmutableList<T> previous,
ImmutableList<ImmutableList<T>> rest,
IList<IList<T>> result)
{
if (rest.IsEmpty)
{
result.Add(previous);
return;
}
var first = rest[0];
rest = rest.RemoveAt(0);
foreach (var label in first)
{
Step(previous.Add(label), rest, result);
}
}
I used this library for the combinations and found it works well. The sample program is a little confusing, but the article explains what is needed to use the code.
Today I stumbled upon this and figured I could share my implementation.
For all integers between N and M you have to make an array first:
IEnumerable<int> Range(int n, int m) {
for(var i = n; i < m; ++i) {
yield return i;
}
}
and run it through Permutations(Range(1, 10))
:
enum PermutationsOption {
None,
SkipEmpty,
SkipNotDistinct
}
private IEnumerable<IEnumerable<T>> Permutations<T>(IEnumerable<T> elements, PermutationsOption option = PermutationsOption.None, IEqualityComparer<T> equalityComparer = default(IEqualityComparer<T>)) {
var elementsList = new List<IEnumerable<T>>();
var elementsIndex = 0;
var elementsCount = elements.Count();
var elementsLength = Math.Pow(elementsCount + 1, elementsCount);
if (option.HasFlag(PermutationsOption.SkipEmpty)) {
elementsIndex = 1;
}
if (elements.Count() > 0) {
do {
var elementStack = new Stack<T>();
for (var i = 0; i < elementsCount; ++i) {
var ind = (int)(elementsIndex / Math.Pow(elementsCount + 1, i) % (elementsCount + 1));
if (ind == 0) {
continue;
}
elementStack.Push(elements.ElementAt(ind - 1));
}
var elementsCopy = elementStack.ToArray() as IEnumerable<T>;
if (option.HasFlag(PermutationsOption.SkipNotDistinct)) {
elementsCopy = elementsCopy.Distinct();
elementsCopy = elementsCopy.ToArray();
if (elementsList.Any(p => CompareItemEquality(p, elementsCopy, equalityComparer))) {
continue;
}
}
elementsList.Add(elementsCopy);
} while (++elementsIndex < elementsLength);
}
return elementsList.ToArray();
}
private bool CompareItemEquality<T>(IEnumerable<T> elements1, IEnumerable<T> elements2, IEqualityComparer<T> equalityComparer = default(IEqualityComparer<T>)) {
if (equalityComparer == null) {
equalityComparer = EqualityComparer<T>.Default;
}
return (elements2.Count() == elements2.Count()) && (elements2.All(p => elements1.Contains(p, equalityComparer)));
}
Note: the Comparer<T> is entirely optional. If you provide one, the permutations will be returned in lexical order. If you don't, but the original items are ordered, it will still enumerate in lexical order. Ian Griffiths played with this 6 years ago, using a simpler algo (that does not do lexical ordering, as far as I remember): http://www.interact-sw.co.uk/iangblog/2004/09/16/permuterate.
Keep in mind that this code is a few years old and targets .NET 2.0, so no extension methods and the like (but should be trivial to modify).
It uses the algorithm that Knuth calls "Algorithm L". It is non-recursive, fast, and is used in the C++ Standard Template Library.
static partial class Permutation
{
/// <summary>
/// Generates permutations.
/// </summary>
/// <typeparam name="T">Type of items to permute.</typeparam>
/// <param name="items">Array of items. Will not be modified.</param>
/// <param name="comparer">Optional comparer to use.
/// If a <paramref name="comparer"/> is supplied,
/// permutations will be ordered according to the
/// <paramref name="comparer"/>
/// </param>
/// <returns>Permutations of input items.</returns>
public static IEnumerable<IEnumerable<T>> Permute<T>(T[] items, IComparer<T> comparer)
{
int length = items.Length;
IntPair[] transform = new IntPair[length];
if (comparer == null)
{
//No comparer. Start with an identity transform.
for (int i = 0; i < length; i++)
{
transform[i] = new IntPair(i, i);
};
}
else
{
//Figure out where we are in the sequence of all permutations
int[] initialorder = new int[length];
for (int i = 0; i < length; i++)
{
initialorder[i] = i;
}
Array.Sort(initialorder, delegate(int x, int y)
{
return comparer.Compare(items[x], items[y]);
});
for (int i = 0; i < length; i++)
{
transform[i] = new IntPair(initialorder[i], i);
}
//Handle duplicates
for (int i = 1; i < length; i++)
{
if (comparer.Compare(
items[transform[i - 1].Second],
items[transform[i].Second]) == 0)
{
transform[i].First = transform[i - 1].First;
}
}
}
yield return ApplyTransform(items, transform);
while (true)
{
//Ref: E. W. Dijkstra, A Discipline of Programming, Prentice-Hall, 1997
//Find the largest partition from the back that is in decreasing (non-icreasing) order
int decreasingpart = length - 2;
for (;decreasingpart >= 0 &&
transform[decreasingpart].First >= transform[decreasingpart + 1].First;
--decreasingpart) ;
//The whole sequence is in decreasing order, finished
if (decreasingpart < 0) yield break;
//Find the smallest element in the decreasing partition that is
//greater than (or equal to) the item in front of the decreasing partition
int greater = length - 1;
for (;greater > decreasingpart &&
transform[decreasingpart].First >= transform[greater].First;
greater--) ;
//Swap the two
Swap(ref transform[decreasingpart], ref transform[greater]);
//Reverse the decreasing partition
Array.Reverse(transform, decreasingpart + 1, length - decreasingpart - 1);
yield return ApplyTransform(items, transform);
}
}
#region Overloads
public static IEnumerable<IEnumerable<T>> Permute<T>(T[] items)
{
return Permute(items, null);
}
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> items, IComparer<T> comparer)
{
List<T> list = new List<T>(items);
return Permute(list.ToArray(), comparer);
}
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> items)
{
return Permute(items, null);
}
#endregion Overloads
#region Utility
public static IEnumerable<T> ApplyTransform<T>(
T[] items,
IntPair[] transform)
{
for (int i = 0; i < transform.Length; i++)
{
yield return items[transform[i].Second];
}
}
public static void Swap<T>(ref T x, ref T y)
{
T tmp = x;
x = y;
y = tmp;
}
public struct IntPair
{
public IntPair(int first, int second)
{
this.First = first;
this.Second = second;
}
public int First;
public int Second;
}
#endregion
}
class Program
{
static void Main()
{
int pans = 0;
int[] digits = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (var p in Permutation.Permute(digits))
{
pans++;
if (pans == 720) break;
}
sw.Stop();
Console.WriteLine("{0}pcs, {1}ms", pans, sw.ElapsedMilliseconds);
Console.ReadKey();
}
}
000 001 010 011 100 101 110 111
for each binaryNumber in ar{ for i = 0 to n-1{ if binaryNumber(i) = 1 permunation.add(products(i)) } permunations.add(permutation) }
example: if binaryNumber= 001 then permunation1 = product1 if binaryNumber= 101 then permunation1 = product3,product1
The output from the answer of Mr Lippert can be seen as all the possible distributions of elements among 0 and 2 in 4 slots.
For instance
0 3 1
reads as "no 0, three 1 and one 2"
This is nowhere near as elegant as the answer of Mr Lippert, but at least not less efficient
public static void Main()
{
var distributions = Distributions(4, 3);
PrintSequences(distributions);
}
/// <summary>
/// Entry point for the other recursive overload
/// </summary>
/// <param name="length">Number of elements in the output</param>
/// <param name="range">Number of distinct values elements can take</param>
/// <returns></returns>
static List<int[]> Distributions(int length, int range)
{
var distribution = new int[range];
var distributions = new List<int[]>();
Distributions(0, length, distribution, 0, distributions);
distributions.Reverse();
return distributions;
}
/// <summary>
/// Recursive methode. Not to be called directly, only from other overload
/// </summary>
/// <param name="index">Value of the (possibly) last added element</param>
/// <param name="length">Number of elements in the output</param>
/// <param name="distribution">Distribution among element distinct values</param>
/// <param name="sum">Exit condition of the recursion. Incremented if element added from parent call</param>
/// <param name="distributions">All possible distributions</param>
static void Distributions(int index,
int length,
int[] distribution,
int sum,
List<int[]> distributions)
{
//Uncomment for exactness check
//System.Diagnostics.Debug.Assert(distribution.Sum() == sum);
if (sum == length)
{
distributions.Add(distribution.Reverse().ToArray());
for (; index < distribution.Length; index++)
{
sum -= distribution[index];
distribution[index] = 0;
}
return;
}
if (index < distribution.Length)
{
Distributions(index + 1, length, distribution, sum, distributions);
distribution[index]++;
Distributions(index, length, distribution, ++sum, distributions);
}
}
static void PrintSequences(List<int[]> distributions)
{
for (int i = 0; i < distributions.Count; i++)
{
for (int j = distributions[i].Length - 1; j >= 0; j--)
for (int k = 0; k < distributions[i][j]; k++)
Console.Write("{0:D1} ", distributions[i].Length - 1 - j);
Console.WriteLine();
}
}