C# VriliancyDigits Random Numbers In Random Order

Recently, I was asked to write a script which would allow a user of an application to draw numbers and display them randomly. The poster wanted to be able to draw one number at random, and they also wanted something that would be able to store these values like an array. Firstly an array is not the proper type to use here because working with array values often forces you to work with an index within the array. And this can make accessing the data in a slick way rather awkward. More so, its just not the right collection type to use for this type of program.

The options I would recommend are to use a Stack<T> where ‘T’ is the type of object being stored in the Stack. Since the OP wants to work with lotto numbers, then the type T will be integers. The other is a Queue<T> and that is what I choose for this library. The similarities between a Stack<T> and a Queue<T> are very similar in functionality, and they are best used when you want a temporary storage mechanism for holding lots of data which you plan to gradually offload and dispose of.

So as you can read so far, this would not have been a viable option for the OP since they wanted to use arrays. This would have required them to recreate or copy an array without one item, one item being the number they wanted to use, but this is a terrible waste of resources and an impractical use of a connection item of this type, since Arrays don’t let you kick one element out and resize itself. But a Queue<T> works similarly. It’s worth noting:

Stacks and queues are useful when you need temporary storage for information; that is, when you might want to discard an element after retrieving its value. Use Queue<T> if you need to access the information in the same order that it is stored in the collection. Use System.Collections.Generic.Stack<T> if you need to access the information in reverse order.

Microsoft


To point out one difference between the two: Stack<T> and Queue<T>.
A Queue<T> will offload items in the order that it was entered.
So that the first integers in, will be the first to be ousted when you call the Dequeue method on your Queue<T>.

Use Stack if you need to access the information in reverse order.

Microsoft

Meaning, that the last item entered into the collection will be the first item out of the collection when you call the Pop method on your Stack<T>.

The reasoning behind why I explained this important difference is so you can change out the Queue<T> for a Stack<T> if you want your numbers to come out in the reverse order they were added. And by following the linked documentation, you can make this change really easy.

Now on to the code…

The library only has one public member :

 public Queue<int> NumberQueue = new();

And one protected method which is the one that does all the work. That’s the FillQueue method and it takes one parameter which is a
Tuple<int, int>. The tuple may be one parameter, but don’t be fooled here. The Tuple has an overload, and we need to pass this Tuple parameter called range a starting integer, for the minimum number to begin generating numbers, and the ending integer which is the ending number which is the maximum number to be used during the random number generation sequence. This method is protected since it is actually called by a task called CreateNumbers. Here is a glance at the working methods code :

 protected Queue<int> FillQueue(Tuple<int, int> range)
 {
 Random random = new();
 while (NumberQueue.Count != range.Item2)
 {
 using IEnumerator<int> number = Enumerable.Range(range.Item1, range.Item2).OrderBy(_ => random.Next()).GetEnumerator();
 while (number.MoveNext())
 {
 if (!NumberQueue.Contains(number.Current))
 {
 NumberQueue.Enqueue(number.Current);
 }
 }
 }
 return NumberQueue;
 }

And that is called by this task which takes the exact same Tuple parameters as mentioned above :

 public Queue<int> CreateNumbers(Tuple<int, int> range)
 {
 Task<Queue<int>> result = Task.Run(() => FillQueue(range));
 return result.Result;
 }

This is how we create the task and share the parameters passed to this task, and these parameters also gets shared with the FillQueue method :

 Task<Queue<int>> result = Task.Run(() => FillQueue(range));

Inside the working method, all we are doing is creating and instantiating a new Random Generator and saying while the NumberQueue does not equal the max value of Item2 which is the second parameter of our Tuple.

Next we use an Enumerator on the Enumerable.Range of the Tuple’s first and last values. Tuple values are accessed ie Item1, Item2 etc… depending on how many overloads you are using.

 using IEnumerator<int> number = Enumerable.Range(range.Item1, range.Item2).OrderBy(_ => random.Next()).GetEnumerator();
 while (number.MoveNext())

We then call on Linq here to use OrderBy which sorts our elements in a random order while using _ to discard the values as each random next is enumerated. While number.MoveNext moves to the next Enumerable item. And to be doubly safe we don’t end up with duplicate numbers, I added this check, to ensure the NumberQueue does not contain the current number. The main reason I added this check was also because I am currently expanding this DLL to do a bit more than what it does. :

 if (!NumberQueue.Contains(number.Current))
 {
 NumberQueue.Enqueue(number.Current);
 } 

As the enumeration cycle completes, and the while condition was met, we can now return our result to the calling task :

 return NumberQueue;

Which then brings us full circle back to our CreateNumbers method where our task first started, and this is where our Queue<T> result is returned to you as a Queue<int> :

 return result.Result;

The alternative method to this DLL is to write out your own Fisher-Yates type algorithm. First introduced by Richard Durstenfeld in 1964 and this algorithm was later popularised by Donald E. Knuth in his book The Art of Computer Programming which I highly recommend reading. Knuth is one of my all time favourite computer scientists and programmers. Anyway, the full code for this DLL is below, and I hope this tutorial has helped you to build your own lotto rolling system, or whatever you may want to use it in.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace VriliancyDigits
{
 public class Vriliancy_Digits
 {
 public Queue<int> NumberQueue = new();
 public Queue<int> CreateNumbers(Tuple<int, int> range)
 {
 Task<Queue<int>> result = Task.Run(() => FillQueue(range));
 return result.Result;
 }
 protected Queue<int> FillQueue(Tuple<int, int> range)
 {
 Random random = new();
 while (NumberQueue.Count != range.Item2)
 {
 using IEnumerator<int> number = Enumerable.Range(range.Item1, range.Item2).OrderBy(_ => random.Next()).GetEnumerator();
 while (number.MoveNext())
 {
 if (!NumberQueue.Contains(number.Current))
 {
 NumberQueue.Enqueue(number.Current);
 }
 }
 }
 return NumberQueue;
 }
 }
}