Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Non-recursive Permutations and Combinations

4.87/5 (11 votes)
22 Dec 2010CPOL4 min read 49.1K   365  
Enumerating permutations and combinations without recursion.

Introduction

Here I'll present my implementation of non-recursive permutations and combinations of a generic list of objects. I don't actually use this code in production, I wrote it as a personal exercise. I only hope that the techniques used will be of some use to someone.

If the provided list contains nulls or duplicates, they will be included, not ignored; if you don't want nulls and duplicates, don't provide them; otherwise, I'll assume you provided them because you want them.

Background

A while back, someone posted recursive implementations of permutations and combinations. I commented that recursion wasn't a good way to do it. At that time, I wrote these implementations, mainly to be sure that I wasn't talking out the wrong end. Eventually, someone else called me on it and asked that I put my cards on the table, so here is my code.

Theory

I won't go into the theory of permutations and combinations; you can look them up on Wikipedia. What I will say, is that as I thought about this in relation to .NET and the IList<T> interface in particular, it occurred to me that performing permutations and combinations can be reduced to performing the permutations and combinations of the indices of the items, rather than the items themselves. (The IList interface specifies 32-bit signed integers as indices.) That makes the task a little easier -- the indices are always a contiguous sequence of integers from 0 to n-1 (where n is the number of items in the list). Therefore, the underlying code need not be generic.

UniqueInt

Because each index value may appear in the output list at most once, I needed a class that would track which values were already in use and not allow a second index to hold a repeated value. The UniqueInt class accomplishes this; no two instances of the class that share the same HashSet will hold the same value. If an attempt to set the value of an instance would result in a duplicate value, the instance will try the next higher value; this continues until an unused value is found.

(Usage of this technique is somewhat overkill for combinations, but is important for permutations.)

C#
public sealed class UniqueInt : System.IDisposable
{
    private System.Collections.Generic.HashSet<int> taken ;

    private readonly int current ;

    public UniqueInt
    (
        int   Value
    ,
        System.Collections.Generic.HashSet<int> Taken
    )
    {
        if ( Taken == null )
        {
            throw ( new System.ArgumentNullException
            (
                "Taken"
            ,
                "Taken must not be null"
            ) ) ;
        }

        this.taken = Taken ;

        lock ( this.taken )
        {
            while ( this.taken.Contains ( Value ) )
            {
                Value++ ;
            }

            this.taken.Add ( this.current = Value ) ;
        }

        return ;
    }

    public void
    Dispose
    (
    )
    {
        if ( this.taken != null )
        {
            this.taken.Remove ( this.current ) ;

            this.taken = null ;
        }

        return ;
    }

    public int
    Value
    {
        get
        {
            if ( this.taken == null )
            {
                throw ( new System.ObjectDisposedException
                    ( "" , "This instance has been disposed" ) ) ;
            }

            return ( this.current ) ;
        }
    }

    public override string
    ToString
    (
    )
    {
        return ( this.Value.ToString() ) ;
    }

    public static implicit operator
    int
    (
        UniqueInt Op
    )
    {
        return ( Op.Value ) ;
    }
}

Notice that the Dispose method will remove the value from the HashSet so that it can be used by another instance.

UniqueIntFactory

The UniqueIntFactory class merely encapsulates the process of instantiating a HashSet and then providing it to the UniqueInts that it instantiates. All UniqueInt instances created by the same UniqueIntFactory will share a HashSet and therefore will be unique within that group.

C#
public sealed class UniqueIntFactory
{
    private readonly System.Collections.Generic.HashSet<int> taken ;

    public UniqueIntFactory
    (
    )
    {
        this.taken = new System.Collections.Generic.HashSet<int>() ;

        return ;
    }

    public UniqueInt
    NewValue
    (
    )
    {
        return ( new UniqueInt ( 0 , this.taken ) ) ;
    }

    public UniqueInt
    NewValue
    (
        int Value
    )
    {
        return ( new UniqueInt ( Value , this.taken ) ) ;
    }
}

Stack

The Stack class is a simple class derived from Stack<T>, but with an indexer that accesses the base class' private array. (Go ahead and vote 1 if you like, you know you wanna.)

The important parts are the static constructor, which uses Reflection to access the FieldInfo for the private array.

Note that we can't cache a reference to the array itself because a new array will be instantiated if the Stack is extended.

C#
protected static readonly System.Reflection.FieldInfo arrayinfo ;

static Stack
(
)
{
    arrayinfo = typeof(System.Collections.Generic.Stack<T>).GetField
    (
        "_array"
    ,
         System.Reflection.BindingFlags.Instance
         |
         System.Reflection.BindingFlags.NonPublic
    ) ;

    return ;
}

And the FIFO enumerator that will be used for our permutations and combinations (the built-in enumerator is LIFO):

C#
public virtual System.Collections.Generic.IEnumerable<T>
FIFO
{
    get
    {
        lock ( this.pickle )
        {
            T[] temp = (T[]) arrayinfo.GetValue ( this ) ;

            for ( int i = 0 ; i < this.Count ; i++ )
            {
                yield return ( temp [ i ] ) ;
            }

            yield break ;
        }
    }
}

UniqueIntStack

Combine those two classes together and you get a UniqueIntStack -- a Stack that contains unique 32-bit signed integers.

C#
public class UniqueIntStack
{
    private readonly PIEBALD.Types.UniqueIntFactory               factory ;
    private readonly PIEBALD.Types.Stack<PIEBALD.Types.UniqueInt> stack   ;

    public UniqueIntStack
    (
    )
    {
        this.factory = new PIEBALD.Types.UniqueIntFactory() ;
        this.stack   = new PIEBALD.Types.Stack<PIEBALD.Types.UniqueInt>() ;

        return ;
    }

    public virtual int
    Count
    {
        get
        {
            lock ( this.factory )
            {
                return ( this.stack.Count ) ;
            }
        }
    }

    public virtual int
    Push
    (
        int Value
    )
    {
        lock ( this.factory )
        {
            PIEBALD.Types.UniqueInt result = this.factory.NewValue ( Value ) ;

            this.stack.Push ( result ) ;
            
            return ( result ) ;
        }
    }

    public virtual int
    Pop
    (
    )
    {
        lock ( this.factory )
        {
            using
            (
                PIEBALD.Types.UniqueInt result
             =
                this.stack.Pop()
            )
            {
                return ( result ) ;
            }
        }
    }

    public virtual int
    Peek
    (
    )
    {
        lock ( this.factory )
        {
            return ( this.stack.Peek() ) ;
        }
    }

    public virtual int
    this
    [
        int Index
    ]
    {
        get
        {
            lock ( this.factory )
            {
                try
                {
                    return ( this.stack [ Index ] ) ;
                }
                catch ( System.IndexOutOfRangeException err )
                {
                    throw ( new System.ArgumentOutOfRangeException
                    (
                        "That index is out of range"
                    ,
                        err
                    ) ) ;
                }
            }
        }
    }

    public virtual System.Collections.Generic.IEnumerable<int>
    Values
    {
        get
        {
            lock ( this.factory )
            {
                foreach
                (
                    PIEBALD.Types.UniqueInt val
                in
                    this.stack.FIFO
                )
                {
                    yield return ( val ) ;
                }

                yield break ;
            }
        }
    }
}

Note the Values enumerator, we'll be using it in a moment.

Combinations and Permutations

These two Extension Methods are nearly identical. Other than their names, there is only one statement difference between them, so I'll present Combinations.

Writing this article has given me a good reason to revisit this code. The code wasn't as elegant as I'd like, and I wanted to find a more elegant implementation. What I eventually came up with is a state machine; here are the states:

C#
private enum StackState
{
    Push
,
    Pop
,
    Increment
,
    Return
,
    Break
}

And here is the machine; it is an enumerator that yields enumerators of Things:

C#
PIEBALD.Types.UniqueIntStack stack = new PIEBALD.Types.UniqueIntStack() ;

StackState state = StackState.Push ;

stack.Push ( 0 ) ;

while ( state != StackState.Break )
{
    switch ( state )
    {
        case StackState.Push :
        {
            if ( stack.Count == Take )
            {
                state = StackState.Return ;
            }
            else if ( stack.Push ( stack.Peek() ) == Things.Count )
            {
                state = StackState.Pop ;
            }

            break ;
        }

        case StackState.Pop :
        {
            stack.Pop() ;

            if ( stack.Count == 0 )
            {
                state = StackState.Break ;
            }
            else
            {
                state = StackState.Increment ;
            }

            break ;
        }

        case StackState.Increment :
        {
            if ( stack.Push ( stack.Pop() + 1 ) == Things.Count )
            {
                state = StackState.Pop ;
            }
            else
            {
                state = StackState.Push ;
            }

            break ;
        }

        case StackState.Return :
        {
            yield return ( Things.ApplyIndices ( stack.Values ) ) ;

            state = StackState.Increment ;

            break ;
        }
    }
}

yield break ;

ApplyIndices

ApplyIndices is an Extension Method that returns an enumerator that contains the members of the provided Things based on the provided Indices. The code is a little more convoluted than I'd like because yield isn't allowed within a try/catch.

C#
public static System.Collections.Generic.IEnumerable<T>
ApplyIndices<T>
(
    this System.Collections.Generic.IList<T>    Things
,
    System.Collections.Generic.IEnumerable<int> Indices
)
{
    int count = 0 ;

    foreach
    (
        int index
    in
        Indices
    )
    {
        T thing ;

        try
        {
            thing = Things [ index ] ;
        }
        catch ( System.IndexOutOfRangeException err )
        {
            throw ( new System.IndexOutOfRangeException
            (
                System.String.Format
                (
                    "Index {0} equals {1}, which is outside the range 0..{2}"
                ,
                    count
                ,
                    index
                ,
                    Things.Count - 1
                )
            ,
                err
            ) ) ;
        }

        yield return ( thing ) ;

        count++ ;
    }

    yield break ;
}

PermCombDemo

PermCombDemo is a simple console application that demonstrates the various permutations and combinations of any command-line parameters you provide.

Build.bat

I've included a simple BAT file to build and execute PermCombDemo. Ensure that CSC is in your path, perhaps by using the Visual Studio Command Prompt if you like. If you want to build the code in the IDE of your choice, you're on your own.

Points of Interest

The only surprise was that yield isn't allowed within a try/catch.

History

  • 2010-12-21: First submitted.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)