给定池中所有元素的n分区

时间:2014-02-19 21:57:03

标签: c# algorithm linq list partitioning

我正在尝试编写一个C#算法,该算法从包含x个元素的池中提供可能的n分区列表。

为了更清楚,我们假设我有一个包含6个元素的池:1,2,3,4,5,6(x = 6) 我应该给3个数字的所有分区,即:

1,2,3 and 4,5,6
1,3,4 and 2,5,6
1,2,5 and 3,4,6
1,2,6 and 3,4,5
etc.

我定位的功能如下:

List<List<List<int>>> GetAllPartitions(int x, int n)

编辑:

我正在寻找的不是组合......对于它,我使用它(取自本网站):

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
    return k == 0
       ? new[] {new T[0]}
       : elements.SelectMany((e, i) =>
             elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}

1 个答案:

答案 0 :(得分:1)

一般来说,我不做仅链接答案(通常SO也喜欢你展示尝试解决方案),但是有一个完整的代码解决方案,用于任何类型的对象(不仅仅是int)已经用c#编写了:

http://blogs.msdn.com/b/updownroundnround/archive/2009/12/18/generating-all-partitions-of-a-set-c-implementation.aspx

注意:这将生成集合的每个长度的所有分区,因此您需要在结果列表上进行简单的选择,以仅针对所需的“n”长度分区进行过滤,这可以简单地:

var result = from allResults select result where result.length = n || result.length = x-n

对于担心链接死亡的人,这里是链接代码:

/// <summary>
/// A enumeration of all possible partitions of a set of items.
/// </summary>
/// <typeparam name="T">The type of the items in the set being partitioned.</typeparam>
public sealed class AllPartitionsEnumerable<T> : IEnumerable<IEnumerable<IEnumerable<T>>>
{
    /// <summary></summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
    private IEnumerable<T> items;

    /// <summary>
    /// Creates and initializes an instance of the <see cref="AllPartitionsEnumerable{T}"/> type.
    /// </summary>
    /// <param name="items">The set of items to be partitioned.</param>
    public AllPartitionsEnumerable(IEnumerable<T> items)
        : base ()
    {
        this.items = items;
    }

    /// <summary>
    /// Gets an enumerator to iterate over the partitions in this enumeration.
    /// </summary>
    /// <returns>An instance of <see cref="IEnumerator{T}"/>.</returns>
    public IEnumerator<IEnumerable<IEnumerable<T>>> GetEnumerator()
    {
        return new AllPartitionsEnumerator<T>(this.items);
    }

    /// <summary>
    /// Gets an enumerator to iterate over the partitions in this enumeration.
    /// </summary>
    /// <returns>An instance of <see cref="IEnumerator{T}"/>.</returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
 /// <summary>
/// An enumerator to iterate over the items in an instance of <see cref="AllPartitionsEnumerable{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the items in the set being partitioned.</typeparam>
public sealed class AllPartitionsEnumerator<T> : IEnumerator<IEnumerable<IEnumerable<T>>>
{
    /// <summary>The original set of items over which partitions are created.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private T[] items;

    /// <summary>Flag to indicate if this enumerator has been disposed of.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private bool isDisposed;

    /// <summary>Flag to indicate if this enumerator is in its initial state.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private bool isFirst;

    /// <summary>The number of partitions in the current selection.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int nc;

    /// <summary>An array of values indicating the number of values in the partition at the specified index.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int[] p;

    /// <summary>An array of indices indicating to which partition the item at the specified index belongs.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int[] q;

    /// <summary>The current partition.</summary>
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private T[][] current;

    /// <summary>
    /// Creates and initializes an instance of the <see cref="AllPartitionsEnumerator{T}"/> type.
    /// </summary>
    /// <param name="items">The original set of items over which partitions are enumerated.</param>
    public AllPartitionsEnumerator(IEnumerable<T> items)
        : base()
    {
        if (null == items)
        {
            throw new ArgumentNullException("items");
        }

        this.isFirst = true;
        this.items = items.ToArray();
        this.nc = 0;
        this.p = new int[this.items.Length];
        this.q = new int[this.items.Length];
        this.current = null;
    }

    /// <summary>
    /// Gets the current partition.
    /// </summary>
    public IEnumerable<IEnumerable<T>> Current
    {
        get
        {
            this.CheckIfDisposed();

            return this.current;
        }
    }

    /// <summary>
    /// Disposes of this enumerator and releases all resources held by it.
    /// </summary>
    public void Dispose()
    {
        if (this.isDisposed)
        {
            return;
        }

        this.isDisposed = true;
        this.items = null;
        this.p = null;
        this.q = null;
        this.nc = 0;
        this.current = null;
        this.isFirst = true;
    }

    /// <summary>
    /// Gets the current partition.
    /// </summary>
    object IEnumerator.Current
    {
        get
        {
            return this.Current;
        }
    }

    /// <summary>
    /// Selects the next item in the set of all partitions.
    /// </summary>
    /// <returns><c>true</c> if an item was selected; <c>false</c> if we are past the last element.</returns>
    public bool MoveNext()
    {
        this.CheckIfDisposed();

        if (this.isFirst)
        {
            this.isFirst = false;
            this.nc = 1;
            this.p[0] = this.items.Length;
            for (int i = 0; i < this.items.Length; ++i)
            {
                this.q[i] = this.nc;
            }
            this.Select();
            return true;
        }

        if (this.nc == this.items.Length )
        {
            return false;
        }

        int n = this.items.Length;
        int m = n;
        int l = this.q[m-1];

        while (this.p[l - 1] == 1)
        {
            this.q[m - 1] = 1;
            --m;
            l = this.q[m - 1];
        }

        this.nc += m - n;
        this.p[0] = this.p[0] + n - m;

        if (l == this.nc)
        {
            ++this.nc;
            this.p[this.nc - 1] = 0;
        }

        this.q[m - 1] = l + 1;
        this.p[l - 1] = this.p[l - 1] - 1;
        this.p[l] = this.p[l] + 1;

        this.Select();
        return true;
    }

    /// <summary>
    /// Resets this enumerator to its initial state.
    /// </summary>
    public void Reset()
    {
        this.CheckIfDisposed();

        this.current = null;
        this.isFirst = true;
        this.isDisposed = false;
        this.p = new int[this.items.Length];
        this.q = new int[this.items.Length];
        this.nc = 0;
    }

    /// <summary>
    /// Selects the items for the current partition.
    /// </summary>
    private void Select()
    {
        this.current = new T[this.nc][];

        for (int i = 0; i < this.nc; ++i)
        {
            int k = 0;
            this.current[i] = new T[this.p[i]];

            for (int j = 0; j < this.items.Length; ++j)
            {
                if (this.q[j] == i + 1)
                {
                    this.current[i][k] = this.items[j];
                    ++k;
                }
            }
        }
    }

    /// <summary>
    /// Checks and throws an exception if this enumerator has been disposed.
    /// </summary>
    private void CheckIfDisposed()
    {
        if (this.isDisposed)
        {
            throw new ObjectDisposedException(this.GetType().FullName);
        }
    }
}
    /// <summary>
    /// Retrieves all possible partitions of a set of items.
    /// </summary>
    /// <typeparam name="T">The type of the items in the original set.</typeparam>
    /// <param name="items">The original set of items over which partitions are created.</param>
    /// <returns>All possible partitions of the items in <paramref name="items"/>.</returns>
    public static IEnumerable<IEnumerable<IEnumerable<T>>> AllPartitions<T>(this IEnumerable<T> items)
    {
        return new AllPartitionsEnumerable<T>(items);
    }