Is it possible to generate a random number between 2 doubles?
Example:
public double GetRandomeNumber(double minimum, double maximum)
{
return Random.NextDouble(minimum, maximum)
}
Then I call it with the following:
double result = GetRandomNumber(1.23, 5.34);
Any thoughts would be appreciated.
Yes.
Random.NextDouble returns a double between 0 and 1. You then multiply that by the range you need to go into (difference between maximum and minimum) and then add that to the base (minimum).
public double GetRandomNumber(double minimum, double maximum)
{
Random random = new Random();
return random.NextDouble() * (maximum - minimum) + minimum;
}
Real code should have random be a static member. This will save the cost of creating the random number generator, and will enable you to call GetRandomNumber very frequently. Since we are initializing a new RNG with every call, if you call quick enough that the system time doesn't change between calls the RNG will get seeded with the exact same timestamp, and generate the same stream of random numbers.
Johnny5 suggested creating an extension method. Here's a more complete code example showing how you could do this:
public static class RandomExtensions
{
public static double NextDouble(
this Random random,
double minValue,
double maxValue)
{
return random.NextDouble() * (maxValue - minValue) + minValue;
}
}
Now you can call it as if it were a method on the Random class:
Random random = new Random();
double value = random.NextDouble(1.23, 5.34);
Note that you should not create lots of new Random objects in a loop because this will make it likely that you get the same value many times in a row. If you need lots of random numbers then create one instance of Random and re-use it.
Watch out: if you're generating the random inside a loop like for example for(int i = 0; i < 10; i++), do not put the new Random() declaration inside the loop.
From MSDN:
The random number generation starts from a seed value. If the same
seed is used repeatedly, the same series of numbers is generated. One
way to produce different sequences is to make the seed value
time-dependent, thereby producing a different series with each new
instance of Random. By default, the parameterless constructor of the
Random class uses the system clock to generate its seed value...
So based on this fact, do something as:
var random = new Random();
for(int d = 0; d < 7; d++)
{
// Actual BOE
boes.Add(new LogBOEViewModel()
{
LogDate = criteriaDate,
BOEActual = GetRandomDouble(random, 100, 1000),
BOEForecast = GetRandomDouble(random, 100, 1000)
});
}
double GetRandomDouble(Random random, double min, double max)
{
return min + (random.NextDouble() * (max - min));
}
Doing this way you have the guarantee you'll get different double values.
I'm a bit late to the party but I needed to implement a general solution and it turned out that none of the solutions can satisfy my needs.
The accepted solution is good for small ranges; however, maximum - minimum can be infinity for big ranges. So a corrected version can be this version:
public static double NextDoubleLinear(this Random random, double minValue, double maxValue)
{
// TODO: some validation here...
double sample = random.NextDouble();
return (maxValue * sample) + (minValue * (1d - sample));
}
This generates random numbers nicely even between double.MinValue and double.MaxValue. But this introduces another "problem", which is nicely presented in this post: if we use such big ranges the values might seem too "unnatural". For example, after generating 10,000 random doubles between 0 and double.MaxValue all of the values were between 2.9579E+304 and 1.7976E+308.
So I created also another version, which generates numbers on a logarithmic scale:
public static double NextDoubleLogarithmic(this Random random, double minValue, double maxValue)
{
// TODO: some validation here...
bool posAndNeg = minValue < 0d && maxValue > 0d;
double minAbs = Math.Min(Math.Abs(minValue), Math.Abs(maxValue));
double maxAbs = Math.Max(Math.Abs(minValue), Math.Abs(maxValue));
int sign;
if (!posAndNeg)
sign = minValue < 0d ? -1 : 1;
else
{
// if both negative and positive results are expected we select the sign based on the size of the ranges
double sample = random.NextDouble();
var rate = minAbs / maxAbs;
var absMinValue = Math.Abs(minValue);
bool isNeg = absMinValue <= maxValue ? rate / 2d > sample : rate / 2d < sample;
sign = isNeg ? -1 : 1;
// now adjusting the limits for 0..[selected range]
minAbs = 0d;
maxAbs = isNeg ? absMinValue : Math.Abs(maxValue);
}
// Possible double exponents are -1022..1023 but we don't generate too small exponents for big ranges because
// that would cause too many almost zero results, which are much smaller than the original NextDouble values.
double minExponent = minAbs == 0d ? -16d : Math.Log(minAbs, 2d);
double maxExponent = Math.Log(maxAbs, 2d);
if (minExponent == maxExponent)
return minValue;
// We decrease exponents only if the given range is already small. Even lower than -1022 is no problem, the result may be 0
if (maxExponent < minExponent)
minExponent = maxExponent - 4;
double result = sign * Math.Pow(2d, NextDoubleLinear(random, minExponent, maxExponent));
// protecting ourselves against inaccurate calculations; however, in practice result is always in range.
return result < minValue ? minValue : (result > maxValue ? maxValue : result);
}
Some tests:
Here are the sorted results of generating 10,000 random double numbers between 0 and Double.MaxValue with both strategies. The results are displayed with using logarithmic scale:
Though the linear random values seem to be wrong at first glance the statistics show that none of them are "better" than the other: even the linear strategy has an even distribution and the average difference between the values are pretty much the same with both strategies.
Playing with different ranges showed me that the linear strategy gets to be "sane" with range between 0 and ushort.MaxValue with a "reasonable" minimum value of 10.78294704
(for ulong range the minimum value was 3.03518E+15; int: 353341). These are the same results of both strategies displayed with different scales:
Edit:
Recently I made my libraries open source, feel free to see the RandomExtensions.NextDouble method with the complete validation.
The simplest approach would simply generate a random number between 0 and the difference of the two numbers. Then add the smaller of the two numbers to the result.
You could use code like this:
public double getRandomNumber(double minimum, double maximum) {
return minimum + randomizer.nextDouble() * (maximum - minimum);
}
You could do this:
public class RandomNumbers : Random
{
public RandomNumbers(int seed) : base(seed) { }
public double NextDouble(double minimum, double maximum)
{
return base.NextDouble() * (maximum - minimum) + minimum;
}
}
What if one of the values is negative? Wouldn't a better idea be:
double NextDouble(double min, double max)
{
if (min >= max)
throw new ArgumentOutOfRangeException();
return random.NextDouble() * (Math.Abs(max-min)) + min;
}
Use a static Random or the numbers tend to repeat in tight/fast loops due to the system clock seeding them.
public static class RandomNumbers
{
private static Random random = new Random();
//=-------------------------------------------------------------------
// double between min and the max number
public static double RandomDouble(int min, int max)
{
return (random.NextDouble() * (max - min)) + min;
}
//=----------------------------------
// double between 0 and the max number
public static double RandomDouble(int max)
{
return (random.NextDouble() * max);
}
//=-------------------------------------------------------------------
// int between the min and the max number
public static int RandomInt(int min, int max)
{
return random.Next(min, max + 1);
}
//=----------------------------------
// int between 0 and the max number
public static int RandomInt(int max)
{
return random.Next(max + 1);
}
//=-------------------------------------------------------------------
}
See also : https://learn.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8
About generating the same random number if you call it in a loop a nifty solution is to declare the new Random() object outside of the loop as a global variable.
Notice that you have to declare your instance of the Random class outside of the GetRandomInt function if you are going to be running this in a loop.
“Why is this?” you ask.
Well, the Random class actually generates pseudo random numbers, with the “seed” for the randomizer being the system time. If your loop is sufficiently fast, the system clock time will not appear different to the randomizer and each new instance of the Random class would start off with the same seed and give you the same pseudo random number.
Source is here : http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/
Random random = new Random();
double NextDouble(double minimum, double maximum)
{
return random.NextDouble()*random.Next(minimum,maximum);
}
If you need a random number in the range [double.MinValue; double.MaxValue]
// Because of:
double.MaxValue - double.MinValue == double.PositiveInfinity
// This will be equals to NaN or PositiveInfinity
random.NextDouble() * (double.MaxValue - double.MinValue)
Use instead:
public static class RandomExtensions
{
public static double NextDoubleInMinMaxRange(this Random random)
{
var bytes = new byte[sizeof(double)];
var value = default(double);
while (true)
{
random.NextBytes(bytes);
value = BitConverter.ToDouble(bytes, 0);
if (!double.IsNaN(value) && !double.IsInfinity(value))
return value;
}
}
}
Random rand = new Random();
rand.Next(1,5)/10; //Means between 0,1 and 0,5
Related
Context: I am building a random-number generating user interface where a user can enter values for the following:
lowerLimit: the lower limit for each randomly generated number
upperLimit: the upper limit for each randomly generated number
maxPrecision: the maximum precision each randomly generated number
Quantity: the maximum number of random number values to be generated
The question is: how can I ensure that at a given lowerLimit/upperLimit range and at a given precision, that the user does not request a greater quantity than is possible?
Example:
lowerLimit: 1
upperLimit: 1.01
maxPrecision: 3
Quantity: 50
At this precision level (3), there are 11 possible values between 1 and 1.01: 1.000, 1.001, 1.002, 1.003, 1.004, 1.005, 1.006, 1.007, 1.008, 1.009, 1.100, yet the user is asking for the top 50.
In one version of the function that returns only distinct values that match user criteria, I am using a dictionary object to store already-generated values and if the value already exists, try another random number until I have found X distinct random number values where X is the user-desired quantity. The problem is, my logic allows for a never-ending loop if the number of possible values is less than the user-entered quantity.
While I could probably employ logic to detect runaway condition, I thought it would be a nicer approach to somehow calculate the quantity of possible return values in advance to make sure it is possible. But that logic is eluding me. (Haven't tried anything because I can't think of how to do it).
Please note: I did see question Generating random, unique values C# but is does not address the specifics of my question relating to number of possible values at a given precision and subsequent runaway condition.
private Random RandomSeed = new Random();
public double GetRandomDouble(double lowerBounds, double upperBounds, int maxPrecision)
{
//Return a randomly-generated double between lowerBounds and upperBounds
//with maximum precision of maxPrecision
double x = (RandomSeed.NextDouble() * ((upperBounds - lowerBounds))) + lowerBounds;
return Math.Round(x, maxPrecision);
}
public double[] GetRandomDoublesUnique(double lowerBounds, double upperBounds, int maxPrecision, int quantity)
{
//This method returns an array of doubles containing randomly-generated numbers
//between user-entered lowerBounds and upperBounds with a maximum precision of
//maxPrecision. The array size is capped at user-entered quantity.
//Create Dictionary to store number values already generated so we can ensure
//we don't have duplicates
Dictionary<double, int> myDoubles = new Dictionary<double, int>();
double[] returnValues = new double[quantity];
double nextValue;
for (int i = 0; i < quantity; i++)
{
nextValue = GetRandomDouble(lowerBounds, upperBounds, maxPrecision);
if (!myDoubles.ContainsKey(nextValue))
{
myDoubles.Add(nextValue, i);
returnValues[i] = nextValue;
}
else
{
i -= 1;
}
}
return returnValues;
}
Number of items can be computed by just subtracting "position" of first from last (pseudo-code below, use Math.Pow to compute 10^x):
(int)(last * 10 ^ precision) - (int)(first * 10 ^ precision)
This may need to be adjusted depending on whether you want boundaries and whether you take decimal (precise) or float/double as input - some +/-1 and Math.Round may need to be sprinkled in to get desired results for all expected values.
After you get number of items there are essentially two cases
there are significantly more choices that desired results (i.e. 1 to 100, take 5 random numbers) - use code you have to filter out duplicates.
there the number of choices is close or less than desired number of results (i.e. 1 to 10, return 11 random numbers) - pre-generate the list of all value and shuffle.
Experiment with the boundary between "significantly more" and "close" - I'd use 25% as boundary ( i.e. 1 to 100, take 76 - use shuffling) to avoid excessive retires close to the end (which is exact reason of slowness/infinite retries of basic approach).
Correct implementation of shuffle is in Randomize a List<T> (check out similar posts like Generating random, unique values C# for more discussion).
The easiest way would probably be to convert the values to integers by multiplying them by 10 ^ precision and then subtract
int lowerInt = (int)(lower * (decimal)Math.Pow(10, precision));
int higherInt = (int)(higher * (decimal)Math.Pow(10, precision));
int possibleValues = higherInt - lowerInt + 1
I feel like it would defeat the purpose of you project to require the user to know how many possible values there are in advance, since it seems like thats what they are hitting this function for in the first place. I'm assuming that requirement was just to alleviate the technical issues you were having. You can just change your loop to this now
for (int i = 0; i < possibleValues; i++)
This is what worked based on Josh Williard's answer.
public double[] GetRandomDoublesUnique(double lowerBounds, double upperBounds, int maxPrecision, int quantity)
{
if (lowerBounds >= upperBounds)
{
throw new Exception("Error in GetRandomDoublesUnique is: LowerBounds is greater than UpperBounds!");
}
//These next few lines are for the purpose of determining the maximum possible number of return values
//possibleValues is populated to prevent a runaway condition that could occurs if the
//max possible values--at the given precision level--is less than the user-selected quantity.
//i.e. if user selects 1 to 1.01, precision of 3, and quantity of 50, there would be a problem
// if we didn't limit loop to the 11 possible values at precision of 3:
//1.000, 1.001, 1.002, 1.003, 1.004, 1.005, 1.006, 1.007, 1.008, 1.009, 1.010
int lowerInt = (int)(lowerBounds * (double)Math.Pow(10, maxPrecision));
int higherInt = (int)(upperBounds * (double)Math.Pow(10, maxPrecision));
int possibleValues = higherInt - lowerInt + 1;
//Create Dictionary to store number values already generated so we can ensure
//we don't have duplicates
Dictionary<double, int> myDoubles = new Dictionary<double, int>();
double[] returnValues = new double[(quantity>possibleValues?possibleValues:quantity)];
double NextValue;
//Iterate through and generate values--limiting to both the user-selected quantity and # of possible values
for (int i = 0; (i < quantity)&&(i<possibleValues); i++)
{
NextValue = GetRandomDouble(lowerBounds, upperBounds, maxPrecision);
if (!myDoubles.ContainsKey(NextValue))
{
myDoubles.Add(NextValue, i);
returnValues[i] = NextValue;
}
else
{
i -= 1;
}
}
return returnValues;
}
I want to get the random number between 1 and 0. However, I'm getting 0 every single time. Can someone explain me the reason why I and getting 0 all the time?
This is the code I have tried.
Random random = new Random();
int test = random.Next(0, 1);
Console.WriteLine(test);
Console.ReadKey();
According to the documentation, Next returns an integer random number between the (inclusive) minimum and the (exclusive) maximum:
Return Value
A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.
The only integer number which fulfills
0 <= x < 1
is 0, hence you always get the value 0. In other words, 0 is the only integer that is within the half-closed interval [0, 1).
So, if you are actually interested in the integer values 0 or 1, then use 2 as upper bound:
var n = random.Next(0, 2);
If instead you want to get a decimal between 0 and 1, try:
var n = random.NextDouble();
You could, but you should do it this way:
double test = random.NextDouble();
If you wanted to get random integer ( 0 or 1), you should set upper bound to 2, because it is exclusive
int test = random.Next(0, 2);
Every single answer on this page regarding doubles is wrong, which is sort of hilarious because everyone is quoting the documentation. If you generate a double using NextDouble(), you will not get a number between 0 and 1 inclusive of 1, you will get a number from 0 to 1 exclusive of 1.
To get a double, you would have to do some trickery like this:
public double NextRandomRange(double minimum, double maximum)
{
Random rand = new Random();
return rand.NextDouble() * (maximum - minimum) + minimum;
}
and then call
NextRandomRange(0,1 + Double.Epsilon);
Seems like that would work, doesn't it? 1 + Double.Epsilon should be the next biggest number after 1 when working with doubles, right? This is how you would solve the problem with ints.
Wellllllllllllllll.........
I suspect that this will not work correctly, since the underlying code will be generating a few bytes of randomness, and then doing some math tricks to fit it in the expected range. The short answer is that Logic that applies to ints doesn't quite work the same when working with floats.
Lets look, shall we? (https://referencesource.microsoft.com/#mscorlib/system/random.cs,e137873446fcef75)
/*=====================================Next=====================================
**Returns: A double [0..1)
**Arguments: None
**Exceptions: None
==============================================================================*/
public virtual double NextDouble() {
return Sample();
}
What the hell is Sample()?
/*====================================Sample====================================
**Action: Return a new random number [0..1) and reSeed the Seed array.
**Returns: A double [0..1)
**Arguments: None
**Exceptions: None
==============================================================================*/
protected virtual double Sample() {
//Including this division at the end gives us significantly improved
//random number distribution.
return (InternalSample()*(1.0/MBIG));
}
Ok, starting to get somewhere. MBIG btw, is Int32.MaxValue(2147483647 or 2^31-1), making the division work out to:
InternalSample()*0.0000000004656612873077392578125;
Ok, what the hell is InternalSample()?
private int InternalSample() {
int retVal;
int locINext = inext;
int locINextp = inextp;
if (++locINext >=56) locINext=1;
if (++locINextp>= 56) locINextp = 1;
retVal = SeedArray[locINext]-SeedArray[locINextp];
if (retVal == MBIG) retVal--;
if (retVal<0) retVal+=MBIG;
SeedArray[locINext]=retVal;
inext = locINext;
inextp = locINextp;
return retVal;
}
Well...that is something. But what is this SeedArray and inext crap all about?
private int inext;
private int inextp;
private int[] SeedArray = new int[56];
So things start to fall together. Seed array is an array of ints that is used for generating values from. If you look at the init function def, you see that there is a whole lot of bit addition and trickery being done to randomize an array of 55 values with initial quasi-random values.
public Random(int Seed) {
int ii;
int mj, mk;
//Initialize our Seed array.
//This algorithm comes from Numerical Recipes in C (2nd Ed.)
int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
mj = MSEED - subtraction;
SeedArray[55]=mj;
mk=1;
for (int i=1; i<55; i++) { //Apparently the range [1..55] is special (All hail Knuth!) and so we're skipping over the 0th position.
ii = (21*i)%55;
SeedArray[ii]=mk;
mk = mj - mk;
if (mk<0) mk+=MBIG;
mj=SeedArray[ii];
}
for (int k=1; k<5; k++) {
for (int i=1; i<56; i++) {
SeedArray[i] -= SeedArray[1+(i+30)%55];
if (SeedArray[i]<0) SeedArray[i]+=MBIG;
}
}
inext=0;
inextp = 21;
Seed = 1;
}
Ok, going back to InternalSample(), we can now see that random doubles are generated by taking the difference of two scrambled up 32 bit ints, clamping the result into the range of 0 to 2147483647 - 1 and then multiplying the result by 1/2147483647. More trickery is done to scramble up the list of seed values as it uses values, but that is essentially it.
(It is interesting to note that the chance of getting any number in the range is roughly 1/r EXCEPT for 2^31-2, which is 2 * (1/r)! So if you think some dumbass coder is using RandNext() to generate numbers on a video poker machine, you should always bet on 2^32-2! This is one reason why we don't use Random for anything important...)
so, if the output of InternalSample() is 0 we multiply that by 0.0000000004656612873077392578125 and get 0, the bottom end of our range. if we get 2147483646, we end up with 0.9999999995343387126922607421875, so the claim that NextDouble produces a result of [0,1) is...sort of right? It would be more accurate to say it is int he range of [0,0.9999999995343387126922607421875].
My suggested above solution would fall on its face, since double.Epsilon = 4.94065645841247E-324, which is WAY smaller than 0.0000000004656612873077392578125 (the amount you would add to our above result to get 1).
Ironically, if it were not for the subtraction of one in the InternalSample() method:
if (retVal == MBIG) retVal--;
we could get to 1 in the return values that come back. So either you copy all the code in the Random class and omit the retVal-- line, or multiply the NextDouble() output by something like 1.0000000004656612875245796924106 to slightly stretch the output to include 1 in the range. Actually testing that value gets us really close, but I don't know if the few hundred million tests I ran just didn't produce 2147483646 (quite likely) or there is a floating point error creeping into the equation. I suspect the former. Millions of test are unlikely to yield a result that has 1 in 2 billion odds.
NextRandomRange(0,1.0000000004656612875245796924106); // try to explain where you got that number during the code review...
TLDR? Inclusive ranges with random doubles is tricky...
You are getting zero because Random.Next(a,b) returns number in range [a, b), which is greater than or equal to a, and less than b.
If you want to get one of the {0, 1}, you should use:
var random = new Random();
var test = random.Next(0, 2);
Because you asked for a number less than 1.
The documentation says:
Return Value
A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values
includes minValue but not maxValue. If minValue equals maxValue,
minValue is returned.
Rewrite the code like this if you are targeting 0.0 to 1.0
Random random = new Random();
double test = random.NextDouble();
Console.WriteLine(test);
Console.ReadKey();
I need your help.
i would like to display a random number between two different value of an url parameters.
This is the code. It doesn't work
public class RandomIntController : ApiController
{
public double Get()
{
double min = Request.QueryString("min");
double max = Request.QueryString("max");
min = 1;
max = 100;
Random number = new Random();
return number.NextDouble();
}
}
So for exemple the guest insert this url :
• http://localhost:51832/api/randomint?min=12&max=23
My web api must display a random number between the max value(23) and the min value(12).
I have also several error with the attribut 'resquest.queryString()' :/
I don't found my error so please help me
Thank's a lot
int min = (int)Request.QueryString["min"];
int max = (int)Request.QueryString["max"];
Random number = new Random();
return number.Next(min, max);
QueryString is a key value pair, not method. use [] instead of (). Also pass min and max as a parameter to Next method as in code.
Not sure if you really need a "double" which includes fractional parts (e.g. 12.443). If needed, it can be done. But consider this for integers:
public int Get()
{
string minStr = Request.QueryString.Get("min");
string maxStr = Request.QueryString.Get("max");
int min = int.Parse(minStr);
int max = int.Parse(maxStr);
Random number = new Random();
int value = number.Next(min, max);
return value;
}
If you have a double max and a double min and you want to generate a random number in the range [min, max):
Random rng = new Random(); // Declare this once somewhere - don't keep redeclaring it!
double value = min + rng.NextDouble()*(max - min);
Note that the random number generated by this will NOT include max - [max, min) defines a half-open interval.
If you don't want doubles, then see the answer provided by Imad.
So there is several ways of creating a random bool in C#:
Using Random.Next(): rand.Next(2) == 0
Using Random.NextDouble(): rand.NextDouble() > 0.5
Is there really a difference? If so, which one actually has the better performance? Or is there another way I did not see, that might be even faster?
The first option - rand.Next(2) executes behind the scenes the following code:
if (maxValue < 0)
{
throw new ArgumentOutOfRangeException("maxValue",
Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", new object[] { "maxValue" }));
}
return (int) (this.Sample() * maxValue);
and for the second option - rand.NextDouble():
return this.Sample();
Since the first option contains maxValue validation, multiplication and casting, the second option is probably faster.
Small enhancement for the second option:
According to MSDN
public virtual double NextDouble()
returns
A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
So if you want an evenly spread random bool you should use >= 0.5
rand.NextDouble() >= 0.5
Range 1: [0.0 ... 0.5[
Range 2: [0.5 ... 1.0[
|Range 1| = |Range 2|
The fastest. Calling the method Random.Next has the less overhead. The extension method below runs 20% faster than Random.NextDouble() > 0.5, and 35% faster than Random.Next(2) == 0.
public static bool NextBoolean(this Random random)
{
return random.Next() > (Int32.MaxValue / 2);
// Next() returns an int in the range [0..Int32.MaxValue]
}
Faster than the fastest. It is possible to generate random booleans with the Random class even faster, by using tricks. The 31 significant bits of a generated int can be used for 31 subsequent boolean productions. The implementation below is 40% faster than the previously declared as the fastest.
public class RandomEx : Random
{
private uint _boolBits;
public RandomEx() : base() { }
public RandomEx(int seed) : base(seed) { }
public bool NextBoolean()
{
_boolBits >>= 1;
if (_boolBits <= 1) _boolBits = (uint)~this.Next();
return (_boolBits & 1) == 0;
}
}
I ran tests with stopwatch. 100,000 iterations:
System.Random rnd = new System.Random();
if (rnd.Next(2) == 0)
trues++;
CPUs like integers, so the Next(2) method was faster. 3,700 versus 7,500ms, which is quite substantial.
Also: I think random numbers can be a bottleneck, I created around 50 every frame in Unity, even with a tiny scene that noticeably slowed down my system, so I also was hoping to find a method to create a random bool.
So I also tried
if (System.DateTime.Now.Millisecond % 2 == 0)
trues++;
but calling a static function was even slower with 9,600ms. Worth a shot.
Finally I skipped the comparison and only created 100,000 random values, to make sure the int vs. double comparison did not influence the elapsed time, but the result was pretty much the same.
Based on answers here, Below is the code I used to generate random bool value.
Random rand = new Random();
bool randomBool = rand.NextDouble() >= 0.5;
References:
Generate a random boolean
https://stackoverflow.com/a/28763727/2218697
I'm using the RNG crypto provider to generate numbers in a range the truly naive way:
byte[] bytes = new byte[4];
int result = 0;
while(result < min || result > max)
{
RNG.GetBytes(bytes);
result = BitConverter.ToInt32(bytes);
}
This is great when the range is wide enough such that there is a decent chance of getting a result, but earlier today I hit a scenario where the range is sufficiently small (within 10,000 numbers) that it can take an age.
So I've been trying to think of a better way that will achieve a decent distribution but will be faster. But now I'm getting into deeper maths and statistics that I simply didn't do at school, or at least if I did I have forgotten it all!
My idea is:
get the highest set bit positions of the min and max, e.g. for 4 it would be 3 and for 17 it would be 5
select a number of bytes from the prng that could contain at least the high bits, e.g.1 in this case for 8 bits
see if any of the upper bits in the allowed range (3-5) are set
if yes, turn that into a number up to and including the high bit
if that number is between min and max, return.
if any of the previous tests fail, start again.
Like I say, this could be exceedingly naive, but I am sure it will return a match in a narrow range faster than the current implementation. I'm not in front of a computer at the moment so can't test, will be doing that tomorrow morning UK time.
But of course speed isn't my only concern, otherwise I would just use Random (needs a couple of tick marks there to format correctly if someone would be kind enough - they're not on the Android keyboard!).
The biggest concern I have with the above approach is that I am always throwing away up to 7 bits that were generated by the prng, which seems bad. I thought of ways to factor them in (e.g. a simple addition) but they seem terribly unscientific hacks!
I know about the mod trick, where you only have to generate one sequence, but I also know about its weaknesses.
Is this a dead end? Ultimately if the best solution is going to be to stick with the current implementation I will, I just feel that there must be a better way!
Stephen Toub and Shawn Farkas has co-written an excellent article on MSDN called Tales From The CryptoRandom that you should definitely read if you're experimenting with RNGCryptoServiceProviders
In it they provide an implementation that inherits from System.Random (which contains the nice range-random method that you're looking for) but instead of using pseudo random numbers their implementation uses the RNGCryptoServiceProvider.
The way he has implemented the Next(min, max) method is as follows:
public override Int32 Next(Int32 minValue, Int32 maxValue)
{
if (minValue > maxValue)
throw new ArgumentOutOfRangeException("minValue");
if (minValue == maxValue) return minValue;
Int64 diff = maxValue - minValue;
while (true)
{
_rng.GetBytes(_uint32Buffer);
UInt32 rand = BitConverter.ToUInt32(_uint32Buffer, 0);
Int64 max = (1 + (Int64)UInt32.MaxValue);
Int64 remainder = max % diff;
if (rand < max - remainder)
{
return (Int32)(minValue + (rand % diff));
}
}
}
The reasoning for the choice of implementation as well as a detailed analysis about loss of randomness and what steps they are taking to produce high-quality random numbers is in their article.
Thread safe bufferred CryptoRandom
I've written an extended implementation of Stephen's class which utilized a random buffer in order to minimize any overhead of calling out to GetBytes(). My implementation also uses synchronization to provide thread safety, making it possible to share the instance between all your threads to make full use of the buffer.
I wrote this for a very specific scenario so you should of course profile whether or not is makes sense for you given the specific contention and concurrency attributes of your application. I threw the code up on github if you wan't to check it out.
Threadsafe buffered CryptoRandom based on Stephen Toub and Shawn Farkas' implementation
When I wrote it (a couple of years back) I seem to have done some profiling as well
Results produced by calling Next() 1 000 000 times on my machine (dual core 3Ghz)
System.Random completed in 20.4993 ms (avg 0 ms) (first: 0.3454 ms)
CryptoRandom with pool completed in 132.2408 ms (avg 0.0001 ms) (first: 0.025 ms)
CryptoRandom without pool completed in 2 sec 587.708 ms (avg 0.0025 ms) (first: 1.4142 ms)
|---------------------|------------------------------------|
| Implementation | Slowdown compared to System.Random |
|---------------------|------------------------------------|
| System.Random | 0 |
| CryptoRand w pool | 6,6x |
| CryptoRand w/o pool | 19,5x |
|---------------------|------------------------------------|
Please note that theese measurements only profile a very specific non-real-world scenario and should only be used for guidance, measure your scenario for proper results.
If you use a while loop, this is going to be slow and is based on an unknown number of iterations.
You could compute it on the first try using the modulo operator (%).
But, if we squeeze results with modulo, we immediately create an imbalance in the probability distributions.
This means that this approach could be applied if we care only about the speed, not probabilistic randomness of the generated number.
Here is a RNG utility that could suit your needs:
using System;
using System.Security.Cryptography;
static class RNGUtil
{
/// <exception cref="ArgumentOutOfRangeException"><paramref name="min" /> is greater than <paramref name="max" />.</exception>
public static int Next(int min, int max)
{
if (min > max) throw new ArgumentOutOfRangeException(nameof(min));
if (min == max) return min;
using (var rng = new RNGCryptoServiceProvider())
{
var data = new byte[4];
rng.GetBytes(data);
int generatedValue = Math.Abs(BitConverter.ToInt32(data, startIndex: 0));
int diff = max - min;
int mod = generatedValue % diff;
int normalizedNumber = min + mod;
return normalizedNumber;
}
}
}
In this case RNGUtil.Next(-5, 20) would fetch an arbitrary number within range -5..19
A small test:
var list = new LinkedList<int>();
for (int i = 0; i < 10000; i++)
{
int next = RNGUtil.Next(-5, 20);
list.AddLast(next);
}
bool firstNumber = true;
foreach (int x in list.Distinct().OrderBy(x => x))
{
if (!firstNumber) Console.Out.Write(", ");
Console.Out.Write(x);
firstNumber = false;
}
Output: -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
You can generate many more bytes at once for a very small overhead. The main overhead with the RNGCrptoService is the call itself to fill in the bytes.
While you might throw away unused bytes, I'd give it a shot as I've gotten very good speeds from this and the modulo method (that you aren't using).
int vSize = 20*4;
byte[] vBytes = new byte[vSize];
RNG.GetBytes(vBytes);
int vResult = 0;
int vLocation = 0;
while(vResult < min || vResult > max)
{
vLocation += 4;
vLocation = vLocation % vSize;
if(vLocation == 0)
RNG.GetBytes(vBytes);
vResult = BitConverter.ToInt32(vBytes, vLocation);
}
Another thing you can do is the comparison you where thinking of bitwise. However, I'd focus on if the range fits in a byte, a short, an int, or a long. Then you can modulo the int result by the max of that type (giving you the lower order bits).
//We want a short, so we change the location increment and we modulo the result.
int vSize = 20*4;
byte[] vBytes = new byte[vSize];
RNG.GetBytes(vBytes);
int vResult = 0;
int vLocation = 0;
while(vResult < min || vResult > max)
{
vLocation += 2;
vLocation = vLocation % vSize;
if(vLocation == 0)
RNG.GetBytes(vBytes);
vResult = BitConverter.ToInt32(vBytes, vLocation) % 32768;
}
The following is an adaptation of #Andrey-WD's answer above, but with the difference that you simply send in a random number you already have generated (a ulong in this case, could be changed to uint). Where this is very efficient is when you need multiple random numbers in a range, you can simply generate an array of such numbers via RNGCryptoServiceProvider (or whatever, even with Random if that fit your needs). This I'm sure will be far more performant when needing to generate multiple random numbers within a range. All you need is the stash of random numbs to feed the function. See my note above on #Andrey-WD's answer, I'm curious why others aren't doing this simpler modulus route which doesn't require multiple iterations. If there really is a required reason for the multiple iterations route, I would be glad to hear it.
public static int GetRandomNumber(int min, int max, ulong randomNum)
{
if (min > max) throw new ArgumentOutOfRangeException(nameof(min));
if (min == max) return min;
//var rng = new RNGCryptoServiceProvider();
//byte[] data = new byte[4];
//rng.GetBytes(data);
//int generatedValue = Math.Abs(BitConverter.ToInt32(data, startIndex: 0));
int diff = max - min;
int mod = (int)(randomNum % (ulong)diff); // generatedValue % diff;
int normalizedNumber = min + mod;
return normalizedNumber;
}
Here's how you can effectively get a clean array of random numbers. I like how this cleanly encapsulates getting the random numbers, the code that uses this then doesn't have to be cluttered with byte conversion on every iteration to get the int or long with BitConverter. I also assume this gains performance by a singular conversion of bytes to the array type.
public static ulong[] GetRandomLongArray(int length)
{
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
ulong[] arr = new ulong[length];
if (length > 0) { // if they want 0, why 'throw' a fit, just give it to them ;)
byte[] rndByteArr = new byte[length * sizeof(ulong)];
var rnd = new RNGCryptoServiceProvider();
rnd.GetBytes(rndByteArr);
Buffer.BlockCopy(rndByteArr, 0, arr, 0, rndByteArr.Length);
}
return arr;
}
Usage:
ulong[] randomNums = GetRandomLongArray(100);
for (int i = 0; i < 20; i++) {
ulong randNum = randomNums[i];
int val = GetRandomNumber(10, 30, randNum); // get a rand num between 10 - 30
WriteLine(val);
}
public int RandomNumber(int min = 1, int max = int.MaxValue)
{
using (var rng = new RNGCryptoServiceProvider())
{
byte[] buffer = new byte[4];
rng.GetBytes(buffer);
return (int)(BitConverter.ToUInt32(buffer, 0) >> 1) % ((max - min) + 1);
}
}