6

I am trying to create a method that will return all subsets of a set.

For example if I have the collection 10,20,30 I will like to get the following output

        return new List<List<int>>()
        {
            new List<int>(){10},
            new List<int>(){20},
            new List<int>(){30},
            new List<int>(){10,20},
            new List<int>(){10,30},
            new List<int>(){20,30},
            //new List<int>(){20,10}, that substet already exists
            // new List<int>(){30,20}, that subset already exists
            new List<int>(){10,20,30}
        };

because the collection can also be a collection of strings for instance I want to create a generic method. This is what I have worked out based on this solution.

    static void Main(string[] args)
    {
        Foo<int>(new int[] { 10, 20, 30});
    }

    static List<List<T>> Foo<T>(T[] set)
    {

        // Init list
        List<List<T>> subsets = new List<List<T>>();

        // Loop over individual elements
        for (int i = 1; i < set.Length; i++)
        {
            subsets.Add(new List<T>(){set[i - 1]});

            List<List<T>> newSubsets = new List<List<T>>();

            // Loop over existing subsets
            for (int j = 0; j < subsets.Count; j++)
            {
                var tempList = new List<T>();
                tempList.Add(subsets[j][0]);
                tempList.Add(subsets[i][0]);
                var newSubset = tempList;
                newSubsets.Add(newSubset);
            }

            subsets.AddRange(newSubsets);
        }

        // Add in the last element
        //subsets.Add(set[set.Length - 1]);
        //subsets.Sort();

        //Console.WriteLine(string.Join(Environment.NewLine, subsets));
        return null;
    }

Edit

Sorry that is wrong I still get duplicates...

    static List<List<T>> GetSubsets<T>(IEnumerable<T> Set)
    {
        var set = Set.ToList<T>();

        // Init list
        List<List<T>> subsets = new List<List<T>>();

        subsets.Add(new List<T>()); // add the empty set

        // Loop over individual elements
        for (int i = 1; i < set.Count; i++)
        {
            subsets.Add(new List<T>(){set[i - 1]});

            List<List<T>> newSubsets = new List<List<T>>();

            // Loop over existing subsets
            for (int j = 0; j < subsets.Count; j++)
            {
                var newSubset = new List<T>();
                foreach(var temp in subsets[j])
                    newSubset.Add(temp);

                newSubset.Add(set[i]);


                newSubsets.Add(newSubset);
            }

            subsets.AddRange(newSubsets);
        }

        // Add in the last element
        subsets.Add(new List<T>(){set[set.Count - 1]});
        //subsets.Sort();

        return subsets;
    }

Then I could call that method as:

enter image description here

Mat
  • 202,337
  • 40
  • 393
  • 406
Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • 1
    I want to get all subsets of a set. if I have the set {1,2,3} I will like to get {1},{2},{3},{1,2},{1,3},{2,3},{1,2,3} . – Tono Nam Apr 29 '12 at 17:50
  • and what particular problem do you have with your current code? What doesn't work? – BrokenGlass Apr 29 '12 at 17:54
  • Are the elements in the input distinct? If not, how do you want to treat duplicates? – CodesInChaos Apr 29 '12 at 17:59
  • Guys if you don't know what all the subsets of a set means then attempt to answer another question. I don't want to sound rude sorry but I just want to get this homework done and I appreciate very much your help. Perhaps I should named the questions get all subsets of a set but since I am relating it to c# I want to pass a collection to a method and a subset is a list or array that's why I wanted a `List>` as the output of the method that I am trying to create. – Tono Nam Apr 29 '12 at 18:04
  • @TonoNam: I think people understand the subset problem. What's unclear is not what you are trying to do, but what you are struggling with. – Mathias Apr 29 '12 at 18:34
  • Yeah sorry you are right. its just that I been trying to do this algorithm for so long. I had a break, came back to it and finally was able to do it. I posted the answer just now – Tono Nam Apr 29 '12 at 18:50
  • Possible duplicate of [How to get all subsets of an array?](https://stackoverflow.com/questions/999050/how-to-get-all-subsets-of-an-array) – dharmatech Sep 08 '17 at 04:04

7 Answers7

7

This is a basic algorithm which i used the below technique to make a single player scrabble word solver (the newspaper ones).

Let your set have n elements. Increment an integer starting from 0 to 2^n. For each generater number bitmask each position of the integer. If the i th position of the integer is 1 then select the i th element of the set. For each generated integer from 0 to 2^n doing the above bitmasting and selection will get you all the subsets.

Here is a post: http://phoxis.org/2009/10/13/allcombgen/

phoxis
  • 60,131
  • 14
  • 81
  • 117
6

Here is an adaptation of the code provided by Marvin Mendes in this answer but refactored into a single method with an iterator block.

public static IEnumerable<IEnumerable<T>> Subsets<T>(IEnumerable<T> source)
{
    List<T> list = source.ToList();
    int length = list.Count;
    int max = (int)Math.Pow(2, list.Count);

    for (int count = 0; count < max; count++)
    {
        List<T> subset = new List<T>();
        uint rs = 0;
        while (rs < length)
        {
            if ((count & (1u << (int)rs)) > 0)
            {
                subset.Add(list[(int)rs]);
            }
            rs++;
        }
        yield return subset;
    }
}
Community
  • 1
  • 1
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Sorry to ask a dumb question, but why does this work? I don't quite follow the shifting part (`if ((count & (1u << (int)rs)) > 0)`) – Casey Jan 22 '16 at 22:12
  • 1
    @Casey So it's treating `count` as a binary number with number of digits equal to the length of the list. If a digit is `1` then the item in the list that corresponds to that item is included in that permutation, of it's `0` then it isn't. If you count from `0` to `11111...` (where the number of ones equals the size of the list) you've counted every permutation. – Servy Jan 23 '16 at 01:08
2

I know that this question is a little old but i was looking for a answer and dont find any good here, so i want to share this solution that is an adaptation found in this blog: http://praseedp.blogspot.com.br/2010/02/subset-generation-in-c.html

I Only transform the class into a generic class:

public class SubSet<T>
{
    private IList<T> _list;
    private int _length;
    private int _max;
    private int _count;

    public SubSet(IList<T> list)
    {
        if (list== null)
            throw new ArgumentNullException("lista");
        _list = list;
        _length = _list.Count;
        _count = 0;
        _max = (int)Math.Pow(2, _length);
    }


    public IList<T> Next()
    {
        if (_count == _max)
        {
            return null;
        }
        uint rs = 0;

        IList<T> l = new List<T>();            

        while (rs < _length)
        {
            if ((_count & (1u << (int)rs)) > 0)
            {
                l.Add(_list[(int)rs]);                    
            }
            rs++;
        }
        _count++;
        return l;            
    }
}

To use this code you can do like something that:

        List<string> lst = new List<string>();

        lst.AddRange(new string[] {"A", "B", "C" });

        SubSet<string> subs = new SubSet<string>(lst);

        IList<string> l = subs.Next();

        while (l != null)
        {

            DoSomething(l);
            l = subs.Next();
        }

Just remember: this code still be an O(2^n) and if you pass something like 20 elements in the list you will get 2^20= 1048576 subsets!

Edit: As Servy sugest i add an implementation with interator block to use with Linq an foreach, the new class is like this:

private class SubSet<T> : IEnumerable<IEnumerable<T>>
    {
        private IList<T> _list;
        private int _length;
        private int _max;
        private int _count;

        public SubSet(IEnumerable<T> list)
        {
            if (list == null)
                throw new ArgumentNullException("list");
            _list = new List<T>(list);
            _length = _list.Count;
            _count = 0;
            _max = (int)Math.Pow(2, _length);
        }

        public int Count
        {
            get { return _max; }
        }



        private IList<T> Next()
        {
            if (_count == _max)
            {
                return null;
            }
            uint rs = 0;

            IList<T> l = new List<T>();

            while (rs < _length)
            {
                if ((_count & (1u << (int)rs)) > 0)
                {
                    l.Add(_list[(int)rs]);
                }
                rs++;
            }
            _count++;
            return l;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            IList<T> subset;
            while ((subset = Next()) != null)
            {
                yield return subset;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

and you now can use it like this:

        List<string> lst = new List<string>();

        lst.AddRange(new string[] {"A", "B", "C" });

        SubSet<string> subs = new SubSet<string>(lst);

        foreach(IList<string> l in subs)
        {
            DoSomething(l);
        }

Thanks Servy for the advice.

  • You can improve the usability of a method such as this by making it return an `IEnumerable>` and using iterator blocks to write it, rather than having your own custom iterator that doesn't quite match the `IEnumerable` syntax. If you make that change you will allow callers to easily `foreach` over the results, use LINQ, etc., and it would not be a significant amount of work on your part thanks to iterator blocks. – Servy Feb 11 '13 at 18:59
  • Thanks for the advice, this could be done really simple, just run the loop inside the SubSet class insert the elements on an IEnumerable> or using interator blocks like you said but i guess this class is dangerous becouse of the O(2^n) and could take too many time to return all the subsets. I just do this way becouse is more easy to control how much sets you want return from the subsets and for simplicity, of course. – Marvin Mendes Feb 11 '13 at 20:15
  • If you put the items into a list you would be right, as that would eagerly evaluate all of the results. I didn't suggest doing that. If you use an iterator block it will defer execution thus resulting in each subset only being computed when it is requested, thus maintaining the same behavior as your current implementation in that regard. – Servy Feb 11 '13 at 20:16
  • I guess is this what you want, if no, please correct me, and thanks :-) – Marvin Mendes Feb 11 '13 at 21:05
  • That certainly works, although I was suggesting that you just make the entire solution just a method that returns `IEnumerable>` instead of exposing both, but that's fine too. – Servy Feb 11 '13 at 21:08
  • I think it's easier to just show you what I meant. I posted another answer to this question since it wouldn't fit in a comment, and it's a bit much for an edit. http://stackoverflow.com/a/14820995/1159478 . You should note that it looks pretty much the same; it's not structuring the code significantly differently, it's just a lot shorter since you can remove a lot of the boilerplate code. – Servy Feb 11 '13 at 21:17
2

It Doesn't give duplicate value;

Don't add a value of the int array at the start of the subsets

Correct program is as follows:

class Program
    {
        static HashSet<List<int>> SubsetMaker(int[] a, int sum)
        {
            var set = a.ToList<int>();
            HashSet<List<int>> subsets = new HashSet<List<int>>();
            subsets.Add(new List<int>());
            for (int i =0;i<set.Count;i++)
            {
                //subsets.Add(new List<int>() { set[i]});
                HashSet<List<int>> newSubsets = new HashSet<List<int>>();
                for (int j = 0; j < subsets.Count; j++)
                {
                   var newSubset = new List<int>();
                   foreach (var temp in subsets.ElementAt(j))
                   {
                      newSubset.Add(temp);


                    }
                    newSubset.Add(set[i]);
                    newSubsets.Add(newSubset);

                }
                Console.WriteLine("New Subset");
                foreach (var t in newSubsets)
                {
                    var temp = string.Join<int>(",", t);
                    temp = "{" + temp + "}";
                    Console.WriteLine(temp);
                }
                Console.ReadLine();

                subsets.UnionWith(newSubsets);
            }
            //subsets.Add(new List<int>() { set[set.Count - 1] });
            //subsets=subsets.;
            return subsets;

        }
        static void Main(string[] args)
        {
            int[] b = new int[] { 1,2,3 };
            int suma = 6;
            var test = SubsetMaker(b, suma);
            Console.WriteLine("Printing final set...");
            foreach (var t in test)
            {
                var temp = string.Join<int>(",", t);
                temp = "{" + temp + "}";
                Console.WriteLine(temp);
            }
            Console.ReadLine();

        }
    }
Avinash Singh
  • 4,970
  • 8
  • 20
  • 35
0

You don't want to return a set of lists, you want to use java's set type. Set already does part of what you are looking for by holding only one unique element of each type. So you can't add 20 twice for instance. It is an unordered type, so what you might do is write a combinatoric function that creates a bunch of sets and then return a list that includes alist of those.

Adam Miller
  • 1,756
  • 1
  • 25
  • 44
0

Get all subsets of a collection of a specific subsetlength:

    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length) where T : IComparable
    {
        if (length == 1) return list.Select(t => new T[] { t });
        return GetPermutations(list, length - 1).SelectMany(t => list.Where(e => t.All(g => g.CompareTo(e) != 0)), (t1, t2) => t1.Concat(new T[] { t2 }));
    }

    public static IEnumerable<IEnumerable<T>> GetOrderedSubSets<T>(IEnumerable<T> list, int length) where T : IComparable
    {
        if (length == 1) return list.Select(t => new T[] { t });
        return GetOrderedSubSets(list, length - 1).SelectMany(t => list.Where(e => t.All(g => g.CompareTo(e) == -1)), (t1, t2) => t1.Concat(new T[] { t2 }));
    }

Testcode:

        List<int> set = new List<int> { 1, 2, 3 };
        foreach (var x in GetPermutations(set, 3))
        {
            Console.WriteLine(string.Join(", ", x));
        }
        Console.WriteLine();
        foreach (var x in GetPermutations(set, 2))
        {
            Console.WriteLine(string.Join(", ", x));
        }
        Console.WriteLine();
        foreach (var x in GetOrderedSubSets(set, 2))
        {
            Console.WriteLine(string.Join(", ", x));
        }

Test results:

1, 2, 3
1, 3, 2
2, 1, 3
2, 3, 1
3, 1, 2
3, 2, 1

1, 2
1, 3
2, 1
2, 3
3, 1
3, 2

1, 2
1, 3
2, 3
0

A simple algorithm based upon recursion:

private static List<List<int>> GetPowerList(List<int> a)
    {
        int n = a.Count;
        var sublists = new List<List<int>>() { new List<int>() };
        for (int i = 0; i < n; i++)
        {
            for (int j = i; j < n; j++)
            {
                var first = a[i];
                var last = a[j];
                if ((j - i) > 1)
                {
                    sublists.AddRange(GetPowerList(a
                        .GetRange(i + 1, j - i - 1))
                        .Select(l => l
                        .Prepend(first)
                        .Append(last).ToList()));
                }
                else sublists.Add(a.GetRange(i,j - i + 1));
            }
        }
        return sublists;
    }
Deepak Mishra
  • 2,984
  • 1
  • 26
  • 32