Generating Permutations using LINQ

后端 未结 8 1965
我寻月下人不归
我寻月下人不归 2020-12-01 04:59

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

相关标签:
8条回答
  • 2020-12-01 05:18
        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);
            }
        }
    
    0 讨论(0)
  • 2020-12-01 05:20

    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.

    • Permutations, Combinations, and Variations using C# Generics
    • By Adrian Akison | 23 May 2008
    • Discusses the six major types of combinatorial collections, with examples and formulas for counting. Expands with a C# Generics-based set of classes for enumerating each meta-collection.
    • Inserted from http://www.codeproject.com/KB/recipes/Combinatorics.aspx
    0 讨论(0)
  • 2020-12-01 05:20

    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)));
        }
    
    0 讨论(0)
  • 2020-12-01 05:24

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-01 05:25
    1. create another array of length 2^n where n is the number of products
    2. count in binary from 0 to 2^n and fill in the array with each count. for example if n=3 the array will look like this :

    000 001 010 011 100 101 110 111

    1. loop through the binary array and find the ones in each number then add the product with the same index:
     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

    0 讨论(0)
  • 2020-12-01 05:33

    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();
      }
    }
    
    0 讨论(0)
提交回复
热议问题