EDIT 1:
Single line solution without recursion
I repeated the main method (from the previous solution below in this answer), so now it is no longer recursive. Now it’s easy to make a one-line solution.
I had to use Enumerable methods and extension methods to do this. Without them, I think that it is impossible to do.
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 // eg length = 3: mods = [ p%2, p%3 ] // eg 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 decision
The result is this monster:
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 decision
I 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]); } }
Usage example:
var items = new[] { "0", "1", "2" }; var p = Permutator.Get(items); var result = p.Select(a=>a.ToArray()).ToArray();
How it works
The kernel is the CreateIndices method. It creates a sequence containing the indices of the source elements for each permutation.
Best explained 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 ]
This is a recursive method based only on enumerated extensions and LINQ syntax queries.
The idea of recursion is that each level builds on the basis of the previous one.
CreateIndices(n) adds element n-1 to the permutations returned by CreateIndices(n-1) at all available positions.
CreateIndices(0) recursion root returns an empty set of permutations.
Explanation in stages: CreateIndices(3)
1. Let's start by creating the result of CreateIndices(0) :
2. Then the result of CreateIndices(1) :
- add element
0 (n-1) to each of the previous substitutions, at position 0
[0]
3. Then the result of CreateIndices(2)
- add element
1 (n-1) to each of the previous permutations, at position 0
[ten] - add element
1 (n-1) to each of the previous permutations, at position 1
[0, 1]
4. Then the result of CreateIndices(3)
- add element
2 (n-1) to each of the previous substitutions, at position 0
[2, 1, 0]
[2, 0, 1] - add element
2 (n-1) to each of the previous substitutions, at position 1
[1, 2, 0]
[0, 2, 1] - add element
2 (n-1) to each of the previous substitutions, at position 2
[1, 0, 2]
[0, 1, 2]
What will happen next
Now that we have indexes for each of the permutations, we can use them to construct real permutations of values. This is what the general Get method does.
Also note that the Get method is the only one that materializes the original sequence into an array. CreateIndices is just an enumerator, without any real objects ... so you only list costs when listing sequences and when you call Get , which you pay to create an array of the original sequence.
This explains why, after calling Get in this example, I had to materialize the result so that we could use it:
var items = new[] { "0", "1", "2" }; var p = Permutator.Get(items);
This allows us to pay only half the cost if we simply list half the permutations.