Generating Permutations using LINQ

后端 未结 8 1964
我寻月下人不归
我寻月下人不归 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条回答
  •  -上瘾入骨i
    2020-12-01 05:24

    Note: the Comparer 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
    {
        /// 
        /// Generates permutations.
        /// 
        /// Type of items to permute.
        /// Array of items. Will not be modified.
        /// Optional comparer to use.
        /// If a  is supplied, 
        /// permutations will be ordered according to the 
        /// 
        /// 
        /// Permutations of input items.
        public static IEnumerable> Permute(T[] items, IComparer 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> Permute(T[] items)
        {
            return Permute(items, null);
        }
    
        public static IEnumerable> Permute(IEnumerable items, IComparer comparer)
        {
            List list = new List(items);
            return Permute(list.ToArray(), comparer);
        }
    
        public static IEnumerable> Permute(IEnumerable items)
        {
            return Permute(items, null);
        }
    
        #endregion Overloads
    
        #region Utility
    
        public static IEnumerable ApplyTransform(
            T[] items, 
            IntPair[] transform)
        {
            for (int i = 0; i < transform.Length; i++)
            {
                yield return items[transform[i].Second];
            }
        }
    
        public static void Swap(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();
        }
    }
    

提交回复
热议问题