20

I am trying to create a program that is a base for creating possible combinations of a sequence, string or a number. This is some sort of encryption / decryption program. I am using Visual Studio 2013 and C#. What I am trying to do is to generate a power set out of a sequence, but I am little bit confused and can't proceed any further. Here is the code.

public static void randomSeq()
{
    int temp = 0;
    string seq = "1234";

    var sb = new StringBuilder();
    char[] bits = seq.Select((char c) => c).ToArray();

    Console.Write("Given Sequence: ");
    Console.Write(seq);
    Console.WriteLine();
    Console.WriteLine("Generated possiblities");

    foreach (char item in bits)
        Console.WriteLine(item);
    do
    {
        if (temp <= 2)
        {
            for (int i = temp + 1; i < bits.Length; i++)
            {
                 sb.Append(bits[temp]);
                 sb.Append(bits[i]);
                 Console.WriteLine(sb);
                
                 sb.Clear();
            }
        }
        else if (temp > 2)
        {
            for (int k = 0; k < temp; k++)
                sb.Append(bits[k]);

            for (int l = temp + 1; l < bits.Length; l++)
            {
                sb.Append(bits[temp]);
                sb.Append(bits[l]);
                Console.WriteLine(sb);

                sb.Clear();
            }
        }
        temp++;
    }
    while (temp != bits.Length);
}

I want this code to be generic i.e. I pass any sequence and it generates a power set for me. Then I want to reuse it further in my program. I can do the rest simply I am stuck in generating the power set. Can someone help me?.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Jamal Hussain
  • 391
  • 1
  • 3
  • 13
  • 2
    Have a look at this. http://adrianwithy.com/2012/02/23/c-power-set-builder/ – Sam Leach Nov 10 '13 at 14:41
  • 1
    If this subject interests you, you might want to see my article on producing the n-ary Cartesian product of sequences and my series on producing all the permutations of a series. http://ericlippert.com/2013/04/15/producing-permutations-part-one/ and http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/ – Eric Lippert Nov 11 '13 at 22:21

4 Answers4

41

Power set is easy to generate if one is familiar with bits. For the set of N elements, there will be 2^N subsets which will go to power set (including empty set and initial set). So each element will be either IN or OUT (1 or 0 in other words).

Taking this into consideration, it is easy to represent subsets of the set as bit masks. Then enumerating through all possible bit masks, it is possible construct the whole power sets. In order to do this we need to examine each bit in bit mask and take element of input set if there is 1 in that place. Below is example for string (collection of chars) as input. It can be easily rewritten to work for collection of any type values.

private static List<string> PowerSet(string input)
{
    int n = input.Length;
    // Power set contains 2^N subsets.
    int powerSetCount = 1 << n;
    var ans = new List<string>();

    for (int setMask = 0; setMask < powerSetCount; setMask++)
    {
        var s = new StringBuilder();
        for (int i = 0; i < n; i++)
        {
            // Checking whether i'th element of input collection should go to the current subset.
            if ((setMask & (1 << i)) > 0)
            {
                s.Append(input[i]);
            }
        }
        ans.Add(s.ToString());
    }

    return ans;
}

Example

Suppose you have string "xyz" as input, it contains 3 elements, than there will be 2^3 == 8 elements in power set. If you will be iterating from 0 to 7 you will get the following table. Columns: (10-base integer; bits representation (2-base); subset of initial set).

0   000    ...
1   001    ..z
2   010    .y.
3   011    .yz
4   100    x..
5   101    x.z
6   110    xy.
7   111    xyz

You can notice that third column contains all subsets of initial string "xyz"


Another approach (twice faster) and generic implementation

Inspired by Eric's idea, I have implemented another variant of this algorithm (without bits now). Also I made it generic. I believe this code is near to fastest of what can be written for Power Set calculation. Its complexity is the same as for bits approach O(n * 2^n), but for this approach constant is halved.

public static T[][] FastPowerSet<T>(T[] seq)
{
    var powerSet = new T[1 << seq.Length][];
    powerSet[0] = new T[0]; // starting only with empty set

    for (int i = 0; i < seq.Length; i++)
    {
        var cur = seq[i];
        int count = 1 << i; // doubling list each time
        for (int j = 0; j < count; j++)
        {
            var source = powerSet[j];
            var destination = powerSet[count + j] = new T[source.Length + 1];
            for (int q = 0; q < source.Length; q++)
                destination[q] = source[q];
            destination[source.Length] = cur;
        }
    }
    return powerSet;
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
SergeyS
  • 3,515
  • 18
  • 27
  • 1
    Thanks this was quite helpful. How you did it with bits ? – Jamal Hussain Nov 10 '13 at 15:00
  • I have added Example to my answer, hope it helps. – SergeyS Nov 10 '13 at 15:14
  • 1
    I see got it the example. it was excellent explanation by you ! – Jamal Hussain Nov 10 '13 at 16:00
  • 2
    I know its late but I think there is one problem in both version of your code. The code `1 << n` and `1 << seq.Length` will only work if `n` or `seq.Length` are less than or equal to '30' (in other words , it can calculate powerSet of a set containing 30 elements, not more than that) because the literal '1' will be represented as Int32 data type (means only 32 bits to represent 1) and doing `1<<31` will result in '-2147483648' and doing `1<<32` will result in '1' . I know you did this to avoid calculation of 2^N which can be expensive. Kindly correct me If I'm wrong. – Syed Ahmed Jamil Feb 03 '18 at 14:49
  • 1
    @SyedAhmedJamil It can be as big as 31 elements, not 30 (we're counting from 0). But a power set of more than 31 elements is infeasible anyway - it will contain more than `2,147,483,648` elements! Can you handle such a huge array? – Bip901 Feb 10 '21 at 17:09
  • Thanks for this. My trivial contribution: change line 2 to `powerSet[0] = Array.Empty();` – Aaron Hudon Jun 08 '22 at 23:35
9

SergeyS's approach is perfectly reasonable. Here's an alternative way to think about it.

For the purposes of this answer I'm going to assume that "sets" are finite sequences.

We define the function P recursively as follows.

  • A set is either empty, or a single item H followed by a set T.
  • P(empty) --> { empty }
  • P(H : T) --> the union of P(T) and every element of P(T) prepended with H.

Let's try that out. What's the power set of {Apple, Banana, Cherry}?

It's not an empty set, so the power set of {Apple, Banana, Cherry} is the power set of {Banana, Cherry}, plus the sets formed by prepending Apple to each.

So we need to know the power set of {Banana, Cherry}. It's the power set of {Cherry} plus the sets form by prepending Banana to each.

So we need to know the power set of {Cherry}. It's the power set of the empty set, plus the sets formed by prepending Cherry to each.

So we need to know the power set of the empty set. It's the set containing the empty set. { {} }

Now prepend each element with Cherry and take the union. That's { {Cherry}, {} }. That gives us the power set of { Cherry }. Remember we needed that to find the power set of {Banana, Cherry}, so we union it with Banana prepended to each and get { {Banana, Cherry}, {Banana}, {Cherry}, {}} and that's the power set of {Banana, Cherry}.

Now we needed that to get the power set of {Apple, Banana, Cherry}, so union it with Apple appended to each and we have { {Apple, Banana, Cherry}, {Apple, Banana}, {Apple, Cherry}, {Apple}, {Banana, Cherry}, {Banana}, {Cherry}, {}} and we're done.

The code should be straightforward to write. First we'll need a helper method:

static IEnumerable<T> Prepend<T>(this IEnumerable<T> tail, T head)
{
    yield return head;
    foreach(T item in tail) yield return item;
}

And now the code is a straightforward translation of the description of the algorithm:

static IEnumerable<IEnumerable<T>> PowerSet<T>(this IEnumerable<T> items)
{
    if (!items.Any())
        yield return items; // { { } } 
    else
    {
        var head = items.First();
        var powerset = items.Skip(1).PowerSet().ToList();
        foreach(var set in powerset) yield return set.Prepend(head); 
        foreach(var set in powerset) yield return set;
     }
}                

Make sense?

----------- UPDATE ----------------

Sergey points out correctly that my code has a Schlemiel the Painter algorithm and therefore consumes huge amounts of time and memory; good catch Sergey. Here's an efficient version that uses an immutable stack:

class ImmutableList<T>
{
    public static readonly ImmutableList<T> Empty = new ImmutableList<T>(null, default(T));
    private ImmutableList(ImmutableList<T> tail, T head)
    {
        this.Head = head;
        this.Tail = tail;
    }
    public T Head { get; private set; }
    public ImmutableList<T> Tail { get; private set; }
    public ImmutableList<T> Push(T head)
    {
        return new ImmutableList<T>(this, head);
    }
    public IEnumerable<ImmutableList<T>> PowerSet()
    {
        if (this == Empty)
            yield return this;
        else
        {
            var powerset = Tail.PowerSet();
            foreach (var set in powerset) yield return set.Push(Head);
            foreach (var set in powerset) yield return set;
        }
    }
}
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    Idea is good, but implementation is not so good from the performance point of view. Program crashes with OutOfMemory Exception for the set of 20 elements on my computer (more than 1.5 GB of memory usage at time of crash). – SergeyS Nov 10 '13 at 22:20
  • @SergeyS: Good point; there is a lot of unnecessary recomputation going on here due to the deferred nature of LINQ execution. Here, I'll fix it. Try it now; a little `ToList` can make a big difference. – Eric Lippert Nov 11 '13 at 01:11
  • Thanks Eric, but this is still not efficient. Taking .Count() for whole return value plus taking .Count() on each element of return value will take 10 seconds and 700 MB of memory for input of 20 elements on my machine. Both numbers (time and memory) can be improved in about 10 fold. – SergeyS Nov 11 '13 at 06:07
  • @SergeyS: Ah, I see it, we've got a Schlemiel the Painter algorithm in the construction; each 20-element list allocates a 19 element list, which allocates an 18-element list... – Eric Lippert Nov 11 '13 at 18:22
  • I'll see if I can work up a more efficient way to express this idea. – Eric Lippert Nov 11 '13 at 21:12
  • @SergeyS: OK, here's a version with an immutable stack that is quite efficient I believe. – Eric Lippert Nov 11 '13 at 21:49
  • If you ensure `ImmutableList` implements `IEnumerable` it would likely make it a bit more useful, just so that the caller can convert your `PowerSet` back to an `IEnumerable>` when they get the result. – Servy Nov 11 '13 at 21:57
  • Eric, ImmutableList is a good idea, but still not fast enough, and I believe this is because of its recursive nature. I have added fast version of this algorithm to my answer. It uses slight modification of your idea (append instead prepend) and also recursion-free. From my quick measurements it is more than 10 times faster than your latest code. – SergeyS Nov 12 '13 at 23:58
  • @sergey indeed we have yet another painter algorithm, this time hidden in the nested enumerator! That said, fast enough for who? This is an exponential algorithm so optimizing it for speed is perhaps not that interesting. – Eric Lippert Nov 13 '13 at 00:22
  • 3
    Eric, I think it does not matter if algo is polynomial or exponential. If it can be optimized in 10 fold, it is definitely makes some interest to do this :) Concerning exponential algos, it is very interesting to implement them in non-recursive way. – SergeyS Nov 13 '13 at 22:47
  • 3
    @SergeyS: If you have a particular target size in mind then sure, the optimization is likely worth pursuing. My point is: suppose you can reasonably handle cases up to size 40 at speed X. Then you do a lot of optimization and get it up to speed 10X. Now suddenly you can reasonably handle cases up to size 43, not size 400. The effort spent on optimizing the constant factor of an exponential algorithm does not give you a big payoff, the way it does with optimizing the constant factor of polynomial algorithms. – Eric Lippert Nov 13 '13 at 23:06
  • 2
    @SergeyS: None of which is to say that your approach is wrong; it's a nice approach. This is also a good example of how the most straightforward LINQ solution can *sometimes* be surprisingly suboptimal due to unexpected recomputation and hidden costs. – Eric Lippert Nov 13 '13 at 23:08
2

Same algorithm SergeyS mention using Linq (where inputSet is the input and outputPowerSet is the output):

int setLength = inputSet.Count;
int powerSetLength = 1 << setLength;
for (int bitMask = 0; bitMask < powerSetLength; bitMask++)
{
    var subSet = from x in inputSet 
                 where ((1 << inputSet.IndexOf(x)) & bitMask) != 0 
                 select x;
    outputPowerSet.Add(subSet.ToList());
}
Community
  • 1
  • 1
uriel
  • 1,209
  • 1
  • 16
  • 26
  • I ran in some unwanted results, I tweaked it abit and I think it will be better now, change subset to `var subSet = input.Where((u, i) => ((1 << i) & bitMask) != 0).Select(x => x);` – matthijsb Jan 12 '16 at 11:32
  • 1
    One-liner version of your entire solution (where `e` is any enumerable): `Enumerable.Range(0, (int)Math.Pow(2, e.Count())).Select(b => e.Where((x, i) => ((1 << i) & b) != 0))` – Allon Guralnek Nov 11 '20 at 07:39
0

Very late to the game, but why not the approach below? It seems significantly simpler than the suggestions posted here:

    /*
    Description for a sample set {1, 2, 2, 3}:
    Step 1 - Start with {}:
    {}
    Step 2 - "Expand" previous set by adding 1:
    {}
    ---
    {1}
    Step 3 - Expand previous set by adding the first 2:
    {}
    {1}
    ---
    {2}
    {1,2}
    Step 4 - Expand previous set by adding the second 2:
    {}
    {1}
    {2}
    {1,2}
    ---
    {2}
    {1,2}
    {2,2}
    {1,2,2}
    Step 5 - Expand previous set by adding 3:
    {}
    {1}
    {2}
    {1,2}
    {2}
    {1,2}
    {2,2}
    {1,2,2}
    ---
    {3}
    {1,3}
    {2,3}
    {1,2,3}
    {2,3}
    {1,2,3}
    {2,2,3}
    {1,2,2,3}
    Total elements = 16 (i.e. 2^4), as expected.
    */

    private static void PowerSet(IList<int> nums, ref IList<IList<int>> output)
    {
        // ToDo: validate args
        output.Add(new List<int>());
        ExpandSet(nums, 0, ref output);
    }

    private static void ExpandSet(IList<int> nums, int pos, ref IList<IList<int>> output)
    {
        if (pos == nums.Count)
        {
            return;
        }

        List<int> tmp;
        int item = nums[pos];

        for (int i = 0, n = output.Count; i < n; i++)
        {
            tmp = new List<int>();
            tmp.AddRange(output[i]);
            tmp.Add(item);
            output.Add(tmp);
        }

        ExpandSet(nums, pos + 1, ref output);
    }
davidfg
  • 1
  • 2