I have a program which works with arrays and at some point I have to check if a lsit of values are inside an array, the function that does this takes about 70% of the program CP
Instead of shuffle, you can generate the possible values in a list and take values at random positions. That way you can start using the items even before the whole row is generated:
IEnumerable<int> RandomRange(int count) {
var random = new Random();
var list = new int[count];
for (int i = 0; i < count; i++) list[i] = i;
while (count > 0) {
var i = random.Next(count);
yield return list[i] ;
list[i] = list[--count];
}
}
sample use:
foreach(var item in RandomRange(10))
// ...
You're wasting a lot of work by doing a linear scan for every number throughout the array. What you should instead do is walk the array once and start observing the last number you've seen. If you see any gaps, return false. If you reach the end of the array, return true. This is O(N) instead of O(N^2) like you have it now.
Like so:
public static bool AreAllNumbersGenerated(int[] row)
{
var last = 0;
return row.Aggregate(true, (l, r) => l && r == last++);
}
You can make some additional optimizations that won't affect your big-O complexity, e.g. using a proper loop and early-exiting.
EDIT: I'm assuming here that you're expecting the items to occur sequentially. If this isn't the case, you can easily convert the array to a structure with ~O(1) lookups in O(N), then do the check in O(N) as well. For large input, this is still better than your O(N^2) approach above
Like so:
public static bool AreAllNumbersGenerated2(int[] row)
{
var available = row.ToDictionary(x => x);
return Enumerable.Range(0, row.Length).All(i => available.ContainsKey(i));
}
EDIT: From what I'm seeing in the comments it looks like the goal of this code is to populate an array with a contiguous sequence of numbers in a random order. I didn't realize that was the goal, and if that is the case, shuffling is certainly better than reattempting to generate over and over. But what is even better than generating the array then shuffling (again, for sufficiently large input) is to simply generate the array by assigning each number to a randomly sampled index without substitution. Not as easy to LINQ out, but you can figure it out.
I found a method, thanks to @sous2817 comment, that works perfectly for me.
public static int[] GetRandomNumbers(int count, Random RNG)
{
HashSet<int> randomNumbers = new HashSet<int>();
for (int i = 0; i < count; i++)
while (!randomNumbers.Add(RNG.Next(count))) ;
return randomNumbers.ToArray();
}
This works ~10 times faster then what I was doing and works nicely with the rest of my program.I needed the code to be "linear" so other solutions mess my program. Anyway, thanks to everyone that helped :)
As others in the comments have pointed out, the most efficient way to do this is to generate an array with the values you want and shuffle it:
public static void Shuffle<T>(T[] arr, Random r)
{
for (int i = arr.Length - 1; i > 0; --i)
{
int j = r.Next(i + 1);
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
public static int[] Generate(int length, Random r)
{
var arr = new int[length];
for(int i = 0; i < length; ++i)
{
arr[i] = i;
}
Shuffle(arr, r);
return arr;
}
then
var arr = Generate(10, new Random());
foreach (int i in arr) { Console.WriteLine(i); }