是否有可能产生IEnumerable <t>的所有子集?

时间:2017-05-01 02:16:38

标签: c# .net algorithm memory-management

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

那样的话
{ 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<T> yield可以#include <stdio.h> int main ( void ) { char A [ 2 ]; printf ( "Do you like this theme? Answer Y or N: " ); scanf ( "%d", A ); if ( A == "Y" ) { printf ( "Good.\n" ); } else { printf ( "Why not?\n" ); } return 0; } 任意数量的结果(甚至是无限量),是否有可能为子集问题编写解决方案?

1 个答案:

答案 0 :(得分:0)

你在问题​​中指出了自己的缺陷。我们IEnumerable不会在归档时终止。

事实上,如果你仔细编写你的set枚举器,它仍然可以返回另一个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);            
    }
}