Ordered Permutation of List

后端 未结 1 1893
北恋
北恋 2021-01-25 20:48

Given an ordered list of integers, L = { 1, 2, 3, 4 } and a permutation size of k,

I need to generate all \'ordered\' permutations of length k having the first sequence

相关标签:
1条回答
  • 2021-01-25 21:21

    Assuming that by "in order" you mean matching the order in the initial list:

    public static IEnumerable<T> Yield<T>( T value )
    {
        yield return value;
    }
    
    public static IEnumerable<IEnumerable<T>> GetOrderedPermutations<T>( IEnumerable<T> source, int k )
    {
        if( k == 0 ) return new[] { Enumerable.Empty<T>() };
    
        int length = source.Count();
    
        if( k == length ) return new[] { source };
    
        if( k > length ) return Enumerable.Empty<IEnumerable<T>>();
    
        return GetOrderedHelper<T>( source, k, length );
    }
    
    private static IEnumerable<IEnumerable<T>> GetOrderedHelper<T>( IEnumerable<T> source, int k, int length )
    {
        if( k == 0 )
        {
            yield return Enumerable.Empty<T>();
            yield break;
        }
        int i = 0;
        foreach( var item in source )
        {
            if( i + k > length ) yield break;
            var permutations = GetOrderedHelper<T>( source.Skip( i + 1 ), k - 1, length - i );
            i++;
    
            foreach( var subPerm in permutations )
            {
                yield return Yield( item ).Concat( subPerm );
            }
        }
    }
    

    This can still be made more efficient (by removing the recursion). But this is the most straight-forward algorithm I could come up with. In your case, since you always want the first element to appear, you can run the algorithm by chopping off the first element and just put it back on later:

    var permutations = GetOrderedPermutations( source.Skip( 1 ), k - 1 )
        .Select( p => Yield( source.First() ).Concat( p ) );
    

    The idea behind this algorithm is fairly simple: All permutations are found by picking the first item in the permutation and just prepending it to all permutations of length k - 1 made out of the remainder of the list.

    If you want to remove the recursion, there's a way of looking at it iteratively:

    If you want a permutation of length k, initialize k pointers pointing to the first k elements of the source. These pointers point to the elements of the current permutation. To get the next permutation, increment the last pointer. If the last pointer goes past the end of the source, increment the previous pointer and set the last pointer to one past it. In code:

    public static IEnumerable<IEnumerable<T>> GetOrderedPermutations<T>( IList<T> source, int k )
    {
        if( k == 0 ) yield return Enumerable.Empty<T>();
        if( k == source.Count ) yield return source;
        if( k > source.Count ) yield break;
        var pointers = Enumerable.Range( 0, k ).ToArray();
    
        // The first element of our permutation can only be in the first
        // Count - k + 1 elements.  If it moves past here, we can't have
        // anymore permutations because there aren't enough items in the list.
        while( pointers[0] <= source.Count - k )
        {
            yield return pointers.Select( p => source[p] );
            // Increment the last pointer
            pointers[k - 1]++;
    
            // The i variable will keep track of which pointer
            // we need to increment.  Start it at the second to
            // last (since this is the one that we're going to
            // increment if the last pointer is past the end).
            int i = k - 2;
            while( pointers[k - 1] >= source.Count && i >= 0 )
            {
                // Okay, the last pointer was past the end, increment
                pointers[i]++;
    
                // Reset all the pointers after pointer i
                for( int j = i + 1; j < k; ++j )
                {
                    pointers[j] = pointers[j - 1] + 1;
                }
    
                // If the last pointer is still past the end, we'll
                // catch it on the next go-around of this loop.
                // Decrement i so we move the previous pointer next time.
    
                --i;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题