CodeKicks.com
Focus on Microsoft Technologies - Tutorials, Articles, Code Samples.

Wednesday, October 11, 2006

C# .NET Randomize - LINQ "Randomize Sequence" Extension Method

C# .NET Randomize - LINQ "Randomize Sequence" Extension Method

Requirement

  1. An extension method for IEnumerable that randomizes an input sequence of IEnumerable
  2. Allows the randomization to be reproducible by passing in a consistent "seed" or random by basing the "seed" from the system clock
  3. Be consistent with existing LINQ over Object calls in error handling (ie. empty source)

My envisioned syntax is:

   1:  int[] numbers= new int[] {0,1,2,3,4,5,6,7,8,9,10};
2: var testValues = numbers.Randomize();

 The Plan

The obvious place to start was the System.Random .NET framework class library. This will work for me as it optionally takes a "seed." The Random class is actually a pseudo-random generator, that means it starts generating random numbers based on a given "seed." The idea is you generate this from the system clock so that it appears to be random. Problem is, if you are generating numbers within a loop, even the tick-count of the system clock difference may not be enough to offer a totally "Random" experience! So, it allows you to pass in any seed you want to support customizing the Random'ness. I'm interested in using the "seed" to support consistency in generating random sequences, because this might be useful when generating test vectors.

I looked at the sample sequence.cs source from the May LINQ Preview and copied the Reverse extension method to a new C# LINQ Class library. I'll start there.

public static IEnumerable<T> Reverse<T>
(this IEnumerable<T> source) {
if (source == null) throw Error.ArgumentNull
("source");
return ReverseIterator<T>(source);
}
static IEnumerable<T> ReverseIterator<T>
(IEnumerable<T> source) {
Buffer<T> buffer = new Buffer<T>(source);
for (int i = buffer.count - 1; i >= 0; i--)
yield return buffer.items[i];
}

 It works by taking a snapshot copy of the source sequence then returning them through the "yield return" in reverse order. The "yield" operator was new to C# 2.0, and it assists in authoring iterators. In simple terms, every time a yield return is reached, .NET suspends execution and the next time a value is required from the iterator (IEnumerable), it restarts from that point remembering all of its state. So, in the Reverse case, it works backwards through the index's returning from count -1 to zero, hence: the sequence generated from Reverse will be from the last element to the first element.

For my Randomize method, I'll generate random index values and yield return those elements. I don't want to repeat any, so once I use an element I can't return that one again. This would mean keeping a list of index positions I have used, or better still removing them from a snapshot copy each time they are returned, then next iteration, generate a random index value from those remaining.

One concern was the use of a custom struct "Buffer" as seen in line 7 in .NET's Reverse implementation....I think it masks the differences in collection types that implement IEnumerable but don't support index access, but I'm not certain. I'll have to look into this in more detail.

C# .NET Randomize - LINQ "Randomize Sequence" Extension Method

Post a Comment