Creating a power set of a Sequence

后端 未结 4 1547
南旧
南旧 2020-12-01 06:49

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

4条回答
  •  醉话见心
    2020-12-01 07:40

    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 Prepend(this IEnumerable 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> PowerSet(this IEnumerable 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
    {
        public static readonly ImmutableList Empty = new ImmutableList(null, default(T));
        private ImmutableList(ImmutableList tail, T head)
        {
            this.Head = head;
            this.Tail = tail;
        }
        public T Head { get; private set; }
        public ImmutableList Tail { get; private set; }
        public ImmutableList Push(T head)
        {
            return new ImmutableList(this, head);
        }
        public IEnumerable> 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;
            }
        }
    }
    

提交回复
热议问题