Generating Permutations using LINQ

后端 未结 8 1978
我寻月下人不归
我寻月下人不归 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:35

    Here's a simple permutation extension method for C# 7 (value tuples and inner methods). It's derived from @AndrasVaas's answer, but uses only a single level of laziness (preventing bugs due to mutating items over time), loses the IComparer feature (I didn't need it), and is a fair bit shorter.

    public static class PermutationExtensions
    {
        /// 
        /// Generates permutations.
        /// 
        /// Type of items to permute.
        /// Array of items. Will not be modified.
        /// Permutations of input items.
        public static IEnumerable Permute(this T[] items)
        {
            T[] ApplyTransform(T[] values, (int First, int Second)[] tx)
            {
                var permutation = new T[values.Length];
                for (var i = 0; i < tx.Length; i++)
                    permutation[i] = values[tx[i].Second];
                return permutation;
            }
    
            void Swap(ref U x, ref U y)
            {
                var tmp = x;
                x = y;
                y = tmp;
            }
    
            var length = items.Length;
    
            // Build identity transform
            var transform = new(int First, int Second)[length];
            for (var i = 0; i < length; i++)
                transform[i] = (i, i);
    
            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-increasing) order
                var decreasingpart = length - 2;
                while (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
                var greater = length - 1;
                while (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);
            }
        }
    }
    

提交回复
热议问题