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 CPU time, so I was wondering if there's a way to do this more efficiently.
These are my functions:
private static int[] GenerateRow(int length, Random RNG)
{
int[] row = InitalizeRow(length);
int index = 0;
while (!AreAllNumbersGenerated(row))
{
int value = RNG.Next(0, length);
if (!RowContains(row, value))
{
row[index] = value;
index++;
}
}
return row;
}
private static bool AreAllNumbersGenerated(int[] row)
{
for (int i = 0; i < row.Length; i++)
if(!RowContains(row, i))
return false;
return true;
}
private static bool RowContains(int[] row, int value)
{
for (int i = 0; i < row.Length; i++)
if (row[i] == value)
return true;
return false;
}
All the work is by AreAllNumbersGenerated(), then by RowContains() and finally by this line:
if (row[i] == value)
That this functions take most of the time is normal, since they do heavy work, but I wanted to know if there's a better way to do this.
EDIT:
private static int[] InitalizeRow(int length)
{
int[] row = new int[length];
for (int i = 0; i < length; i++)
row[i] = -1;
return row;
}
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.
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); }
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 :)
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))
// ...
Related
I am a complete beginner in programming. Trying to make sorting a choice. Everything seems to be ok. Only there is one caveat. Only numbers up to 24 index are filled in the new array. I can’t understand what the problem is.
int[] Fillin(int[] mass)
{
Random r = new Random();
for(int i = 0; i < mass.Length; i++)
{
mass[i] = r.Next(1, 101);
}
return mass;
}
int SearchSmall(int[] mass)
{
int smallest = mass[0];
int small_index = 0;
for(int i = 1; i < mass.Length; i++)
{
if (mass[i] < smallest)
{
smallest = mass[i];
small_index = i;
}
}
return small_index;
}
int[] Remove(int[] massiv,int remind)
{
List<int> tmp = new List<int>(massiv);
tmp.RemoveAt(remind);
massiv = tmp.ToArray();
return massiv;
}
public int[] SortMass(int[] mass)
{
mass = Fillin(mass);
Print(mass);
Console.WriteLine("________________________________");
int[] newmass = new int[mass.Length];
int small;
for(int i = 0; i < mass.Length; i++)
{
small = SearchSmall(mass);
newmass[i] = mass[small];
mass = Remove(mass, small);
}
return newmass;
}
I think your main issue is that when you remove an element in the Remove function, the main loop in for (int i = 0; i < mass.Length; i++) will not check all elements o the initial array. A simple (and ugly) way to fix that would be not to remove the elements but to assign a very high value
public static int[] Remove(int[] massiv, int remind)
{
massiv[remind] = 999999;
return massiv;
}
Or as Legacy suggested simply modify the mass.length for newmass.lengh in the main loop.
As some others have mentioned this is not the best way to order an array, but it is an interesting exercise.
Basically i'm creating a method that randomises a number then either returns 0 or the random value. (Depending upon if an array already has that value stored in it). There's currently 2 problems. Not all paths return a value. And the last part of my loop (i++) is unreachable. Any help would be great.
Also heres the array that i created for the randomiser:
int[] arr = new int[4];
Heres the method:
public int UniqueRandomiser()
{
Random rnd = new Random();
int j = rnd.Next(1, 4);
for (int i = 0; i < 4; i++)
{
if (rand1[i] == j)
{
return 0;
}
else
{
return j;
}
}
}
You can use .Contains on the array to check if the number exists:
return randomArray.Contains(randomNumber) ? 0 : randomNumber;
EDIT: If we require to check only the first 4 numbers:
return randomArray.Take(4).Contains(randomNumber) ? 0 : randomNumber;
I suggest using Linq and make the code being readable:
// Do not recreate Random (or you're going to have badly skewed values);
// the simplest, but not thread safe
private static Random rnd = new Random();
public int UniqueRandomiser() {
//TODO: what does 4 stand for? Get rid of magic numbers...
int v = rnd.Next(1, 4);
//TODO: another magic number 4; is it connected with the previous one?
// if any of first 4 items of rand1 is equal to v return 0
return rand1.Take(4).Any(item => item == v) ? 0 : v;
}
The "unreachable" for the i++ is because the first time through the loop, it returns on every code path - so the i++ will indeed never be reached. The other error is because there is a hypothetical (but actually impossible) code path that doesn't return a value - if the loop ran to completion (it can't) without returning a value, then there is no return value from the method.
I expect you meant:
public int UniqueRandomiser()
{
Random rnd = new Random();
int j = rnd.Next(1, 4);
for (int i = 0; i < 4; i++)
{
if (rand1[i] == j)
{
return 0;
}
}
return j;
}
However.. this still looks like a very bad way to do ... whatever it is you're trying to do.
I think this is what you need.
public int UniqueRandomiser()
{
Random rnd = new Random();
int j = rnd.Next(1, 4);
for (int i = 0; i < 4; i++)
{
if (rand1[i] == j)
{
return 0;
}
}
return j;
}
It doesn't really make sense though. If the number returned must be unique, it's not a truly random number.
Trying to build a method that will find the sum of all the values within the 2D array. I'm very new to programming and can't find a good starting point on trying to figure out how its done. Here is what I have so far (forgive me, I'm usually an english/history guy, logic isn't my forte...)
class Program
{
static void Main(string[] args)
{
int[,] myArray = new int[5,6];
FillArray(myArray);
LargestValue(myArray);
}
//Fills the array with random values 1-15
public static void FillArray(int[,] array)
{
Random rnd = new Random();
int[,] tempArray = new int[,] { };
for (int i = 0; i < tempArray.GetLength(0); i++)
{
for (int j = 0; j < tempArray.GetLength(1); j++)
{
tempArray[i, j] = rnd.Next(1, 16);
}
}
}
//finds the largest value in the array (using an IEnumerator someone
//showed me, but I'm a little fuzzy on how it works...)
public static void LargestValue(int[,] array)
{
FillArray(array);
IEnumerable<int> allValues = array.Cast<int>();
int max = allValues.Max();
Console.WriteLine("Max value: " + max);
}
//finds the sum of all values
public int SumArray(int[,] array)
{
FillArray(array);
}
}
I guess I could try to find the sum of each row or column and add them up with a for loop? Add them up and return an int? If anyone could shed any insight, it would be greatly appreciated, thanks!
Firstly, you don't need to call FillArray in the beginning of each method, you have already populated the array in the main method, you are passing a populated array to these other methods.
A loop similar to what you use to populate the array is the easiest to understand:
//finds the sum of all values
public int SumArray(int[,] array)
{
int total = 0;
// Iterate through the first dimension of the array
for (int i = 0; i < array.GetLength(0); i++)
{
// Iterate through the second dimension
for (int j = 0; j < array.GetLength(1); j++)
{
// Add the value at this location to the total
// (+= is shorthand for saying total = total + <something>)
total += array[i, j];
}
}
return total;
}
To sum an array if you know the length is easy
As a bonus code included to get the highest valeu too.
You could easily expand this to get other kinds of statistical code.
I asume below Xlength and Ylength are integers too, and known by you.
You could also replace them by a number in the code.
int total = 0;
int max=0;
int t=0; // temp valeu
For (int x=0;x<Xlength;x++)
{
for (int y=0;y<Ylength;y++)
{
t = yourArray[x,y];
total =total +t;
if(t>max){max=t;} // an if on a single line
}
}
here is a link with an MSDN sample on how to retrieve unknown array lengths.
and there is a nice site to have around when you start in c#
google ".net perls"
This is a function that makes an array and fills it from LoLim till HiLim and then shuffles the order:
static Random random = new Random(); //these two statics are to make a random number, i need the first static to be outside the function so it won't keep making the same random number
static int RandomNum(int LoLim, int HiLim, int index)
{
var nums = Enumerable.Range(LoLim,HiLim).ToArray(); //these next lines make an array from LoLim to HiLim and then shuffle it
for (int i = 0; i < nums.Length; i++)
{
int randomIndex = random.Next(nums.Length);
int temp = nums[randomIndex];
nums[randomIndex] = nums[i];
nums[i] = temp;
}
return nums[index];
Then I have this function that makes a 2 dimensional array and then prints it. It uses RandomNum but not in the way I want to, but I don't know how to make it work.
static Array Matrix(int Rows, int Columns) //this is a function to initiate and print the array
{
int[,] LotteryArray = new int[Rows, Columns];
for (int i = 0; i < LotteryArray.GetLength(0); i++) //this is a series of loops to initiate and print the array
{
for (int j = 0; j < LotteryArray.GetLength(1); j++)
{
LotteryArray[i, j] = RandomNum(1,46,j); //the numbers are supposed to be between 1 and 45, so i tell it to do it until 46 because the upper limit is exclusive
Console.Write("{0,3},", LotteryArray[i, j]); //the {0,3} the 3 is three spaces after the first variable
}
Console.WriteLine();
}
return LotteryArray;
Basically I want it to call to RandomNum every "row" so it could shuffle the array, then I want it to pull out nums[index] and print it out. The reason I want this is so I can have non repeating random numbers in every row. For example: I don't want a row to be "21, 4, 21, 5, 40, 30".
There are various problems with your code, even if it worked. The "logical" problems are:
You are using a biased shuffling algorithm. You should use Fisher-Yates to do correct shuffling (see http://blog.codinghorror.com/the-danger-of-naivete/)
If you really want to do a "good" shuffling, you shouldn't use the Random number generator, and instead use the RNGCryptoServiceProvider (see http://www.cigital.com/papers/download/developer_gambling.php, but note that this is a much lesser problem than the other other one... we can survive with "a little less random" shuffles, if are the generated shuffles are all equiprobable)
You are using wrongly the arguments of Enumerable.Range()
Your coding problem is different: you have to save the shuffled row somewhere, and then take the first column values.
Here I'm using a class to encapsulate a row with shuffling
public class ShuffledRow
{
public static readonly Random Random = new Random();
public readonly int[] Row;
/// <summary>
/// Generates and shuffles some numbers
/// from min to max-1
/// </summary>
/// <param name="min"></param>
/// <param name="max">Max is excluded</param>
public ShuffledRow(int min, int max)
{
int count = max - min;
Row = Enumerable.Range(min, count).ToArray();
Shuffle(Row);
}
private static void Shuffle<T>(T[] array)
{
// Fisher-Yates correct shuffling
for (int i = array.Length - 1; i > 0; i--)
{
int j = Random.Next(i + 1);
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
Another problem: don't ever use multidimensional arrays in C# unless you know what you are doing. Sadly they are a bastard-and-forgotten child of .NET. Use jagged arrays (arrays of arrays).
public static int[][] Matrix(int rows, int columns)
{
int[][] lottery = new int[rows][];
for (int i = 0; i < lottery.Length; i++)
{
ShuffledRow sr = new ShuffledRow(1, 46);
lottery[i] = sr.Row;
Array.Resize(ref lottery[i], columns);
Console.WriteLine(
string.Join(",", lottery[i].Select(
x => string.Format("{0,3}", x))));
}
return lottery;
}
public static int[][] Matrix(int rows, int columns)
{
int[][] lottery = new int[rows][];
for (int i = 0; i < lottery.Length; i++)
{
ShuffledRow sr = new ShuffledRow(1, 46);
lottery[i] = new int[columns];
for (int j = 0; j < columns; j++)
{
lottery[i][j] = sr.Row[j];
Console.Write("{0,3},", lottery[i][j]);
}
Console.WriteLine();
}
return lottery;
}
I have prepared two versions of the Matrix function, one that is more similar to the one you are using, one that is more LINQ and "advanced".
to generate random numbers really do not use random function, use this:
private static RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
// Main method.
public static int[] generateTrueRandomOK()
{
const int totalRolls = 2500;
int[] results = new int[50]; //Number of random ints that you need
// Roll the dice 25000 times and display
// the results to the console.
for (int x = 0; x < totalRolls; x++)
{
byte roll = RollDice((byte)results.Length);
results[roll - 1]++;
}
return results;
}
// This method simulates a roll of the dice. The input parameter is the
// number of sides of the dice.
public static byte RollDice(byte numberSides)
{
if (numberSides <= 0)
throw new ArgumentOutOfRangeException("numberSides");
// Create a byte array to hold the random value.
byte[] randomNumber = new byte[1];
do
{
// Fill the array with a random value.
rngCsp.GetBytes(randomNumber);
}
while (!IsFairRoll(randomNumber[0], numberSides));
// Return the random number mod the number
// of sides. The possible values are zero-
// based, so we add one.
return (byte)((randomNumber[0] % numberSides) + 1);
}
private static bool IsFairRoll(byte roll, byte numSides)
{
// There are MaxValue / numSides full sets of numbers that can come up
// in a single byte. For instance, if we have a 6 sided die, there are
// 42 full sets of 1-6 that come up. The 43rd set is incomplete.
int fullSetsOfValues = Byte.MaxValue / numSides;
// If the roll is within this range of fair values, then we let it continue.
// In the 6 sided die case, a roll between 0 and 251 is allowed. (We use
// < rather than <= since the = portion allows through an extra 0 value).
// 252 through 255 would provide an extra 0, 1, 2, 3 so they are not fair
// to use.
return roll < numSides * fullSetsOfValues;
}
You could use jagged arrays:
Random random = new Random();
int[] RandomNum(int LoLim, int HiLim)
{
var nums = Enumerable.Range(LoLim,HiLim).ToArray();
for (int i = 0; i < nums.Length; i++)
{
int randomIndex = random.Next(nums.Length);
int temp = nums[randomIndex];
nums[randomIndex] = nums[i];
nums[i] = temp;
}
return nums;
}
int[][] Matrix(int Rows, int Columns)
{
int[][] LotteryArray = new int[Rows][];
for (int i = 0; i < LotteryArray.Length; i++)
{
LotteryArray[i] = RandomNum(1,46);
for (int j = 0; j < LotteryArray[i].Length; j++)
{
Console.Write("{0,3},", LotteryArray[i][j]);
}
Console.WriteLine();
}
return LotteryArray;
}
Well im trying to find a maxValue in a array and its harder then i feel it should. Normally this code here works. If i declare an array and manually put in numbers for the array it find the max value fine. But when i put in a method to make an array with random numbers it breaks and returns the last value set as the maximum one.
static int MaxArray(int[] Array)
{
int maxVal = Array[0];
for(int i = 0; i < Array.Length; i++)
{
if(Array[i] > maxVal)
{
maxVal = Array[i];
}
}
return maxVal;
}
static void Main(string[] args)
{
Random r = new Random();
int[] myArray = new int[5];
for(int i = 0; i < myArray.Length; i++)
{
int rNumb = r.Next(0, 100);
for (int v = 0; v < myArray.Length; v++)
{
myArray[v] = rNumb;
}
Console.WriteLine(myArray[i]);
}
Console.WriteLine("Press entere to find the max value");
Console.ReadKey();
Console.Write(MaxArray(myArray));
Console.Read();
}
The inner for-loop in your Main method is useless. It fills the whole array with the current random number (so at the end the whole array will contain the last random number repeated).
The correct code is the following:
for(int i = 0; i < myArray.Length; i++)
{
int rNumb = r.Next(0, 100);
myArray[i] = rNumb;
Console.WriteLine(myArray[i]);
}
You were overwriting the values with your second for loop,
for (int v = 0; v < myArray.Length; v++)
{
myArray[v] = rNumb;
}
will write the current random number at each index of your array. The last random number will overwrite previous ones, so it will be declared as max since it's the only available number in your array.
Try this instead :
static void Main(string[] args)
{
Random r = new Random();
int[] myArray = new int[5];
for (int i = 0; i < myArray.Length; i++)
{
myArray[i] = r.Next(0, 100);
Console.WriteLine(myArray[i]);
}
Console.WriteLine("Press entere to find the max value");
Console.Write(MaxArray(myArray));
Console.Read();
}
But honestly that MaxArray method is useless, don't reinvent the wheel, use Max from LINQ instead :
Console.Write(myArray.Max());
This is easily achieved using Linq.
using System.Linq;
private static Random _random = new Random();
public static int[] GenerateRandomArray(int arrayLength)
{
return Enumerable.Range(0, arrayLength).Select(i => _random.Next(0, 100)).ToArray();
}
public static int FindMaxValue(int[] array)
{
return array.Max();
}
It's because your array initialization is completely useless. This is what you're looking for:
for(int i = 0; i < myArray.Length; i++)
{
myArray[i] = r.Next(0, 100);
Console.WriteLine(myArray[i]);
}
At the end of it, the array will look like this (with your code):
{n, n, n, n, n}
where n is the last random number.
You don't need this loop. You should change this
for (int v = 0; v < myArray.Length; v++)
{
myArray[v] = rNumb;
}
with
myArray[i] = rNumb;
By writing this loop you are overwriting all the values in the array with the last value.
The reason you get the last value set as the maximum every time is because you set every element in the array to the last random number generated on the last iteration of the outer for loop.
Your Console.WriteLine(myArray[i]); is giving you a false impression of what the values of the array you pass into MaxValue() actually are!