I like this 6 line solution a lot and am trying to replicate it in C#. Basically, it permutes the elements of an array:
def permute(xs, pre=[]):
if len(xs) == 0:
yield pre
for i, x in enumerate(xs):
for y in permute(xs[:i] + xs[i+1:], pre + [x]):
yield y
-
Not entirely to the point I must admit after some comments, but the code below can be used to generate a random permutation of a finite sequence. It's a variation of the Fisher-Yates shuffle algorithm. The example uses a sequence of
int's but you can use anyEnumerable<T>of course.var ints = Enumerable.Range(0, 51); var shuffledInts = ints.OrderBy(a => Guid.NewGuid());You order by a random value (in this case a
Guid) which essentially permutates your list. Whether NewGuid is a good source of randomness is debatable, but it's an elegant and compact solution (albeit for another problem then the question was actually about).Taken from Jeff Atwood (Coding Horror).
Adam Robinson : How does this perform any permutations...?Ronald Wildenberg : If you order by a random value (a Guid in this case), you essentially permutate your list.Samuel : @rwwilden: No you don't. http://en.wikipedia.org/wiki/PermutationLucas : @rwwilden: permute != shuffleJoel Coehoorn : shuffles are like a special case of a permutation, in that you can use permutations to shuffle accurately (if more slowly than with a specialized algorithm). Even the wikipedia article above mentions Fisher-Yates as "based on the same principle".Joel Coehoorn : Even so, ordering by a new guid is not the best idea, and while all shuffles are permutations, not all permutations are good shuffles.Ronald Wildenberg : After reading up on permutations I see that I have answered this one incorrect, sorry for that. However, the method described is a good way for generating random permutations of a finite set, although not as fast as possible (and the source of randomness is debatable). Updated my answer. -
C# has a yield keyword that I imagine works pretty much the same as what your python code is doing, so it shouldn't be too hard to get a mostly direct translation.
However this is a recursive solution, so for all it's brevity it's sub-optimal. I don't personally understand all the math involved, but for good efficient mathematical permutations you want to use factoradics. This article should help:
http://msdn.microsoft.com/en-us/library/aa302371.aspx[Update]: The other answer brings up a good point: if you're just using permutations to do a shuffle there are still better options available. Specifically, the Knuth/Fisher-Yates shuffle.
-
Well, it probably isn't how I'd write it, but:
static IEnumerable<T[]> Permute<T>(this T[] xs, params T[] pre) { if (xs.Length == 0) yield return pre; for (int i = 0; i < xs.Length; i++) { foreach (T[] y in Permute(xs.Take(i).Union(xs.Skip(i+1)).ToArray(), pre.Union(new[] { xs[i] }).ToArray())) { yield return y; } } }
Re your comment; I'm not entirely clear on the question; if you mean "why is this useful?" - among other things, there are a range of brute-force scenarios where you would want to try different permutations - for example, for small ordering problems like travelling sales person (that aren't big enough to warrant a more sophisticated solution), you might want to check whether it is best to go {base,A,B,C,base}, {base,A,C,B,base},{base,B,A,C,base}, etc.
If you mean "how would I use this method?" - untested, but something like:
int[] values = {1,2,3}; foreach(int[] perm in values.Permute()) { WriteArray(perm); } void WriteArray<T>(T[] values) { StringBuilder sb = new StringBuilder(); foreach(T value in values) { sb.Append(value).Append(", "); } Console.WriteLine(sb); }If you mean "how does it work?" - iterator blocks (
yield return) are a complex subject in themselves - Jon has a free chapter (6) in his book, though. The rest of the code is very much like your original question - just using LINQ to provide the moral equivalent of+(for arrays).Marc Gravell : Well, it would involve less LINQ, and probably two methods (one for the public caller, one separate for recursion), and a re-used buffer. Or I'd look at the links in the other post ;-p -
While you cannot port it while maintaining the brevity, you can get pretty close.
public static class IEnumerableExtensions { public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> source) { if (source == null) throw new ArgumentNullException("source"); return PermutationsImpl(source, new T[0]); } private static IEnumerable<IEnumerable<T>> PermutationsImpl<T>(IEnumerable<T> source, IEnumerable<T> prefix) { if (source.Count() == 0) yield return prefix; foreach (var x in source) foreach (var permutation in PermutationsImpl(source.Except(new T[] { x }), prefix.Union(new T[] { x })))) yield return permutation; } }Marc Gravell : The approach with Equals makes it hazardous for anything with either duplicates or nulls.Samuel : Regular permutations don't take duplicates (should be checked), but you are right about nulls.
0 comments:
Post a Comment