【问题标题】:Is it possible to yield all subcollections of an IEnumerable<T>?是否可以产生 IEnumerable<T> 的所有子集合?
【发布时间】:2017-09-28 12:48:21
【问题描述】:

我的意思是,如果我有一套像

{ 1, 2, 3 } 

那么所有子集都是

{  },
{ 1 }, 
{ 2 }, 
{ 3 },
{ 1, 2 },
{ 2, 3 },
{ 1, 3 },
{ 1, 2, 3 }

这是其中的2^3,因为集合的大小为3。我想到的任何解决方案都需要“记住”以前的子集,最大大小为n - 1,其中n 是集合的长度。例如,我有一个我写的解决方案,看起来像

    public static IEnumerable<IEnumerable<T>> AllSubcollections<T>(this IEnumerable<T> source)
    {
        // The empty set is always a subcollection
        yield return new List<T>() { };

        T[] arr = source.ToArray();
        IEnumerable<int> indices = Enumerable.Range(0, arr.Length);
        var last = new List<HashSet<int>>(new List<HashSet<int>>() { new HashSet<int>() });
        for(int k = 1; k < arr.Length; ++k)
        {
            var next = new List<HashSet<int>>(new List<HashSet<int>>());
            foreach(HashSet<int> hs in last)
            {
                foreach(int index in indices)
                {
                    if(!hs.Contains(index))
                    {
                        var addition = hs.Concat(new List<int> { index });
                        yield return addition.Select(i => arr[i]);
                        next.Add(new HashSet<int>(addition));
                    }
                }
            }
        }
    }

注意这是如何假设 source 可以放入数组中,并假设 HashSet 可以保存以前的子集合。

鉴于IEnumerable&lt;T&gt; 可以yield 获得任意数量的结果(甚至无限数量),是否可以编写一个解决方案来解决子集问题?

【问题讨论】:

  • @C.McCoyIV:他的解决方案在其输入上调用 .ToArray() 并想知道是否有可能为此编写一个不会在无限馈送时立即爆炸的算法。

标签: c# .net algorithm memory-management


【解决方案1】:

您自己指出了问题中的缺陷。我们有IEnumerables 不会在存档时终止。

事实上,如果你仔细编写你的集合枚举器,让它返回另一个IEnumerable,这仍然是可能的。如果源没有终止,这不会终止,但这并不是一个糟糕的问题。无论如何,十亿行的所有子集都会运行太长时间,因此您可能永远不会遇到OutOfMemoryException

public static IEnumerable<IReadOnlyList<T>> AllSubcollections<T>(this IEnumerable<T> source)
{
    // The empty set is always a subcollection
    yield return new List<T>() { };

    List<T> priors = new List<T>();
    List<bool> includes = new List<bool>(); // Need a bitvector to get past 64 elements.
    while (source.MoveNext())
    {
        for (int i = 0; i < includes.Count; ++i)
            includes[i] = false;
        // Always return the newest item in any set. This avoids dupes.
        priors.Add(source.Current);
        priors.Add(true);

        bool breakout;
        do {
            List<T> set = new List<T>();
            for (int i = 0; i < priors.Count; ++i)
                if (includes[i])
                     set[i] = includes[i];
            yield return (IReadOnlyList<T>)set;

            // Bitvector increment
            breakout = true;
            for (int i = 0; i < priors.Count; ++i) {
                if (!includes[i]) {
                    for (int j = 0; j < i; ++j)
                        includes[j] = 0;
                    includes[i] = true;
                    breakout = false;
                    break;
                }
            }
        } while (!breakout);            
    }
}

【讨论】:

    猜你喜欢
    • 2010-12-23
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多