How to use query syntax to create permutations?
Asked Answered
B

3

6

I've tried to write a method which returns the permutation of a given enumerable as simple as possible. The code:

using System.Collections.Generic;

public static partial class Permutable {
    static IEnumerable<IEnumerable<T>> PermuteIterator<T>(
        IEnumerable<T> source, int offset) {
        var count=0;

        foreach(var dummy in source)
            if(++count>offset)
                foreach(
                    var sequence in
                        Permutable.PermuteIterator(
                            source.Exchange(offset, count-1), 1+offset)
                    )
                    yield return sequence;

        if(offset==count-1)
            yield return source;
    }

    public static IEnumerable<IEnumerable<T>> AsPermutable<T>(
        this IEnumerable<T> source) {
        return Permutable.PermuteIterator(source, 0);
    }

    public static IEnumerable<T> Exchange<T>(
        this IEnumerable<T> source, int index1, int index2) {
        // exchange elements at index1 and index2
    }
}

As the code has simplified within the iterator block, I'm trying to make it simply a single query expression of LINQ.

There's a recursion in the nested foreach with this code, even another possibly yield outside the foreach; and which is the difficult part for me to rewrite it in query syntax.

I've read this answer:

C# String permutation

But I guess it's not the solution for me ..

I tried various ways, and think it's not so easy to do. How can I get it done?

(The Exchange method is another problem, and I've asked a question:

How to exchange the items of enumeration by interating only once?

But I guess it's not the matter here .. )

Belfry answered 3/6, 2013 at 22:42 Comment(2)
You're in luck. Eric Lippert just wrote a series on generating permutations: ericlippert.com/2013/04/15/producing-permutations-part-oneSemimonthly
@spender: Yep, thank you. I've actually read up the whole series from part one to seven. The algorithm in Mr. Lippert's article is indeed much better than this. Just that this question is related to the query syntax though ..Belfry
T
3

EDIT 1:

Single-line solution without recursions

I have recreated the core method (from the Previous Solution below in this answer), so that now it is not recursive anymore. It is now easy to make a single-line solution from this.

I had to use Enumerable methods and extension methods to do it though. Without these, I think it is impossible to do this.

class Permutator
{
    private static IEnumerable<IEnumerable<int>> CreateIndices(int length)
    {
        var factorial = Enumerable.Range(2, length - 1)
            .Aggregate((a, b) => a * b);

        return (from p in Enumerable.Range(0, factorial)
                // creating module values from 2 up to length
                // e.g. length = 3: mods = [ p%2, p%3 ]
                // e.g. length = 4: mods = [ p%2, p%3, p%4 ]
                let mods = Enumerable.Range(2, length - 1)
                    .Select(m => p % m).ToArray()
                select (
                    // creating indices for each permutation
                    mods.Aggregate(
                        new[] { 0 },
                        (s, i) =>
                            s.Take(i)
                            .Concat(new[] { s.Length })
                            .Concat(s.Skip(i)).ToArray())
                    ));
    }

    public static IEnumerable<IEnumerable<T>> Get<T>(IEnumerable<T> items)
    {
        var array = items.ToArray();
        return from indices in CreateIndices(array.Length)
                select (from i in indices select array[i]);
    }
}

Now the final solution

The resulting thing is this monstrosity:

class Permutator
{
    public static IEnumerable<IEnumerable<T>> Get<T>(IEnumerable<T> items)
    {
        return
            from p in Enumerable.Range(0,
                Enumerable.Range(2, items.Count() - 1)
                    .Aggregate((a, b) => a * b))
            let mods = Enumerable.Range(2, items.Count() - 1)
                .Select(m => p % m).ToArray()
            select mods.Aggregate(
                items.Take(1).ToArray(),
                (s, i) =>
                    s.Take(i)
                    .Concat(items.Skip(s.Length).Take(1))
                    .Concat(s.Skip(i)).ToArray());
    }
}

Previous solution

I have created something that might be what you are looking for:

class Permutator
{
    private static IEnumerable<IEnumerable<int>> CreateIndices(int length)
    {
        return (from p in Enumerable.Range(0, length)
                select (
                    from s in Permutator.CreateIndices(length - 1)
                              .DefaultIfEmpty(Enumerable.Empty<int>())
                    select s.Take(p)
                           .Concat(new[] { length - 1 })
                           .Concat(s.Skip(p))
                    ))
                    .SelectMany(i => i);
    }

    public static IEnumerable<IEnumerable<T>> Get<T>(IEnumerable<T> items)
    {
        var array = items.ToArray();
        return from indices in CreateIndices(array.Length)
                select (from i in indices select array[i]);
    }
}

Example of how to use it:

var items = new[] { "0", "1", "2" };
var p = Permutator.Get(items);
var result = p.Select(a=>a.ToArray()).ToArray();

How it works

The core is the CreateIndices method. It creates a sequence that contains the indices of the source elements, for each permutation.

It is best to explain with an example:

CreateIndices(0);
// returns no permutations

CreateIndices(1);
// returns 1 permutation
// [ 0 ]

CreateIndices(2);
// returns 2 permutations
// [ 1, 0 ]
// [ 0, 1 ]

CreateIndices(3);
// returns 6 permutations
// [ 2, 1, 0 ]
// [ 2, 0, 1 ]
// [ 1, 2, 0 ]
// [ 0, 2, 1 ]
// [ 1, 0, 2 ]
// [ 0, 1, 2 ]

It is a recursive method, based only on enumerable extensions, and LINQ syntax queries.

The idea of the recursion is that each level builds based on the previous.

CreateIndices(n) adds the element n-1 to the permutations returned by CreateIndices(n-1), in all available positions.

The root of the recursion is CreateIndices(0) returning an empty set of permutations.

Explaining step by step: CreateIndices(3)


1. Lets start by creating the result of CreateIndices(0):

  • empty


2. Then the result of CreateIndices(1):

  • add element 0 (n-1) to each of previous permutations, at position 0
    [ 0 ]


3. Then the result of CreateIndices(2)

  • add element 1 (n-1) to each of previous permutations, at position 0
    [ 1, 0 ]
  • add element 1 (n-1) to each of previous permutations, at position 1
    [ 0, 1 ]


4. Then the result of CreateIndices(3)

  • add element 2 (n-1) to each of previous permutations, at position 0
    [ 2, 1, 0 ]
    [ 2, 0, 1 ]
  • add element 2 (n-1) to each of previous permutations, at position 1
    [ 1, 2, 0 ]
    [ 0, 2, 1 ]
  • add element 2 (n-1) to each of previous permutations, at position 2
    [ 1, 0, 2 ]
    [ 0, 1, 2 ]

What happens next

Now that we have the indices for each of the permutations, we can use them to build the real permutations of values. This is what the generic Get method does.

Also note that the Get method is the only one that materializes the source sequence into an array. The CreateIndices is just an enumerator, without any real objects... so you only pay the cost, when enumerating the sequences, and when calling the Get you pay to create an array of the source sequence.

This explains why after calling Get in the example, I had to materialize the result, so that we can use it:

var items = new[] { "0", "1", "2" };
var p = Permutator.Get(items); // pay to create array from source elements
var result = p.Select(a => a.ToArray() // pay to create arrays for each of the permutations
    ).ToArray(); // pay to get the permutations

This allows us to pay only half of the cost, if we just enumerate half of the permutations.

Twoedged answered 13/6, 2013 at 0:5 Comment(5)
Looks fine. I'm actually wondering how to write a single query expression without declaring method to generate full permutations. However, all the answers are still some steps far.Belfry
I see... I'll try another non recursive approach to this problem then, since recursions requires methods.Twoedged
It's done... but I had to use Enumerable class methods to create the solution. I hope it is what you are looking for. I don't think there are other ways.Twoedged
I'd say that, this is the closest one. Well done, thank you. I'll award the bounty after the duration of promotion.Belfry
It is funny to me how easy (or at least short) it is to generate all combinations in LINQ, but permutations is so much more difficult.Egocentrism
P
4

Since you're looking for an answer that leverages the existing LINQ query operators, rather than using iterator blocks, here's one. Note that it won't be as efficient as some other solutions; the use of these query operators isn't as efficient as, say Eric Lippert's solution. (It is a lot shorter though.)

Also note that since this solution uses the overloads of SelectMany and Where that accept an index, those operators need to be called using method syntax, not query syntax, and several of the other operators don't have query syntax equivalents. I could change the one Select into query syntax, but for consistencies sake I did not.

public static IEnumerable<IEnumerable<T>> Permuatations<T>(
    this IEnumerable<T> source)
{
    var list = source.ToList();//becase we iterate it multiple times
    return list.SelectMany((item, i) => list.Where((_, index) => index != i)
            .Permuatations()
            .Select(subsequence => new[] { item }.Concat(subsequence)))
        .DefaultIfEmpty(Enumerable.Empty<T>());
}

So, to talk through what this is doing.

First it goes through the source sequence; for each item in that sequence it creates a sequence that is just like the source sequence but with the "current item" taken out. (This is the list.Where method).

Next it (recursively) gets all of the permutations of that subsequence.

After that it prepends the "removed" item to the beginning of each of those subsequences.

All of these subsequences are flattened together as they are all inside the SelectMany.

The DefaultIfEmpty is there to ensure that the outer sequence is never empty. Permuting an empty sequence results in a sequence with an empty sequence inside of it. This is effective the "base case" of the recursive operation.

Phidippides answered 7/6, 2013 at 16:29 Comment(3)
Thank you. Got the first answer. There still be some difficulty for me to rewrite this in query syntax, and I need some time for thinking about it.Belfry
@KenKin As I explained, the only operation here that can be converted into query syntax is the one Select call. None of the other LINQ methods used have query syntax implementations (due to the overloads used).Phidippides
I believe that I can translate most method syntax to query syntax by some commonly known tricks. But, yes, some might not be efficient. I'd like to give it a try.Belfry
T
3

EDIT 1:

Single-line solution without recursions

I have recreated the core method (from the Previous Solution below in this answer), so that now it is not recursive anymore. It is now easy to make a single-line solution from this.

I had to use Enumerable methods and extension methods to do it though. Without these, I think it is impossible to do this.

class Permutator
{
    private static IEnumerable<IEnumerable<int>> CreateIndices(int length)
    {
        var factorial = Enumerable.Range(2, length - 1)
            .Aggregate((a, b) => a * b);

        return (from p in Enumerable.Range(0, factorial)
                // creating module values from 2 up to length
                // e.g. length = 3: mods = [ p%2, p%3 ]
                // e.g. length = 4: mods = [ p%2, p%3, p%4 ]
                let mods = Enumerable.Range(2, length - 1)
                    .Select(m => p % m).ToArray()
                select (
                    // creating indices for each permutation
                    mods.Aggregate(
                        new[] { 0 },
                        (s, i) =>
                            s.Take(i)
                            .Concat(new[] { s.Length })
                            .Concat(s.Skip(i)).ToArray())
                    ));
    }

    public static IEnumerable<IEnumerable<T>> Get<T>(IEnumerable<T> items)
    {
        var array = items.ToArray();
        return from indices in CreateIndices(array.Length)
                select (from i in indices select array[i]);
    }
}

Now the final solution

The resulting thing is this monstrosity:

class Permutator
{
    public static IEnumerable<IEnumerable<T>> Get<T>(IEnumerable<T> items)
    {
        return
            from p in Enumerable.Range(0,
                Enumerable.Range(2, items.Count() - 1)
                    .Aggregate((a, b) => a * b))
            let mods = Enumerable.Range(2, items.Count() - 1)
                .Select(m => p % m).ToArray()
            select mods.Aggregate(
                items.Take(1).ToArray(),
                (s, i) =>
                    s.Take(i)
                    .Concat(items.Skip(s.Length).Take(1))
                    .Concat(s.Skip(i)).ToArray());
    }
}

Previous solution

I have created something that might be what you are looking for:

class Permutator
{
    private static IEnumerable<IEnumerable<int>> CreateIndices(int length)
    {
        return (from p in Enumerable.Range(0, length)
                select (
                    from s in Permutator.CreateIndices(length - 1)
                              .DefaultIfEmpty(Enumerable.Empty<int>())
                    select s.Take(p)
                           .Concat(new[] { length - 1 })
                           .Concat(s.Skip(p))
                    ))
                    .SelectMany(i => i);
    }

    public static IEnumerable<IEnumerable<T>> Get<T>(IEnumerable<T> items)
    {
        var array = items.ToArray();
        return from indices in CreateIndices(array.Length)
                select (from i in indices select array[i]);
    }
}

Example of how to use it:

var items = new[] { "0", "1", "2" };
var p = Permutator.Get(items);
var result = p.Select(a=>a.ToArray()).ToArray();

How it works

The core is the CreateIndices method. It creates a sequence that contains the indices of the source elements, for each permutation.

It is best to explain with an example:

CreateIndices(0);
// returns no permutations

CreateIndices(1);
// returns 1 permutation
// [ 0 ]

CreateIndices(2);
// returns 2 permutations
// [ 1, 0 ]
// [ 0, 1 ]

CreateIndices(3);
// returns 6 permutations
// [ 2, 1, 0 ]
// [ 2, 0, 1 ]
// [ 1, 2, 0 ]
// [ 0, 2, 1 ]
// [ 1, 0, 2 ]
// [ 0, 1, 2 ]

It is a recursive method, based only on enumerable extensions, and LINQ syntax queries.

The idea of the recursion is that each level builds based on the previous.

CreateIndices(n) adds the element n-1 to the permutations returned by CreateIndices(n-1), in all available positions.

The root of the recursion is CreateIndices(0) returning an empty set of permutations.

Explaining step by step: CreateIndices(3)


1. Lets start by creating the result of CreateIndices(0):

  • empty


2. Then the result of CreateIndices(1):

  • add element 0 (n-1) to each of previous permutations, at position 0
    [ 0 ]


3. Then the result of CreateIndices(2)

  • add element 1 (n-1) to each of previous permutations, at position 0
    [ 1, 0 ]
  • add element 1 (n-1) to each of previous permutations, at position 1
    [ 0, 1 ]


4. Then the result of CreateIndices(3)

  • add element 2 (n-1) to each of previous permutations, at position 0
    [ 2, 1, 0 ]
    [ 2, 0, 1 ]
  • add element 2 (n-1) to each of previous permutations, at position 1
    [ 1, 2, 0 ]
    [ 0, 2, 1 ]
  • add element 2 (n-1) to each of previous permutations, at position 2
    [ 1, 0, 2 ]
    [ 0, 1, 2 ]

What happens next

Now that we have the indices for each of the permutations, we can use them to build the real permutations of values. This is what the generic Get method does.

Also note that the Get method is the only one that materializes the source sequence into an array. The CreateIndices is just an enumerator, without any real objects... so you only pay the cost, when enumerating the sequences, and when calling the Get you pay to create an array of the source sequence.

This explains why after calling Get in the example, I had to materialize the result, so that we can use it:

var items = new[] { "0", "1", "2" };
var p = Permutator.Get(items); // pay to create array from source elements
var result = p.Select(a => a.ToArray() // pay to create arrays for each of the permutations
    ).ToArray(); // pay to get the permutations

This allows us to pay only half of the cost, if we just enumerate half of the permutations.

Twoedged answered 13/6, 2013 at 0:5 Comment(5)
Looks fine. I'm actually wondering how to write a single query expression without declaring method to generate full permutations. However, all the answers are still some steps far.Belfry
I see... I'll try another non recursive approach to this problem then, since recursions requires methods.Twoedged
It's done... but I had to use Enumerable class methods to create the solution. I hope it is what you are looking for. I don't think there are other ways.Twoedged
I'd say that, this is the closest one. Well done, thank you. I'll award the bounty after the duration of promotion.Belfry
It is funny to me how easy (or at least short) it is to generate all combinations in LINQ, but permutations is so much more difficult.Egocentrism
M
2

Old as I am, I never like recursion when it's not necessary.

Since you need to iterate the source anyways, I simply store it in an array. I keep track of a 'swappings' array, that tells the program what to swap with what; the index is the source position of the swap, the index the destination position.

You can also do sub-permutations if you like.

If you really feel comfortable with lots of pain, I guess you can also change the swapping to a complex Exchange IEnumerable; I guess that's a good way to add a lot of complexity while at the same time making everything slower. :-)

I also reuse the array; it seems foolish to re-iterate everything every time, which saves you quite a bit of time copying data and doing recursion, when 'src' is large. It's quite efficient.

public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> src)
{
    T[] array = src.ToArray();

    // Initialize
    int[] swappings = new int[array.Length];
    for (int j = 0; j < array.Length; ++j)
    {
        swappings[j] = j;
    }

    yield return array;

    int i = swappings.Length - 2;
    while (i >= 0)
    {
        int prev = swappings[i];
        Swap(array, i, prev);
        int next = prev + 1;
        swappings[i] = next;
        Swap(array, i, next);

        yield return array;

        // Prepare next
        i = swappings.Length - 1;
        while (i >= 0 && swappings[i] == array.Length - 1)
        {
            // Undo the swap represented by permSwappings[i]
            Swap(array, i, swappings[i]);
            swappings[i] = i;
            i--;
        }
    }
}

private static void Swap<T>(T[] array, int i, int next)
{
    var tmp = array[i];
    array[i] = array[next];
    array[next] = tmp;
}
Maharajah answered 12/6, 2013 at 10:28 Comment(6)
I think I understand your point, unfortunately you don't get mine for this time ..Belfry
@KenKin Oh well, I suppose. And there's the stubbornness which automatically makes me prefer writing efficient code, sorry :-)Maharajah
I totally agree with you, this is more efficient. But my code in fact originally as similar as your. What I'm trying to do is make it more close to functional language though ..Belfry
@StefandeBruijn Keep in mind that since we're dealilng with permutations the source sequence will never be large. A sequence of even twenty items would take a long time to permute to the the time complexity of the inherent algorithm.Phidippides
@Phidippides I know; one of the reasons that I wanted an algorithm without recursion (and without creating new objects every time) is because of the combinatoric explosion.Maharajah
One way I've ever tried a non-recursive version of code to generate permutations, is to iterate up to the factorial number, as Mr. Angelo's answer we've got here. As far as I can memorize it's done in C long time ago, and which is even harder for me to translate to query syntax with my bad practice of allocation.Belfry

© 2022 - 2024 — McMap. All rights reserved.